[fusion-commits] r1830 - in branches/fusion-2.0: . common/php layers/MapGuide layers/MapGuide/php layers/MapServer layers/MapServer/php lib lib/OpenLayers lib/OpenLayers/Lang lib/OpenLayers/theme/default lib/OpenLayers/theme/default/img templates/mapserver/standard widgets widgets/SelectWithin

svn_fusion at osgeo.org svn_fusion at osgeo.org
Fri Apr 3 15:40:17 EDT 2009


Author: madair
Date: 2009-04-03 15:40:15 -0400 (Fri, 03 Apr 2009)
New Revision: 1830

Added:
   branches/fusion-2.0/layers/MapServer/php/Maptip.php
   branches/fusion-2.0/lib/OpenLayers/Lang/es.js
   branches/fusion-2.0/lib/OpenLayers/Lang/hu.js
   branches/fusion-2.0/lib/OpenLayers/Lang/ja.js
   branches/fusion-2.0/lib/OpenLayers/theme/default/ie6-style.css
   branches/fusion-2.0/lib/OpenLayers/theme/default/img/pan-panel-NOALPHA.png
   branches/fusion-2.0/lib/OpenLayers/theme/default/img/pan-panel.png
   branches/fusion-2.0/lib/OpenLayers/theme/default/img/zoom-panel-NOALPHA.png
   branches/fusion-2.0/lib/OpenLayers/theme/default/img/zoom-panel.png
Removed:
   branches/fusion-2.0/lib/CanvasTool.js
Modified:
   branches/fusion-2.0/common/php/Xml2JSON.php
   branches/fusion-2.0/config_dist.json
   branches/fusion-2.0/layers/MapGuide/MapGuide.js
   branches/fusion-2.0/layers/MapGuide/php/Common.php
   branches/fusion-2.0/layers/MapGuide/php/CreateSession.php
   branches/fusion-2.0/layers/MapGuide/php/GetSelectionProperties.php
   branches/fusion-2.0/layers/MapGuide/php/LoadMap.php
   branches/fusion-2.0/layers/MapGuide/php/Measure.php
   branches/fusion-2.0/layers/MapGuide/php/Query.php
   branches/fusion-2.0/layers/MapGuide/php/SaveSelection.php
   branches/fusion-2.0/layers/MapGuide/php/Selection.php
   branches/fusion-2.0/layers/MapGuide/php/SetLayers.php
   branches/fusion-2.0/layers/MapGuide/php/Utilities.php
   branches/fusion-2.0/layers/MapServer/MapServer.js
   branches/fusion-2.0/layers/MapServer/php/CreateSession.php
   branches/fusion-2.0/layers/MapServer/php/LoadMap.php
   branches/fusion-2.0/layers/MapServer/php/LoadScaleRanges.php
   branches/fusion-2.0/layers/MapServer/php/Measure.php
   branches/fusion-2.0/layers/MapServer/php/Query.php
   branches/fusion-2.0/layers/MapServer/php/Selection.php
   branches/fusion-2.0/layers/MapServer/php/SetLayers.php
   branches/fusion-2.0/lib/ApplicationDefinition.js
   branches/fusion-2.0/lib/OpenLayers/OpenLayers.js
   branches/fusion-2.0/lib/OpenLayers/theme/default/framedCloud.css
   branches/fusion-2.0/lib/OpenLayers/theme/default/style.css
   branches/fusion-2.0/lib/Widget.js
   branches/fusion-2.0/lib/fusion.js
   branches/fusion-2.0/lib/jxlib.uncompressed.js
   branches/fusion-2.0/templates/mapserver/standard/ApplicationDefinition.xml
   branches/fusion-2.0/templates/mapserver/standard/index.html
   branches/fusion-2.0/widgets/ColorPicker.js
   branches/fusion-2.0/widgets/LayerManager.js
   branches/fusion-2.0/widgets/Legend.js
   branches/fusion-2.0/widgets/Maptip.js
   branches/fusion-2.0/widgets/PanQuery.js
   branches/fusion-2.0/widgets/Select.js
   branches/fusion-2.0/widgets/SelectRadius.js
   branches/fusion-2.0/widgets/SelectWithin/SelectWithin.php
   branches/fusion-2.0/widgets/Zoom.js
Log:
re #243: merge revs 1781-1828

Modified: branches/fusion-2.0/common/php/Xml2JSON.php
===================================================================
--- branches/fusion-2.0/common/php/Xml2JSON.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/common/php/Xml2JSON.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -35,7 +35,7 @@
     if (!isset($REQUEST_VARS['xml'])) {
         die('xml not set');
     }
-    header('Content-type: text/plain');
+    header('Content-type: application/json');
     header('X-JSON: true');
     $xml = rawurldecode ($REQUEST_VARS['xml']);
     $xml = trim($xml);

Modified: branches/fusion-2.0/config_dist.json
===================================================================
--- branches/fusion-2.0/config_dist.json	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/config_dist.json	2009-04-03 19:40:15 UTC (rev 1830)
@@ -18,7 +18,14 @@
 	       with http and end with /mapguide.  If you have installed
 	       fusion inside the www directory of MapGuide, then you can
 	       leave this empty as it will be automatically calculated. */
-           "webTierUrl": ""
+           "webTierUrl": "",
+      /* for MapGuide OS version > 2.1 and MGE2010 and higher set this to true*/
+          "useAsyncOverlay": false,
+	    /* The tileCacheUrl is the root url to a static mapguide tile cache.
+	       A relative URL will be relative to your application.
+	       The root url will be appended with the 'Sn'
+         directories for each zoom level. */
+           "tileCacheUrl": "http://localhost:8008/sheboygan"
     },
     /* The MapServer section is required if you are installing fusion
        for MapServer. */

Modified: branches/fusion-2.0/layers/MapGuide/MapGuide.js
===================================================================
--- branches/fusion-2.0/layers/MapGuide/MapGuide.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/MapGuide.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -42,6 +42,7 @@
     bSelectionOn: false,
     oSelection: null,
     selectionAsOverlay: true,
+    useAsyncOverlay: false,
     defaultFormat: 'PNG',
 
     initialize: function(map, mapTag, isMapWidgetLayer) {
@@ -311,7 +312,7 @@
                 this.oLayerOL.destroy();
             }
 
-            this.oLayerOL = this.createOLLayer(this._sMapname, this.bIsBaseLayer, this.bSingleTile,2);
+            this.oLayerOL = this.createOLLayer(this._sMapname, this.bSingleTile,2);
             this.oLayerOL.events.register("loadstart", this, this.loadStart);
             this.oLayerOL.events.register("loadend", this, this.loadEnd);
             this.oLayerOL.events.register("loadcancel", this, this.loadEnd);
@@ -530,16 +531,18 @@
      *
      * Returns an OpenLayers MapGuide layer object
      */
-    createOLLayer: function(layerName, bIsBaseLayer, bSingleTile, behavior) {
-      /* prevent the useOverlay flag based on site version       */
-      if (( this.siteVersion[0] == 2 && this.siteVersion[1] < 1 ) || this.siteVersion[0] < 2) { //v2.0.0 or previous
+    createOLLayer: function(layerName, bSingleTile, behavior) {
+      /* prevent the useOverlay flag based on MapGuide config element */
+      this.useAsyncOverlay = Fusion.getConfigurationItem('mapguide', 'useAsyncOverlay');
+      if (!this.useAsyncOverlay) {          //v2.0.1 or earlier
         this.selectionAsOverlay = false;
       }
+      
       var layerOptions = {
         units: this.units,
-        isBaseLayer: bIsBaseLayer,
         maxResolution: 'auto',
         useOverlay: this.selectionAsOverlay,
+        useAsyncOverlay: this.useAsyncOverlay,
         ratio: this.ratio
       };
       if ((behavior & 1) == 0 && !/WebKit/.test(navigator.userAgent)) {
@@ -572,7 +575,6 @@
           mapname: this._sMapname,
           format: this.imageFormat,
           behavior: behavior,
-          version: "2.0.0",
           clientagent: this.clientAgent
         };
         params.showLayers = this.aShowLayers.length > 0 ? this.aShowLayers.toString() : null;
@@ -594,8 +596,18 @@
           clientagent: this.clientAgent
         };
       }
+      
+      //Fix for IE6 PNG transparency
+      if (params.format && params.format.toLowerCase().indexOf('png') >= 0) {
+        layerOptions.alpha = true;
+      }
 
-      var url = Fusion.getConfigurationItem('mapguide', 'mapAgentUrl');
+      var url;
+      if ( !bSingleTile && layerOptions.useHttpTile) {
+        url = Fusion.getConfigurationItem('mapguide', 'tileCacheUrl');
+      } else {
+        url = Fusion.getConfigurationItem('mapguide', 'mapAgentUrl');
+      }
       var oLayerOL = new OpenLayers.Layer.MapGuide( layerName, url, params, layerOptions );
       return oLayerOL;
     },
@@ -1180,6 +1192,7 @@
         this.setSelection(selText, false);
         this.mapWidget._removeWorker();
     }
+
 });
 
 Fusion.SimpleSelectionObject = OpenLayers.Class({

Modified: branches/fusion-2.0/layers/MapGuide/php/Common.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/Common.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/Common.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -36,8 +36,8 @@
  * this file to configure Fusion.
  */
 
-$defaultInstallDir = dirname(__FILE__)."/../../../../../../";
-$defaultExtensionDir = realpath($defaultInstallDir . "webserverextensions/www/");
+$defaultExtensionDir = dirname(__FILE__)."/../../../../";
+$defaultInstallDir = realpath($defaultExtensionDir)."/../../";
 
 /**
  * Developer's notes:

Modified: branches/fusion-2.0/layers/MapGuide/php/CreateSession.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/CreateSession.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/CreateSession.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -37,7 +37,7 @@
     $sessionId =  $site->CreateSession();
     $user->SetMgSessionId($sessionId);
 
-    header('Content-type: text/x-json');
+    header('Content-type: application/json');
     header('X-JSON: true');
     $result = null;
     $result->sessionId = $sessionId;

Modified: branches/fusion-2.0/layers/MapGuide/php/GetSelectionProperties.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/GetSelectionProperties.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/GetSelectionProperties.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -57,7 +57,7 @@
         $properties->layers = array();
 
         //process
-        header('Content-type: text/x-json');
+        header('Content-type: application/json');
         header('X-JSON: true');
         $layers = $selection->GetLayers();
         $nLayers = $layers->GetCount();

Modified: branches/fusion-2.0/layers/MapGuide/php/LoadMap.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/LoadMap.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/LoadMap.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -85,8 +85,8 @@
     }   
 
 
-    //header('Content-type: text/x-json');
-    //header('X-JSON: true');
+    header('Content-type: application/json');
+    header('X-JSON: true');
 
     $mapObj->sessionId = $sessionID;
     $mapObj->mapId = $mapid;

Modified: branches/fusion-2.0/layers/MapGuide/php/Measure.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/Measure.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/Measure.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -57,7 +57,7 @@
         $distance = $srsMap->MeasureEuclideanDistance($x1, $y1, $x2, $y2);
     }
     $distance = $srsMap->ConvertCoordinateSystemUnitsToMeters($distance);   
-    header('Content-type: text/x-json');
+    header('Content-type: application/json');
     header('X-JSON: true');
     echo "{distance:$distance}";
     exit;

Modified: branches/fusion-2.0/layers/MapGuide/php/Query.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/Query.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/Query.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -350,7 +350,7 @@
     //print_r($properties);
     //echo "/* SelectionXML:".$selection->ToXML()."*/";
 
-    header('Content-type: text/x-json');
+    header('Content-type: application/json');
     header('X-JSON: true');
     $layers = $selection->GetLayers();
     $result = NULL;

Modified: branches/fusion-2.0/layers/MapGuide/php/SaveSelection.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/SaveSelection.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/SaveSelection.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -49,7 +49,7 @@
         //now return a data struture which is the same as Query.php
 
         //process
-        header('Content-type: text/x-json');
+        header('Content-type: application/json');
         header('X-JSON: true');
         $layers = $selection->GetLayers();
 

Modified: branches/fusion-2.0/layers/MapGuide/php/Selection.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/Selection.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/Selection.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -139,7 +139,7 @@
 }
 
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 echo var2json($result);
 

Modified: branches/fusion-2.0/layers/MapGuide/php/SetLayers.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/SetLayers.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/SetLayers.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -30,7 +30,7 @@
  *****************************************************************************/
 
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 echo "{";
 

Modified: branches/fusion-2.0/layers/MapGuide/php/Utilities.php
===================================================================
--- branches/fusion-2.0/layers/MapGuide/php/Utilities.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapGuide/php/Utilities.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -321,8 +321,7 @@
 {
     try
     {
-        $cnt = $resourceSrvc->GetResourceContent($dataSourceId);
-        return true;
+        return $resourceSrvc->ResourceExists($dataSourceId);
     }
     catch(MgResourceNotFoundException $rnfe)
     {
@@ -650,18 +649,7 @@
 
 function ByteReaderToString($byteReader)
 {
-    $buffer = '';
-    do
-    {
-        $data = str_pad("\0", 50000, "\0");
-        $len = $byteReader->Read($data, 50000);
-        if ($len > 0)
-        {
-            $buffer = $buffer . substr($data, 0, $len);
-        }
-    } while ($len > 0);
-
-    return $buffer;
+    return $byteReader->ToString();
 }
 
 

Modified: branches/fusion-2.0/layers/MapServer/MapServer.js
===================================================================
--- branches/fusion-2.0/layers/MapServer/MapServer.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/MapServer.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -41,6 +41,7 @@
     mapMetadataKeys: null,
     layerMetadataKeys: null,
     oMaptip:null,
+    bMapTipFired: false,
 
     //the map file
     sMapFile: null,
@@ -53,6 +54,14 @@
 
         this.sMapFile = mapTag.extension.MapFile ? mapTag.extension.MapFile[0] : '';
 
+        // load mapfrom the querystring if "theme" is present.
+        var newTheme = Fusion.getQueryParam('theme');
+        if (newTheme != '') {
+        this.sMapFile = newTheme;
+          //clear the query param after it has been used once
+          Fusion.queryParams['theme'] = null;
+        }
+
         this.mapMetadataKeys = mapTag.extension.MapMetadata ? mapTag.extension.MapMetadata[0] : null;
         this.layerMetadataKeys = mapTag.extension.LayerMetadata ? mapTag.extension.LayerMetadata[0] : null;
         
@@ -783,41 +792,49 @@
     },
 
     getMapTip: function(oMapTips){
-        //console.log("MAPSERVER:getMapTip");
-        var pos = this.mapWidget.pixToGeo(oMapTips.oCurrentPosition.x, oMapTips.oCurrentPosition.y);
-        var dfGeoTolerance = this.mapWidget.pixToGeoMeasure(oMapTips.nTolerance);
-        var minx = pos.x-dfGeoTolerance;
-        var miny = pos.y-dfGeoTolerance;
-        var maxx = pos.x+dfGeoTolerance;
-        var maxy = pos.y+dfGeoTolerance;
-        var geometry = 'POLYGON(('+ minx + ' ' + miny + ', ' + minx + ' ' + maxy + ', ' + maxx + ' ' + maxy + ', ' + maxx + ' ' + miny + ', ' + minx + ' ' + miny + '))';
-        var selectionType = "INTERSECTS";
+        if(this.bMapTipFired == false){
+            //console.log("MAPSERVER:getMapTip");
+            var pos = this.mapWidget.pixToGeo(oMapTips.oCurrentPosition.x, oMapTips.oCurrentPosition.y);
+            var dfGeoTolerance = this.mapWidget.pixToGeoMeasure(oMapTips.nTolerance);
+            var minx = pos.x-dfGeoTolerance;
+            var miny = pos.y-dfGeoTolerance;
+            var maxx = pos.x+dfGeoTolerance;
+            var maxy = pos.y+dfGeoTolerance;
+            var geometry = 'POLYGON(('+ minx + ' ' + miny + ', ' + minx + ' ' + maxy + ', ' + maxx + ' ' + maxy + ', ' + maxx + ' ' + miny + ', ' + minx + ' ' + miny + '))';
+            var selectionType = "INTERSECTS";
 
-        var loadmapScript = '/layers/'+ this.arch + '/php/Maptip.php';
-        var params = {
-            'mapname': this._sMapname,
-            'session': this.getSessionID(),
-            'spatialfilter': geometry,
-            'maxfeatures': 0, //zero means select all features
-            'variant': selectionType,
-            'layer': oMapTips.aLayers[0] || '',
-            'textfield': oMapTips.textField || '',
-            'customURL': oMapTips.customURL || ''
+            var loadmapScript = '/layers/'+ this.arch + '/php/Maptip.php';
+            var params = {
+                'mapname': this._sMapname,
+                'session': this.getSessionID(),
+                'spatialfilter': geometry,
+                'maxfeatures': 0, //zero means select all features
+                'variant': selectionType,
+                'layer': oMapTips.aLayers || '',
+                'textfield': oMapTips.aTextFields || '',
+                'label': oMapTips.aLabels || '',
+                'customURL': oMapTips.aCustomURL || ''
+            }
+            var parseMapTip = this.parseMapTip.bind(this);
+            this.bMapTipFired = true;
+            var ajaxOptions = {
+                onSuccess: function(response){
+                        eval("rjson=" + response.responseText);
+                        parseMapTip(rjson);
+                        },
+                        parameters: params};
+            Fusion.ajaxRequest(loadmapScript, ajaxOptions);
         }
-        var parseMapTip = this.parseMapTip.bind(this);
-        var ajaxOptions = {
-            onSuccess: function(response){
-                    eval("rjson=" + response.responseText);
-                    parseMapTip(rjson);
-                    },
-                    parameters: params};
-        Fusion.ajaxRequest(loadmapScript, ajaxOptions);
     },
     
     parseMapTip: function(json){
-        this.oMaptip = {};
-        this.oMaptip.t = json.mapTipText;
-        this.oMaptip.h = json.mapTipLink;
+        this.bMapTipFired = false;
+        this.oMaptip = {};       
+        this.oMaptip.t = json.maptips;
+        this.oMaptip.h = json.url;
+        // mapserver only
+        this.oMaptip.l= json.label;
+        
         this.mapWidget.triggerEvent(Fusion.Event.MAP_MAPTIP_REQ_FINISHED, this.oMaptip);
     }
 });

Modified: branches/fusion-2.0/layers/MapServer/php/CreateSession.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/CreateSession.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/CreateSession.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -36,7 +36,7 @@
 $sessionId = session_id();
 loadFusionConfig();
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 $result = null;
 $result->sessionId = $sessionId;

Modified: branches/fusion-2.0/layers/MapServer/php/LoadMap.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/LoadMap.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/LoadMap.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -139,8 +139,8 @@
 
 $mapObj = NULL;
 if ($oMap) {
-  //header('Content-type: text/x-json');
-  //header('X-JSON: true');
+    header('Content-type: application/json');
+    header('X-JSON: true');
     $mapObj->sessionId = $sessionID;
     $mapObj->mapId = $mapId;
 

Modified: branches/fusion-2.0/layers/MapServer/php/LoadScaleRanges.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/LoadScaleRanges.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/LoadScaleRanges.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -140,7 +140,7 @@
       
  }
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 
 echo var2json($scaleObj);

Copied: branches/fusion-2.0/layers/MapServer/php/Maptip.php (from rev 1828, trunk/layers/MapServer/php/Maptip.php)
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/Maptip.php	                        (rev 0)
+++ branches/fusion-2.0/layers/MapServer/php/Maptip.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Maptip
+ *
+
+ * Portions copyright (c) 2006, DM Solutions Group Inc.
+ * Portions copyright (c) 2008, ENPLAN
+ * 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.
+ */
+
+/*****************************************************************************
+ * Purpose: duplicate maptip functionality on mapserver
+ *****************************************************************************/
+
+/* set up the session */
+include(dirname(__FILE__).'/../../../common/php/Utilities.php');
+include(dirname(__FILE__).'/Common.php');
+include(dirname(__FILE__).'/Utilities.php');
+
+
+
+/* The Layer Name*/
+if ($_REQUEST['layer'] != '') {
+    $aLayer = split(",",$_REQUEST['layer']);
+}
+
+/* The Label*/
+if ($_REQUEST['layer'] != '') {
+    $aLabel = split(",",$_REQUEST['label']);
+}
+
+/* The Feature Attribute Name used for the text of the map tip */
+if ($_REQUEST['textfield'] != '') {
+    $aMapTipTextField = split(",",$_REQUEST['textfield']);
+}
+/* The Feature Attribute Name used for the URL of the map tip */
+if ($_REQUEST['customURL'] != '') {
+    $aMapTipURL = split(",",$_REQUEST['customURL']);
+}
+/* The Map Name */
+if ($_REQUEST['mapname'] != '') {
+    $mapname = $_REQUEST['mapname'];
+}
+/* selection is intersects only */
+$variant = 'intersects';
+
+/* a spatial filter in the form on a WKT geometry */
+$spatialFilter = (isset($_REQUEST['spatialfilter']) && $_REQUEST['spatialfilter'] != '') ? urldecode($_REQUEST['spatialfilter']) : false;
+
+header('Content-type: application/json');
+header('X-JSON: true');
+
+if (!isset($mapName)) {
+    die("{'error':'mapname not set'}");
+}
+if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
+    $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+
+    /* add the spatial filter if provided.  It is expected to come as a
+    WKT string, so we need to convert it to a shape */
+    if ($spatialFilter !== false ) {
+        $oSpatialFilter = ms_shapeObjFromWkt($spatialFilter);
+    }
+
+    $aMapTips = array();
+    $aURL = array();
+    $aTipLabel = array();
+    foreach($aLayer as $key=>$layer){
+        $oLayer = @$oMap->GetLayerByName($layer);
+
+        // make sure the layer exists in the map.
+        if(is_object($oLayer)){
+            $oLayer->set('tolerance', 0);
+
+            if ($oLayer->type ==  MS_LAYER_RASTER || $oLayer->type == MS_LAYER_QUERY ||
+                    $oLayer->type ==  MS_LAYER_CIRCLE ||  $oLayer->type == MS_LAYER_CHART) {
+                    die("{'error':'maptips are only valid for vector layers'}");
+                }
+
+
+            if (@$oLayer->queryByShape($oSpatialFilter) == MS_SUCCESS) {
+
+                $oRes = $oLayer->getResult(0);
+                $oLayer->open();
+
+                $oShape = $oLayer->getShape($oRes->tileindex,$oRes->shapeindex);
+
+                $szMapTipText .= $oLayer->name." : ".$oShape->values[$aMapTipTextField[$key]].$szBreak;
+
+                $szLabels = $aLabel[$key];
+
+                $szMapTip  = $oShape->values[$aMapTipTextField[$key]];
+                $szURL = buildCustonUrl($oShape->values,$aMapTipURL[$key]);
+
+                $szMapTip = $szMapTip != "undefined" ? $szMapTip : "";
+                $szURL = $szURL != "undefined" ? $szURL : "";
+                $szLabels = $szLabels != "undefined" ? $szLabels : "";
+
+
+                array_push($aMapTips, $szMapTip);
+                array_push($aURL, $szURL);
+                array_push($aTipLabel,$szLabels);
+
+                $oLayer->close();
+            }
+        }
+    }
+    echo "{'maptips':".var2json($aMapTips).",'url':".var2json($aURL).",'label':".var2json($aTipLabel)."}";
+}
+else
+{
+echo "{'maptips':'','url':'','label':''}";
+}
+
+function buildCustonUrl($aValues,$url){
+
+    if($url != ""){
+        $pattern = "/\[(.+?)\]/";
+        preg_match_all($pattern, $url, $values,PREG_PATTERN_ORDER);
+
+        if(is_array($values[0])){
+            foreach($values[0] as $key=>$item){
+                $url = str_replace($item,$aValues[$values[1][$key]],$url);
+            }
+        }
+        return $url;
+    }
+    else
+    {
+    return "";
+    }
+}
+
+?>

Modified: branches/fusion-2.0/layers/MapServer/php/Measure.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/Measure.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/Measure.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -58,7 +58,7 @@
         $distance = GetMetersPerUnit($oMap->units)*$distance;
     }
       
-    header('Content-type: text/x-json');
+    header('Content-type: application/json');
     header('X-JSON: true');
     echo "{distance:$distance}";
     exit;

Modified: branches/fusion-2.0/layers/MapServer/php/Query.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/Query.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/Query.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -122,7 +122,7 @@
 if ($bExtendSelection) {
 }
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 if ($result->hasSelection) {
     $oMap->savequery(getSessionSavePath()."query.qry");

Modified: branches/fusion-2.0/layers/MapServer/php/Selection.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/Selection.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/Selection.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -34,16 +34,17 @@
 include('../../../common/php/Utilities.php');
 include ("Utilities.php");
 
-
 if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
     $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
 }
 
 $result = NULL;
 $result->layers = array();
+$oMapProjection = $oMap->getProjection();
 
 
-if (isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "") 
+
+if (isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "")
 {
     $oMap->loadquery($_REQUEST['queryfile']);
 
@@ -58,7 +59,7 @@
             $aLayers = split(",", $_REQUEST['layers']);
             $bAllLayers = 0;
         }
-    
+
         $aStartCount = array();
         if (isset($_REQUEST['startcount']) && $_REQUEST['startcount'] !='')
         {
@@ -71,7 +72,7 @@
             echo "error : number of layers and number of startcount should be the same";
             exit;
         }
-    
+
         $properties = $_SESSION['selection_array'];
         $aSelectedLayers = $properties->layers;
         if (count($aSelectedLayers) > 0)
@@ -85,7 +86,7 @@
             for ($i=0; $i<count($aSelectedLayers); $i++)
             {
                 $layerName =  $aSelectedLayers[$i];
-                if (($bAllLayers || in_array($layerName, $aLayers)) && 
+                if (($bAllLayers || in_array($layerName, $aLayers)) &&
                     $properties->$layerName->numelements > 0)
                 {
                     array_push($result->layers, $layerName);
@@ -93,10 +94,10 @@
                     $result->$layerName->propertyvalues = $properties->$layerName->propertyvalues;
                     $result->$layerName->propertytypes = $properties->$layerName->propertytypes;
                     $result->$layerName->metadatanames = $properties->$layerName->metadatanames;
-                
+                    $result->$layerName->bbox = getBBox($layerName);
                     /*if start and count are given, validate them. If valid return the valid elements.
                       if not return all elements. */
-                 
+
                     $start = -1;
                     $count = -1;
                     if (count($aStartCount) > 0)
@@ -116,7 +117,7 @@
                         }
 
                         /*invalid entries*/
-                        if ($start < 0 || $count <=0 || 
+                        if ($start < 0 || $count <=0 ||
                             $start >= $properties->$layerName->numelements ||
                             $count > $properties->$layerName->numelements ||
                             ($start + $count) > $properties->$layerName->numelements)
@@ -134,15 +135,40 @@
                     }
                     //print_r($properties->$layerName);
                     $result->$layerName->numelements = $count;
-        
+
                     $result->$layerName->values = array();
                     $result->$layerName->metadata = array();
                     $iIndice = 0;
+
                     for ($j=$start; $j<($start+$count); $j++)
                     {
+                        $szLayerProjection = getLayerProjection($oMap,$layerName);
+
+                        $oLayerExtents = $result->$layerName->bbox;
+
+                        if($szLayerProjection){
+                            if($szLayerProjection != $oMapProjection){
+
+                                $minx = $oLayerExtents->minx;
+                                $miny =$oLayerExtents->miny;
+                                $maxx = $oLayerExtents->maxx;
+                                $maxy =$oLayerExtents->maxy;
+
+                                reprojectPoint( &$minx, &$miny, $szLayerProjection, $oMapProjection );
+                                reprojectPoint( &$maxx, &$maxy, $szLayerProjection, $oMapProjection );
+
+                                $result->extents->minx = $minx;
+                                $result->extents->miny = $miny;
+                                $result->extents->maxx = $maxx;
+                                $result->extents->maxy= $maxy;
+
+                            }
+                        }
+
                         $result->$layerName->values[$iIndice] = $properties->$layerName->values[$j];
                         $result->$layerName->metadata[$iIndice] = $properties->$layerName->metadata[$j];
                         $iIndice++;
+
                     }
                 }
             }
@@ -150,10 +176,76 @@
     }
 }
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 echo var2json($result);
 
+function getBBox($layername){
+    global $properties;
+    foreach($properties->$layername->metadatanames as $key=>$value){
+        if($value == "bbox"){
+            $aBBox = split(",",$properties->$layername->metadata[0][$key]);
+            $oBBox->minx = $aBBox[0];
+            $oBBox->miny = $aBBox[1];
+            $oBBox->maxx = $aBBox[2];
+            $oBBox->maxy= $aBBox[3];
+            return $oBBox;
+        }
+    }
+    return false;
+}
 
+function getLayerProjection($oMap,$szLayerName){
+    $oLayer = &$oMap->getLayerByName($szLayerName);
+    if($oLayer){
+        $szProjection = $oLayer->getProjection();
+        if($szProjection){
+            return $szProjection;
+        }
+        else
+        {
+            return false;
+        }
+    }
+    else
+    {
+    return false;
+    }
+}
 
+function reprojectPoint( &$nX, &$nY, $szFrom, $szTo )
+{
+    //echo "reproject $nX,$nY from $szFrom to $szTo\n";
+
+    //check to see if reprojection is necessary
+    if ($szFrom == '' || $szTo == '')
+    {
+        return;
+    }
+
+    if (stristr($szFrom, "epsg") !== false &&
+        stristr( $szFrom, "init=") == false)
+    {
+        $szFrom = "init=".strtolower($szFrom);
+    }
+    if (stristr($szTo, "epsg") !== false &&
+        stristr( $szTo, "init=") == false)
+    {
+        $szTo = "init=".strtolower($szTo);
+    }
+
+    if ($szFrom == $szTo)
+    {
+        return;
+    }
+
+    $oPoint = ms_newPointObj();
+    $oPoint->setXY( $nX, $nY );
+    $oPoint->project( ms_newProjectionObj( $szFrom ),
+                      ms_newProjectionObj( $szTo )
+                       );
+    $nX = $oPoint->x;
+    $nY = $oPoint->y;
+    //echo " :".$nX." ,".$nY."<br>";
+}
 ?>

Modified: branches/fusion-2.0/layers/MapServer/php/SetLayers.php
===================================================================
--- branches/fusion-2.0/layers/MapServer/php/SetLayers.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/layers/MapServer/php/SetLayers.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -32,7 +32,7 @@
 include ("Common.php");
 include ("Utilities.php");
 
-header('Content-type: text/x-json');
+header('Content-type: application/json');
 header('X-JSON: true');
 
 if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
@@ -49,7 +49,7 @@
 
 if (count($currentLayers) != count($layers) ) {
   echo "/* removing layers ";
-	for ($i=0; $i<count($currentLayers); $i++) {
+	for ($i=0; $i<=count($currentLayers); $i++) {
 		if ($i != $layers[$i]) {
 		  echo $i." ";
 			$layer = $oMap->getLayer($i);

Modified: branches/fusion-2.0/lib/ApplicationDefinition.js
===================================================================
--- branches/fusion-2.0/lib/ApplicationDefinition.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/ApplicationDefinition.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -917,7 +917,7 @@
                         if (!widget.uiClass) {
                             widget.uiClass = Jx.Toolbar.Item;
                             widget.setUiObject(new widget.uiClass());
-                        } else if (widget.uiClass == Jx.Button || widget.uiClass == Jx.Menu) {
+                        } else if (widget.uiClass == Jx.Button || widget.uiClass == Jx.Button.Color || widget.uiClass == Jx.Menu) {
                             widget.setUiObject(new widget.uiClass({
                                 label: widgetTag.label,
                                 image: widgetTag.imageUrl,

Deleted: branches/fusion-2.0/lib/CanvasTool.js
===================================================================
--- branches/fusion-2.0/lib/CanvasTool.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/CanvasTool.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -1,947 +0,0 @@
-/**
- * Fusion.Tool.Canvas
- *
- * $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.Tool.Canvas
- *
- * Utility base class for drawing features on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas = OpenLayers.Class({
-    context: null,
-    canvas: null,
-    width: null,
-    height: null,
-    
-    initializeCanvas: function() {
-        this.context = null;
-        this.canvas = null;
-        this.width = null;
-        this.height = null;
-        
-        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 = OpenLayers.Function.bind(this.resizeCanvas, this);
-    },
-    
-    /**
-     * (public) clearContext()
-     *
-     * wipe the slate clean
-     */
-    clearContext: function() {
-        //console.log('Fusion.Tool.Canvas.clearContext');
-        if (this.context) {
-            this.context.clearRect(0,0,this.width,this.height);
-        }
-    },
-
-    activateCanvas: function() {
-        var map = this.getMap();
-        map.registerForEvent(Fusion.Event.MAP_RESIZED, this.resizeCanvasFn);
-        var domObj = map.getDomObj();
-        
-        var size = $(domObj).getContentBoxSize();
-        this.width = size.width;
-        this.height = size.height;
-        
-        /* create dynamic canvas */
-        if (!this.canvas) {
-            this.canvas = document.createElement('canvas');
-            
-            // we need to init this for IE 
-            if (typeof G_vmlCanvasManager != "undefined") { 
-                document.getElementsByTagName('BODY')[0].appendChild(this.canvas);
-                G_vmlCanvasManager.initElement(this.canvas); 
-                this.canvas = document.getElementsByTagName('BODY')[0].lastChild;
-            } 
-            
-            this.canvas.id = 'featureDigitizer';
-            this.canvas.style.position = 'absolute';
-            this.canvas.style.top = '0px';
-            this.canvas.style.left = '0px';
-            this.canvas.style.width = this.width+'px';
-            this.canvas.style.height = this.height+'px';
-            this.canvas.width = this.width;
-            this.canvas.height = this.height;
-            this.canvas.style.zIndex = 99;
-            
-        }
-    
-        domObj.appendChild(this.canvas);
-        if (!this.context) {
-            this.context = this.canvas.getContext('2d');
-        }
-        this.canvas.style.width = this.width+'px';
-        this.canvas.style.height = this.height+'px';
-        this.canvas.width = this.width;
-        this.canvas.height = this.height;
-        
-        map.observeEvent('mousemove', this.mouseMoveCB);
-        map.observeEvent('mouseup', this.mouseUpCB);
-        map.observeEvent('mousedown', this.mouseDownCB);
-        map.observeEvent('dblclick', this.dblClickCB);
-    },
-    
-    resizeCanvas: function() {
-        var map = this.getMap();
-        var domObj = map.getDomObj();
-        var size = $(domObj).getContentBoxSize();
-        this.width = size.width;
-        this.height = size.height;
-        this.canvas.style.width = this.width+'px';
-        this.canvas.style.height = this.height+'px';
-        this.canvas.width = this.width;
-        this.canvas.height = this.height;        
-    },
-    
-    /**
-     * (public) deactivate()
-     *
-     * deactivate the line digitizing tool
-     */
-    deactivateCanvas: function() {
-        //console.log('Fusion.Tool.Canvas.deactivate');
-        var map = this.getMap();
-        map.deregisterForEvent(Fusion.Event.MAP_RESIZED, this.resizeCanvasFn);
-        map.getDomObj().removeChild(this.canvas);
-        this.context.clearRect(0,0,this.width,this.height);
-        map.stopObserveEvent('mousemove', this.mouseMoveCB);
-        map.stopObserveEvent('mouseup', this.mouseUpCB);
-        map.stopObserveEvent('mousedown', this.mouseDownCB);
-        map.stopObserveEvent('dblclick', this.dblClickCB);
-    },
-    
-    /**
-     * (public) mouseDown(e)
-     *
-     * handle the mouse down event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    mouseDown: function(e) { },
-
-    /**
-     * (public) mouseUp(e)
-     *
-     * handle the mouse up event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    mouseUp: function(e) { },
-
-    /**
-     * (public) mouseMove(e)
-     *
-     * handle the mouse move event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    mouseMove: function(e) { },
-
-    /**
-     * (public) dblClick(e)
-     *
-     * handle the mouse dblclick event
-     *
-     * @param e Event the event that happened on the mapObj
-     */
-    dblClick: function(e) { }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.Point
- *
- * Utility base class for drawing point features on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Point = OpenLayers.Class({
-    center: null,
-    radius: null,
-    lineStyle: null,
-    fillStyle: null,
-    
-    initialize: function(map) {
-        this.center = new Fusion.Tool.Canvas.Node(0,0, map);
-        this.radius = 5;
-        this.lineStyle = new Fusion.Tool.Canvas.Style({lineWidth:2,strokeStyle:'rgba(0,0,0,1.0)'});
-        this.fillStyle = new Fusion.Tool.Canvas.Style({fillStyle:'rgba(0,0,255, 0.5)'});
-        this.segments = [];
-        
-    },
-    
-    setPoint: function(x,y) {
-        this.center.set(x,y);
-    },
-    
-    getPoint: function() {
-        this.center.updateGeo();
-        return {x:this.center.x, y:this.center.y};
-    },
-    
-    draw: function( context ) {
-        var x = this.center.px;
-        var y = this.center.py;
-        var radius = this.radius;
-        
-        this.fillStyle.apply(context);
-        this.lineStyle.apply(context);
-        
-        context.beginPath();
-        context.arc(x,y,radius, 0, 2*Math.PI, 1);
-        context.closePath();
-        context.fill(); 
-        context.stroke();
-        
-    },
-
-    getNodes: function() {
-        return [this.center];
-    },
-
-    clean: function() {},
-    
-    updateGeo: function() {
-        this.center.updateGeo();
-    },
-    
-    updatePx: function() {
-        this.center.updatePx();
-    }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.Circle
- *
- * Utility base class for drawing circle features on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Circle = OpenLayers.Class({
-    map: null,
-    center: null,
-    radius: null,
-    radiusPx: null,
-    start: null,
-    end: null,
-    lineStyle: null,
-    fillStyle: null,
-    
-    initialize: function(map) {
-        this.map = map;
-        this.center = new Fusion.Tool.Canvas.Node(0,0, map);
-        this.radius = 1;
-        this.lineStyle = new Fusion.Tool.Canvas.Style({lineWidth:2,strokeStyle:'rgba(0,0,0,1.0)'});
-        this.fillStyle = new Fusion.Tool.Canvas.Style({fillStyle:'rgba(0,0,255, 0.5)'});
-        this.segments = [];
-    },
-    
-    setCenter: function(x,y) {
-        this.center.set(x,y);
-    },
-    
-    setRadius: function(r) {
-        this.radius = Math.abs(r);
-        this.radiusPx = this.map.geoToPixMeasure(this.radius);
-    },
-    
-    draw: function( context ) {
-        var x = this.center.px;
-        var y = this.center.py;
-        var radius = this.radiusPx;
-        this.fillStyle.apply(context);
-        this.lineStyle.apply(context);
-        
-        context.beginPath();
-        if (this.start && this.end) {
-            context.moveTo(x,y);
-            var s = this.start;
-            var e = this.end;
-            if (s < e) {
-                var t = s;
-                s = e;
-                e = t;
-            }
-            var sx = x + Math.sin(s) * radius;
-            var sy = y - Math.cos(s) * radius;
-            context.lineTo(sx,sy);
-            context.arc(x,y,radius, s - Math.PI/2, e - Math.PI/2, 1);
-            context.lineTo(x, y);
-        } else {
-            context.arc(x,y,radius, 0, 2*Math.PI, 1);
-        }
-        context.closePath();
-        context.fill(); 
-        context.stroke();        
-    },
-    
-    getNodes: function() {
-        return [this.center];
-    },
-    
-    clean: function() {},
-    
-    updateGeo: function() {
-        this.center.updateGeo();
-        this.radius = this.map.pixToGeoMeasure(this.radiusPx);
-    },
-    
-    updatePx: function() {
-        this.center.updatePx();
-        this.radiusPx = this.map.geoToPixMeasure(this.radius);
-    }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.Polygon
- *
- * Utility base class for drawing polygon features on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Polygon = OpenLayers.Class({
-    segments: null,
-    lineStyle: null,
-    fillStyle: null,
-    map: null,
-    
-    initialize: function(map) {
-        this.map = map;
-        this.segments = [];
-        this.lineStyle = new Fusion.Tool.Canvas.Style({lineWidth:2,strokeStyle:'rgba(0,0,0,1.0)'});
-        this.fillStyle = new Fusion.Tool.Canvas.Style({fillStyle:'rgba(0,0,255, 0.5)'});
-    },
-
-    clean: function() {
-        var nodes = this.getNodes();
-        this.segments = [];
-        var n1 = nodes[0];
-        //console.log('n1: '+ n1);
-        var n2 = nodes[1];
-        for (var i=1; i<nodes.length;i++) {
-            if (n1.x != n2.x || n1.y != n2.y) {
-                this.addSegment(new Fusion.Tool.Canvas.Segment(n1,n2));
-                //console.log('n2: '+ n2);
-                n1 = n2;
-            }
-            n2 = nodes[i];
-        }
-        
-        this.addSegment(new Fusion.Tool.Canvas.Segment(n1, nodes[0]));
-        //console.log(this);
-    },
-
-    getNodes: function() {
-        var nodes = [];
-        nodes.push(this.segments[0].from);
-        for (var i=0; i<this.segments.length; i++) {
-            nodes.push(this.segments[i].to);
-        }
-        return nodes;
-    },
-
-    /*
-     * reverse the nodes in the feature
-     * and adjust segments
-     */
-    reverseNodes: function() {
-        var nSegments = this.segments.length;
-        if (!nSegments) {
-            return;
-        }
-        //flip nodes on each segment
-        for (var i=0; i < nSegments; i++) {
-            var seg = this.segments[i];
-            var tmp = seg.from;
-            seg.from = seg.to;
-            seg.to = tmp;
-        };
-        //reverse segment order
-        this.segments.reverse();
-    },
-    
-    /*
-     * remove node from the nodes in this feature
-     * and adjust segments
-     */
-    removeNode: function(node) {
-        //end cases
-        if (node == this.segments[0].from) {
-            this.segments[0].from = null;
-            this.segments.shift();
-            this.segments[0].from = this.segments[this.segments.length - 1].to;
-            return;
-        }
-        if (node == this.segments[this.segments.length -1].from) {
-            this.segments[this.segments.length -1].from = null;
-            this.segments.pop();
-            this.segments[0].from = this.segments[this.segments.length - 1].to;
-            return;
-        }
-        //general case
-        for (var i=1; i < this.segments.length; i++) {
-            if (node == this.segments[i].from){
-                this.segments[i-1].to = this.segments[i].to;
-                this.segments[i].from = null;
-                this.segments.splice(i, 1);
-                return;
-            }
-        };
-        
-    },
-    
-    draw: function( context ) {
-        var x = this.segments[0].from.px;
-        var y = this.segments[0].from.py;
-        if (this.segments.length > 2) {
-            /* draw closing line and fill */
-        
-            this.fillStyle.apply(context);
-            context.beginPath();
-            context.moveTo(x,y);
-            for (var i=0; i<this.segments.length; i++) {
-                var s = this.segments[i];
-                context.lineTo(s.to.px, s.to.py);         
-            }
-            context.lineTo(x,y); //closing line
-            context.closePath();
-            context.fill(); 
-        }
-        /* draw outline */
-        this.lineStyle.apply(context);
-        for (var i=0; i<this.segments.length; i++) {
-            this.segments[i].draw(context);
-        }
-    
-        var last = this.lastSegment();
-        context.beginPath();
-        context.moveTo(last.to.px,last.to.py);
-        context.lineTo(x,y);
-        context.stroke();
-    },
-
-    addSegment: function( s ) {
-        s.normalStyle = this.lineStyle;
-        this.segments[this.segments.length] = s;
-        //console.log('add segment ' + s);
-    },
-
-    lastSegment: function() {
-        return this.segments[this.segments.length-1];
-    },
-
-    /* find the segment with the given node as its end
-     * @param Object node - the node at the end
-     * @param Int tolerance - an optional tolerance in pixels
-     * @return the segment or null if nothing is found.
-     */
-     segmentTo: function(node) {
-         var margin = arguments.length > 1?arguments[1]:3;
-         for (var i=0; i<this.segments.length; i++) {
-             if (this.segments[i].hasTo(node, margin)) {
-                 return this.segments[i];
-             }
-         }
-         return null;        
-     },
-
-    /* find the segment with the given node as its start
-     * @param Object node - the node at the start
-     * @param Int tolerance - an optional tolerance in pixels
-     * @return the segment or null if there is none.
-     */
-     segmentFrom: function(node) {
-         var margin = arguments.length > 1?arguments[1]:3;
-         for (var i=0; i<this.segments.length; i++) {
-             if (this.segments[i].hasFrom(node, margin)) {
-                 return this.segments[i];
-             }
-         }
-         return null;        
-     },
-
-    /* extend an existing line by creating a new segment attached
-     * to the last segment
-     * @return the new segment
-     */
-    extendLine: function() {
-        var last = this.lastSegment();
-        var newNode = new Fusion.Tool.Canvas.Node(last.to.x, last.to.y, this.map);
-        var newSegment = new Fusion.Tool.Canvas.Segment( last.to, newNode );
-        this.addSegment(newSegment);
-        return newSegment;  
-    },
-
-    /* determine if the passed pixel coordinate is within this feature
-     * @param point Object - {px,py} representation of point
-     * @return true if the point is contained
-     *
-     * uses crossing test (Jordan Curve Theorem) algorithm discussed at
-     * http://www.acm.org/tog/editors/erich/ptinpoly/
-     */
-    contains: function(node) {
-        return true;  
-    },
-    
-    
-    toString: function() {
-        var szFeature = this.segments[0].from.toString();
-        for (var i=0; i < this.segments.length; i++) {
-            szFeature += ',' + this.segments[i].to.toString();
-        }
-        return 'POLYGON(' + szFeature + ')';
-    },
-    
-    updateGeo: function() {
-        for (var i=0; i < this.segments.length; i++) {
-            this.segments[i].updateGeo();
-        }
-    },
-    
-    updatePx: function() {
-        for (var i=0; i < this.segments.length; i++) {
-            this.segments[i].updatePx();
-        }
-    }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.line
- *
- * Utility base class for drawing line features on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Line = OpenLayers.Class({
-    segments: null,
-    lineStyle: null,
-    map: null,
-    
-    initialize: function(map) {
-        this.map = map;
-        this.segments = [];
-        this.lineStyle = new Fusion.Tool.Canvas.Style({strokeStyle:'rgba(0,0,0,1.0)'});
-    },
-
-    clean: function() {
-        var nodes = this.getNodes();
-        this.segments = [];
-        var n1 = nodes[0];
-        var n2 = nodes[1];
-        for (var i=1; i<nodes.length;i++) {
-            //console.log('n1: '+ n1);
-            //console.log('n2: '+ n2);
-            n2 = nodes[i];
-            if (n1.x != n2.x || n1.y != n2.y) {
-                this.addSegment(new Fusion.Tool.Canvas.Segment(n1,n2));
-                n1 = n2;
-            }
-        }
-        //console.log(this);
-    },
-
-    getNodes: function() {
-        var nodes = [];
-        nodes.push(this.segments[0].from);
-        for (var i=0; i<this.segments.length; i++) {
-            nodes.push(this.segments[i].to);
-        }
-        return nodes;
-    },
-
-    /*
-     * reverse the nodes in the feature
-     * and adjust segments
-     */
-    reverseNodes: function() {
-        var nSegments = this.segments.length;
-        if (!nSegments) {
-            return;
-        }
-        //flip nodes on each segment
-        for (var i=0; i < nSegments; i++) {
-            var seg = this.segments[i];
-            var tmp = seg.from;
-            seg.from = seg.to;
-            seg.to = tmp;
-        };
-        //reverse segment order
-        this.segments.reverse();
-    },
-    
-    /*
-     * remove node from the nodes in this feature
-     * and adjust segments
-     */
-    removeNode: function(node) {
-        //end cases
-        if (node == this.segments[0].from) {
-            this.segments[0].from = null;
-            this.segments.shift();
-            return;
-        }
-        if (node == this.segments[this.segments.length -1].from) {
-            this.segments[this.segments.length -1].from = null;
-            this.segments.pop();
-            return;
-        }
-        //general case
-        for (var i=1; i < this.segments.length; i++) {
-            if (node == this.segments[i].from){
-                this.segments[i-1].to = this.segments[i].to;
-                this.segments[i].from = null;
-                this.segments.splice(i, 1);
-                return;
-            }
-        };
-        
-    },
-
-    draw: function( context ) {
-        for (var i=0; i<this.segments.length; i++) {
-            this.segments[i].draw(context);
-        }
-    },
-
-    addSegment: function( s ) {
-        s.normalStyle = this.lineStyle;
-        this.segments[this.segments.length] = s;
-    },
-
-    lastSegment: function() {
-        return this.segments[this.segments.length-1];
-    },
-
-    /* find the segment with the given node as its end
-     * @param Object node - the node at the end
-     * @param Int tolerance - an optional tolerance in pixels
-     * @return the segment or null if nothing is found.
-     */
-     segmentTo: function(node) {
-         var margin = arguments.length > 1?arguments[1]:3;
-         for (var i=0; i<this.segments.length; i++) {
-             if (this.segments[i].hasTo(node, margin)) {
-                 return this.segments[i];
-             }
-         }
-         return null;        
-     },
-
-    /* find the segment with the given node as its start
-     * @param Object node - the node at the start
-     * @param Int tolerance - an optional tolerance in pixels
-     * @return the segment or null if there is none.
-     */
-     segmentFrom: function(node) {
-         var margin = arguments.length > 1?arguments[1]:3;
-         for (var i=0; i<this.segments.length; i++) {
-             if (this.segments[i].hasFrom(node, margin)) {
-                 return this.segments[i];
-             }
-         }
-         return null;        
-     },
-
-    /* extend an existing line by creating a new segment attached
-     * to the last segment
-     * @return the new segment
-     */
-    extendLine: function() {
-        var last = this.lastSegment();
-        var newNode = new Fusion.Tool.Canvas.Node(last.to.x, last.to.y, this.map);
-        var newSegment = new Fusion.Tool.Canvas.Segment( last.to, newNode );
-        this.addSegment(newSegment);
-        return newSegment;  
-    },
-    
-    updateGeo: function() {
-        for (var i=0; i < this.segments.length; i++) {
-            this.segments[i].updateGeo();
-        }
-    },
-    
-    updatePx: function() {
-        for (var i=0; i < this.segments.length; i++) {
-            this.segments[i].updatePx();
-        }
-    }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.Segment
- *
- * Utility base class for drawing line segments on the map.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Segment = OpenLayers.Class({
-    from: null,
-    to: null,
-    
-    initialize: function(from, to) {
-        this.from = from;
-        this.to = to;
-        this.isEditing = false;
-        this.normalStyle = new Fusion.Tool.Canvas.Style({lineWidth:1, strokeStyle:'rgba(0,0,0,1.0)'});
-        this.editStyle = new Fusion.Tool.Canvas.Style({lineWidth:1, strokeStyle:'rgba(255,0,0,1.0)'});
-    },
-
-    /* returns true if the node is at the end of this segment
-     * within the given margin
-     * @return Bool true if found within margin, false otherwise
-     */
-    hasTo: function(node, margin) {
-        return this.to.near({x:node.px, y:node.py}, margin);
-    },
-
-    /* returns true if the node is at the start of this segment
-     * within the given margin
-     * @return Bool true if found within margin, false otherwise
-     */
-    hasFrom: function(node, margin) {
-        return this.from.near({x:node.px, y:node.py}, margin);
-    },
-    
-    /* returns true if the given point falls along this segment
-     * within the given margin
-     * @return Bool true if found within margin, false otherwise
-     */
-    intersectsPoint: function(point, margin){
-        //check bbox
-        var minX = Math.min(this.to.px, this.from.px);
-        var maxX = Math.max(this.to.px, this.from.px);
-        if (point.x > maxX || point.x < minX){return false;};
-        var maxY = Math.max(this.to.py, this.from.py);
-        var minY = Math.min(this.to.py, this.from.py);
-        if (point.y < minY || point.y > maxY){return false;};
-        
-        //determine slope
-        var slope = parseFloat((maxY-minY))/(maxX-minX);
-        var segY = slope * (point.x - minX) + minY;
-        return (segY - margin < point.y && segY + margin > point.y);
-
-    },
-    
-    setNormalStyle: function( style ) {
-        this.normalStyle = style;
-    },
-
-    setEditStyle: function( style ) {
-        this.editStyle = style;
-    },
-
-    draw: function( context ) {
-        /* set up correct style */
-        if (this.isEditing) {
-            this.editStyle.apply(context);
-        } else {
-            this.normalStyle.apply(context);
-        }
-    
-        /* draw segment */
-        context.beginPath();
-        context.moveTo(this.from.px, this.from.py);
-        context.lineTo(this.to.px, this.to.py);
-        context.closePath();
-        context.stroke();
-    
-        /* draw nodes if editing */
-        if (this.isEditing) {
-            this.from.draw( context );
-            this.to.draw( context );
-        }
-    },
-
-    /* changes rendering style */
-    setEditing: function(bEditing) {
-        this.isEditing = bEditing;
-    },
-    
-    toString: function() {
-        return this.from.toString() + ', '+ this.to.toString();
-    },
-    
-    updateGeo: function() {
-        this.from.updateGeo();
-        this.to.updateGeo();
-    },
-    
-    updatePx: function() {
-        this.from.updatePx();
-        this.to.updatePx();
-    }
-});
-
-/********************************************
- * Class: Fusion.Tool.Canvas.Node
- *
- * Utility base class to hold nodes that make up otherr features
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Node = OpenLayers.Class({
-    x: null,
-    y: null,
-    px: null,
-    py: null,
-    uid: null,
-    map: null,
-    counter: [0],
-    isSelected: false,
-    
-    initialize: function(x,y, map) {
-        this.map = map;
-        this.set(x,y);
-        var p = map.geoToPix(x, y);
-        this.setPx(p.x, p.y);
-        this.radius = 3;
-        this.uid = this.counter[0];
-        this.counter[0]++;
-        this.normalStyle = new Fusion.Tool.Canvas.Style({lineWidth:1, strokeStyle:'rgba(0,0,0,1.0)'});
-        this.selectedStyle = new Fusion.Tool.Canvas.Style({lineWidth:1, fillStyle:'rgba(255,0,0,1.0)',
-                                                strokeStyle:'rgba(255,0,0,1.0)'});
-    },
-
-    set: function(x,y) {
-        this.x = x;
-        this.y = y;
-        //update px position
-        var p = this.map.geoToPix(x, y);
-        this.setPx(p.x, p.y);
-    },
-    
-    setPx: function(px, py) {
-        this.px = px;
-        this.py = py;
-    },
-    
-    updateGeo: function() {
-        if (!this.px || !this.py) {return;};
-        var g = this.map.pixToGeo(this.px, this.py);
-        this.set(g.x, g.y);
-    },
-    
-    updatePx: function() {
-        if (!this.x || !this.y) {return;};
-        var p = this.map.geoToPix(this.x, this.y);
-        this.setPx(p.x, p.y);
-    },
-    
-    /* returns true if the supplied pixel position is
-     * within the given tolerance
-     * @return Bool true if found within margin, false otherwise
-     */
-     /*TODO: uses a square envelope for speed but could use radius
-      *TODO: should support geographic tolerance
-      */
-    near: function(point, tolerance) {
-        var minX = point.x - tolerance;
-        var maxX = point.x + tolerance;
-        var maxY = point.y + tolerance;
-        var minY = point.y - tolerance;
-        return ((this.px > minX && this.px < maxX) && (this.py > minY && this.py < maxY))?true:false;
-    },
-
-    /* returns true if this node is
-     * within the given bbox
-     * @param Array bbox - array of pixel coordinates to search within
-     * @return Bool true if found within, false otherwise
-     */
-    within: function(bbox) {
-        //TODO: handle > 2 coord pairs
-        var minX = Math.min(bbox[0], bbox[2]);
-        var maxX = Math.max(bbox[0], bbox[2]);
-        var minY = Math.min(bbox[1], bbox[3]);
-        var maxY = Math.max(bbox[1], bbox[3]);
-        return ((this.px > minX && this.px < maxX) && (this.py > minY && this.py < maxY))?true:false;
-    },
-
-    /* draw a node on a canvas. */
-    draw: function( context ) {
-        /* set up correct style */
-        if (this.isSelected) {
-            this.selectedStyle.apply(context);
-        } else {
-            this.normalStyle.apply(context);
-        }
-
-        context.beginPath();
-        context.arc(this.px, this.py, this.radius, 0, 2*Math.PI,1);
-        context.closePath();
-        context.stroke();
-        if(this.isSelected){
-            context.fill();
-        };
-    },
-    
-    /* changes rendering style */
-    setSelected: function(bSelected) {
-        this.isSelected = bSelected;
-    },
-
-    toString: function() {
-        return '('+this.uid+') '+ this.x + ' ['+this.px+'px] '+ this.y+ ' ['+this.py+'px] ';
-    }
-});
-
-/* encapsulate a context style */
-/********************************************
- * Class: Fusion.Tool.Canvas.Style
- *
- * Utility base class to encapsulate a context style.
- * **********************************************************************/
-
-Fusion.Tool.Canvas.Style = OpenLayers.Class({
-    properties: ['fillStyle',
-                 'globalAlpha',
-                 'globalCompositeOperation',
-                 'lineCap',
-                 'lineJoin',
-                 'lineWidth',
-                 'miterLimit',
-                 'shadowBlur',
-                 'shadowColor',
-                 'shadowOffsetX',
-                 'shadowOffsetY',
-                 'strokeStyle'],
-    
-    initialize: function( o ) { 
-        for (var i=0; i<this.properties.length; i++) {
-            var p = this.properties[i];
-            this[p] = o[p] ? o[p]:null;
-        }
-    },
-
-    set: function( p, v ) {
-        this[p] = v;
-    },
-
-    apply: function(context) {
-        for (var i=0; i<this.properties.length; i++) {
-            var p = this.properties[i];
-            if (this[p]) {
-                context[p] = this[p];
-            }
-        }
-    }
-});

Copied: branches/fusion-2.0/lib/OpenLayers/Lang/es.js (from rev 1828, trunk/lib/OpenLayers/Lang/es.js)
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/Lang/es.js	                        (rev 0)
+++ branches/fusion-2.0/lib/OpenLayers/Lang/es.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -0,0 +1,125 @@
+/* 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["es"]
+ * Dictionary for Spanish, UTF8 encoding. 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.es = {
+
+    'unhandledRequest': "Respuesta a petición no gestionada ${statusText}",
+
+    'permalink': "Enlace permanente",
+
+    'overlays': "Capas superpuestas",
+
+    'baseLayer': "Capa Base",
+
+    'sameProjection':
+        "El mini mapa sólo funciona si está en la misma proyección que el mapa principal",
+
+    'readNotImplemented': "Lectura no implementada.",
+
+    'writeNotImplemented': "Escritura no implementada.",
+
+    'noFID': "No se puede actualizar un elemento para el que no existe FID.",
+
+    'errorLoadingGML': "Error cargando el fichero GML ${url}",
+
+    'browserNotSupported':
+        "Su navegador no soporta renderización vectorial. Los renderizadores soportados actualmente son:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : el componente debe ser del tipo ${geomType}",
+
+    // console message
+    'getFeatureError':
+        "getFeatureFromEvent llamado en una capa sin renderizador. Esto normalmente quiere decir que " +
+        "se ha destruido una capa, pero no el manejador asociado a ella.",
+
+    // console message
+    'minZoomLevelError':
+        "La propiedad minZoomLevel debe sólo utilizarse " +
+        "con las capas que tienen FixedZoomLevels. El hecho de que " +
+        "una capa wfs compruebe minZoomLevel is una reliquia del " +
+        "pasado. Sin embargo, no podemos eliminarla sin discontinuar " +
+        "probablemente las aplicaciones OL que puedan depender de ello. " +
+        "Así pues estamos haciéndolo obsoleto --la comprobación " +
+        "minZoomLevel se eliminará en la versión 3.0. Utilice el ajuste " +
+        "de resolution min/max en su lugar, tal como se describe aquí: " +
+        "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+    'commitSuccess': "Transacción WFS: ÉXITO ${response}",
+
+    'commitFailed': "Transacción WFS: FALLÓ ${response}",
+
+    'googleWarning':
+        "La capa Google no pudo ser cargada correctamente.<br><br>" +
+        "Para evitar este mensaje, seleccione una nueva Capa Base " +
+        "en el selector de capas en la esquina superior derecha.<br><br>" +
+        "Probablemente, esto se debe a que el script de la biblioteca de " +
+	"Google Maps no fue correctamente incluido en su página, o no " +
+	"contiene la clave del API correcta para su sitio.<br><br>" +
+        "Desarrolladores: Para ayudar a hacer funcionar esto correctamente, " +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>haga clic aquí</a>",
+
+    'getLayerWarning':
+        "La capa ${layerType} no pudo ser cargada correctamente.<br><br>" +
+        "Para evitar este mensaje, seleccione una nueva Capa Base " +
+        "en el selector de capas en la esquina superior derecha.<br><br>" +
+        "Probablemente, esto se debe a que el script de " +
+	"la biblioteca ${layerLib} " +
+        "no fue correctamente incluido en su página.<br><br>" +
+        "Desarrolladores: Para ayudar a hacer funcionar esto correctamente, " +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>haga clic aquí</a>",
+
+    'scale': "Escala = 1 : ${scaleDenom}",
+
+    // console message
+    'layerAlreadyAdded':
+        "Intentó añadir la capa: ${layerName} al mapa, pero ya había sido añadida previamente",
+
+    // console message
+    'reprojectDeprecated':
+        "Está usando la opción 'reproject' en la capa " +
+        "${layerName}. Esta opción está obsoleta: su uso fue diseñado " +
+        "para soportar la visualización de datos sobre mapas base comerciales, " + 
+        "pero esa funcionalidad debería conseguirse ahora mediante el soporte " +
+        "de la proyección Spherical Mercator. Más información disponible en " +
+        "http://trac.openlayers.org/wiki/SphericalMercator.",
+
+    // console message
+    'methodDeprecated':
+        "Este método está obsoleto y se eliminará en la versión 3.0. " +
+        "Por favor utilice el método ${newMethod} en su lugar.",
+
+    // console message
+    'boundsAddError': "Debe proporcionar los valores x e y a la función add.",
+
+    // console message
+    'lonlatAddError': "Debe proporcionar los valores lon y lat a la función add.",
+
+    // console message
+    'pixelAddError': "Debe proporcionar los valores x e y a la función add.",
+
+    // console message
+    'unsupportedGeometryType': "Tipo de geometría no soportada: ${geomType}",
+
+    // console message
+    'pagePositionFailed':
+        "OpenLayers.Util.pagePosition falló: el elemento con id ${elemId} puede haberse colocado de manera errónea.",
+                    
+    // console message
+    'filterEvaluateNotImplemented': "evaluate no está implementado para este tipo de filtro.",
+
+    'end': ''
+
+};

Copied: branches/fusion-2.0/lib/OpenLayers/Lang/hu.js (from rev 1828, trunk/lib/OpenLayers/Lang/hu.js)
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/Lang/hu.js	                        (rev 0)
+++ branches/fusion-2.0/lib/OpenLayers/Lang/hu.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -0,0 +1,123 @@
+/* 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["hu"]
+ * Dictionary for Hungarian.  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.hu = OpenLayers.Util.applyDefaults({
+
+    'unhandledRequest': "Nem kezelt kérés visszatérése ${statusText}",
+
+    'permalink': "Permalink",
+
+    'overlays': "Rávetítések",
+
+    'baseLayer': "Alapréteg",
+
+    'sameProjection':
+        "Az áttekintő térkép csak abban az esetben működik, ha ugyanazon a vetületen van mint a fő térkép.",
+
+    'readNotImplemented': "Olvasás nincs végrehajtva.",
+
+    'writeNotImplemented': "Írás nincs végrehajtva.",
+
+    'noFID': "Nem frissíthető olyan jellemző, amely nem rendelkezik FID-del.",
+
+    'errorLoadingGML': "Hiba GML-fájl betöltésekor ${url}",
+
+    'browserNotSupported':
+        "A böngészője nem támogatja a vektoros renderelést. A jelenleg támogatott renderelők:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : az összetevőnek ilyen típusúnak kell lennie: ${geomType}",
+
+    // console message
+    'getFeatureError':
+        "A getFeatureFromEvent réteget hívott meg renderelő nélkül. Ez rendszerint azt jelenti, hogy " +
+        "megsemmisített egy fóliát, de néhány ahhoz társított kezelőt nem.",
+
+    // console message
+    'minZoomLevelError':
+        "A minZoomLevel tulajdonságot csak a következővel való használatra szánták: " +
+        "a FixedZoomLevels-leszármazott fóliák. Ez azt jelenti, hogy " +
+        "a minZoomLevel wfs fólia jelölőnégyzetei már " +
+        "a múlté. Mi azonban nem távolíthatjuk el annak a veszélye nélkül, hogy " +
+        "az esetlegesen ettől függő OL alapú alkalmazásokat tönkretennénk." +
+        "Ezért ezt érvénytelenítjük -- a minZoomLevel " +
+        "az alul levő jelölőnégyzet a 3.0-s verzióból el lesz távolítva. Kérjük, helyette " +
+        "használja a  min/max felbontás beállítást, amelyről az alábbi helyen talál leírást: " +
+        "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+    'commitSuccess': "WFS tranzakció: SIKERES ${response}",
+
+    'commitFailed': "WFS tranzakció: SIKERTELEN ${response}",
+
+    'googleWarning':
+        "A Google fólia betöltése sikertelen.<br><br>" +
+        "Ahhoz, hogy ez az üzenet eltűnjön, válasszon egy új BaseLayer fóliát " +
+        "a jobb felső sarokban található fóliakapcsoló segítségével.<br><br>" +
+        "Nagy valószínűséggel ez azért van, mert a Google Maps könyvtár " +
+        "parancsfájlja nem található, vagy nem tartalmazza " +
+        "az Ön oldalához tartozó megfelelő API-kulcsot.<br><br>" +
+        "Fejlesztőknek: A helyes működtetésre vonatkozó segítség az alábbi helyen érhető el, " +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>kattintson ide</a>",
+
+    'getLayerWarning':
+        "A ${layerType} fólia nem töltődött be helyesen.<br><br>" +
+        "Ahhoz, hogy ez az üzenet eltűnjön, válasszon egy új BaseLayer fóliát " +
+        "a jobb felső sarokban található fóliakapcsoló segítségével.<br><br>" +
+        "Nagy valószínűséggel ez azért van, mert a ${layerLib} könyvtár " +
+        "parancsfájlja helytelen.<br><br>" +
+        "Fejlesztőknek: A helyes működtetésre vonatkozó segítség az alábbi helyen érhető el, " +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>kattintson ide</a>",
+
+    'scale': "Lépték = 1 : ${scaleDenom}",
+
+    // console message
+    'layerAlreadyAdded':
+        "Megpróbálta hozzáadni a ${layerName} fóliát a térképhez, de az már hozzá van adva",
+
+    // console message
+    'reprojectDeprecated':
+        "Ön a 'reproject' beállítást használja " +
+        "a ${layerName} fólián. Ez a beállítás érvénytelen: " +
+        "használata az üzleti alaptérképek fölötti adatok megjelenítésének támogatására " + 
+        "szolgált, de ezt a funkció ezentúl a " +
+        "Gömbi Mercator használatával érhető el. További információ az alábbi helyen érhető el: " +
+        "http://trac.openlayers.org/wiki/SphericalMercator.",
+
+    // console message
+    'methodDeprecated':
+        "Ez a módszer érvénytelenítve lett és a 3.0-s verzióból el lesz távolítva. " +
+        "Használja a ${newMethod} módszert helyette.",
+
+    // console message
+    'boundsAddError': "Az x és y értékeknek egyaránt meg kell felelnie, hogy a funkciót hozzáadhassa.",
+
+    // console message
+    'lonlatAddError': "A hossz. és szél. értékeknek egyaránt meg kell felelnie, hogy a funkciót hozzáadhassa.",
+
+    // console message
+    'pixelAddError': "Az x és y értékeknek egyaránt meg kell felelnie, hogy a funkciót hozzáadhassa.",
+
+    // console message
+    'unsupportedGeometryType': "Nem támogatott geometriatípus: ${geomType}",
+
+    // console message
+    'pagePositionFailed':
+        "OpenLayers.Util.pagePosition failed: lehetséges, hogy az ${elemId} azonosítójú elem téves helyre került.",
+                    
+    // console message
+    'filterEvaluateNotImplemented': "ennél a szűrőtípusnál kiértékelés nem hajtódik végre.",
+	
+    'end': ''
+}, OpenLayers.Lang["en"]);

Copied: branches/fusion-2.0/lib/OpenLayers/Lang/ja.js (from rev 1828, trunk/lib/OpenLayers/Lang/ja.js)
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/Lang/ja.js	                        (rev 0)
+++ branches/fusion-2.0/lib/OpenLayers/Lang/ja.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -0,0 +1,123 @@
+/* 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["ja"]
+ * Dictionary for Japanese.  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.ja = OpenLayers.Util.applyDefaults({
+
+    'unhandledRequest': "未処理の要求が ${statusText} を返しました",
+
+    'permalink': "Permalink",
+
+    'overlays': "オーバーレイ",
+
+    'baseLayer': "基準画層",
+
+    'sameProjection':
+        "オーバービュー マップは、メイン マップと同じ投影内にある場合のみ機能します",
+
+    'readNotImplemented': "読み込みは実行されませんでした。",
+
+    'writeNotImplemented': "書き込みは実行されませんでした。",
+
+    'noFID': "FID が指定されていないフィーチャは更新できません。",
+
+    'errorLoadingGML': "GML ファイル ${url} のロードでエラーが発生しました",
+
+    'browserNotSupported':
+        "ご使用のブラウザではベクトルのレンダリングがサポートされていません。現在サポートされているレンダラは次のとおりです:\n${renderers}",
+
+    'componentShouldBe': "addFeatures : コンポーネントは ${geomType} である必要があります",
+
+    // console message
+    'getFeatureError':
+        "レンダラがない画層で getFeatureFromEvent が呼び出されました。通常これは、画層が" +
+        "壊れていて、それに関連付けられている一部のハンドラが壊れていないことを意味します。",
+
+    // console message
+    'minZoomLevelError':
+        "minZoomLevel プロパティは、FixedZoomLevels の下位" +
+        "画層で使用することを意図したものです。wfs 画層が " +
+        "minZoomLevel をチェックすることはなくなりました。" +
+        "ただしこのプロパティを削除するには、これに依存する" +
+        " OL ベースのアプリケーションを削除する必要があります。" +
+        "このため、minZoomLevel 以下のチェックは、3.0 で" +
+        "廃止されます。代わりに最小/最大解像度設定を使用" +
+        "してください。次のサイトを参照してください:" +
+        "http://trac.openlayers.org/wiki/SettingZoomLevels",
+
+    'commitSuccess': "WFS トランザクション: 成功 ${response}",
+
+    'commitFailed': "WFS トランザクション: 失敗 ${response}",
+
+    'googleWarning':
+        "Google 画層を正しくロードできませんでした。<br><br>" +
+        "このメッセージを非表示にするには、右上にある画層" +
+        "スイッチ内の新しい BaseLayer を選択します。<br><br>" +
+        "これは、Google Maps ライブラリ スクリプトが含まれて" +
+        "いないか、サイトの正しい API キーが含まれていない" +
+        "ことが原因であると考えられます。<br><br>" +
+        "開発者の方へ: この作業を正しく実行するための情報は、" +
+        "<a href='http://trac.openlayers.org/wiki/Google' " +
+        "target='_blank'>ここ</a>をクリックしてください",
+
+    'getLayerWarning':
+        "${layerType} 画層を正しくロードできませんでした。<br><br>" +
+        "このメッセージを非表示にするには、右上にある画層" +
+        "スイッチ内の新しい BaseLayer を選択します。<br><br>" +
+        "これは、${layerLib} ライブラリ スクリプトが正しく含まれ" +
+        "ていないことが原因であると考えられます。<br><br>" +
+        "開発者の方へ: この作業を正しく実行するための情報は、" +
+        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
+        "target='_blank'>ここ</a>をクリックしてください",
+
+    'scale': "尺度 = 1 : ${scaleDenom}",
+
+    // console message
+    'layerAlreadyAdded':
+        "画層 ${layerName} をマップに追加しようとしましたが、この画層は既に追加されています",
+
+    // console message
+    'reprojectDeprecated':
+        "'reproject' オプションを同じ ${layerName} 画層で使用して" +
+        "います。このオプションは推奨しません。このオプションは商用の" +
+        "ベースマップにデータを表示することを意図したものですが、" + 
+        "その場合は Spherical Mercator サポートを使用してください。" +
+        "詳細については、http://trac.openlayers.org/wiki/SphericalMercator" +
+        "を参照してください。",
+
+    // console message
+    'methodDeprecated':
+        "このメソッドは非推奨で、3.0 で廃止されます。 " +
+        "代わりに ${newMethod} を使用してください。",
+
+    // console message
+    'boundsAddError': "関数を追加するには、x と y の両方の値を渡す必要があります。",
+
+    // console message
+    'lonlatAddError': "関数を追加するには、lon と lat の両方の値を渡す必要があります。",
+
+    // console message
+    'pixelAddError': "関数を追加するには、x と y の両方の値を渡す必要があります。",
+
+    // console message
+    'unsupportedGeometryType': "サポートされていないジオメトリ タイプ: ${geomType}",
+
+    // console message
+    'pagePositionFailed':
+        "OpenLayers.Util.pagePosition が失敗しました: ID ${elemId} を持つエレメントは誤った場所に配置されている可能性があります。",
+                    
+    // console message
+    'filterEvaluateNotImplemented': "このフィルタの種類では評価が実装されていません。",
+	
+	'end': ''
+}, OpenLayers.Lang["en"]);

Modified: branches/fusion-2.0/lib/OpenLayers/OpenLayers.js
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/OpenLayers.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/OpenLayers/OpenLayers.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -1,95 +1,95 @@
-/*
-
-  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. 
-*
-**/
-
-/**
- * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
- * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
- *
- * 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
- */
-
-/**
- * Contains portions of Gears <http://code.google.com/apis/gears/>
- *
- * Copyright 2007, Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  1. Redistributions of source code must retain the above copyright notice,
- *     this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright notice,
- *     this list of conditions and the following disclaimer in the documentation
- *     and/or other materials provided with the distribution.
- *  3. Neither the name of Google Inc. nor the names of its contributors may be
- *     used to endorse or promote products derived from this software without
- *     specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Sets up google.gears.*, which is *the only* supported way to access Gears.
- *
- * Circumvent this file at your own risk!
- *
- * In the future, Gears may automatically define google.gears.* without this
- * file. Gears may use these objects to transparently fix bugs and compatibility
- * issues. Applications that use the code below will continue to work seamlessly
- * when that happens.
- */
+/*
+
+  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. 
+*
+**/
+
+/**
+ * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
+ * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
+ *
+ * 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
+ */
+
+/**
+ * Contains portions of Gears <http://code.google.com/apis/gears/>
+ *
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of Google Inc. nor the names of its contributors may be
+ *     used to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Sets up google.gears.*, which is *the only* supported way to access Gears.
+ *
+ * Circumvent this file at your own risk!
+ *
+ * In the future, Gears may automatically define google.gears.* without this
+ * file. Gears may use these objects to transparently fix bugs and compatibility
+ * issues. Applications that use the code below will continue to work seamlessly
+ * when that happens.
+ */
 /* ======================================================================
     OpenLayers/SingleFile.js
    ====================================================================== */
@@ -114,6 +114,7 @@
 /* 
  * @requires OpenLayers/BaseTypes.js
  * @requires OpenLayers/Lang/en.js
+ * @requires OpenLayers/Console.js
  */ 
 
 (function() {
@@ -145,28 +146,22 @@
          * {String} Path to this script
          */
         _getScriptLocation: function () {
-            var scriptLocation = "";
-            var scriptName = OpenLayers._scriptName;
+            var scriptLocation = "";            
+            var isOL = new RegExp("(^|(.*?\\/))(" + OpenLayers._scriptName + ")(\\?|$)");
          
             var scripts = document.getElementsByTagName('script');
             for (var i=0, len=scripts.length; i<len; 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);
+                    var match = src.match(isOL);
+                    if(match) {
+                        scriptLocation = match[1];
                         break;
                     }
                 }
             }
             return scriptLocation;
-         }
+        }
     };
     /**
      * OpenLayers.singleFile is a flag indicating this file is being included
@@ -193,9 +188,9 @@
             "Rico/Corner.js",
             "Rico/Color.js",
             "OpenLayers/Ajax.js",
+            "OpenLayers/Events.js",
             "OpenLayers/Request.js",
             "OpenLayers/Request/XMLHttpRequest.js",
-            "OpenLayers/Events.js",
             "OpenLayers/Projection.js",
             "OpenLayers/Map.js",
             "OpenLayers/Layer.js",
@@ -224,6 +219,7 @@
             "OpenLayers/Layer/Markers.js",
             "OpenLayers/Layer/Text.js",
             "OpenLayers/Layer/WorldWind.js",
+            "OpenLayers/Layer/ArcGIS93Rest.js",
             "OpenLayers/Layer/WMS.js",
             "OpenLayers/Layer/WMS/Untiled.js",
             "OpenLayers/Layer/GeoRSS.js",
@@ -266,6 +262,8 @@
             "OpenLayers/Control/Permalink.js",
             "OpenLayers/Control/Scale.js",
             "OpenLayers/Control/ScaleLine.js",
+            "OpenLayers/Control/Snapping.js",
+            "OpenLayers/Control/Split.js",
             "OpenLayers/Control/LayerSwitcher.js",
             "OpenLayers/Control/DrawFeature.js",
             "OpenLayers/Control/DragFeature.js",
@@ -274,6 +272,7 @@
             "OpenLayers/Control/SelectFeature.js",
             "OpenLayers/Control/NavigationHistory.js",
             "OpenLayers/Control/Measure.js",
+            "OpenLayers/Control/WMSGetFeatureInfo.js",
             "OpenLayers/Geometry.js",
             "OpenLayers/Geometry/Rectangle.js",
             "OpenLayers/Geometry/Collection.js",
@@ -292,15 +291,21 @@
             "OpenLayers/Renderer/Canvas.js",
             "OpenLayers/Renderer/VML.js",
             "OpenLayers/Layer/Vector.js",
+            "OpenLayers/Layer/Vector/RootContainer.js",
             "OpenLayers/Strategy.js",
             "OpenLayers/Strategy/Fixed.js",
             "OpenLayers/Strategy/Cluster.js",
             "OpenLayers/Strategy/Paging.js",
             "OpenLayers/Strategy/BBOX.js",
+            "OpenLayers/Strategy/Save.js",
             "OpenLayers/Protocol.js",
             "OpenLayers/Protocol/HTTP.js",
             "OpenLayers/Protocol/SQL.js",
             "OpenLayers/Protocol/SQL/Gears.js",
+            "OpenLayers/Protocol/WFS.js",
+            "OpenLayers/Protocol/WFS/v1.js",
+            "OpenLayers/Protocol/WFS/v1_0_0.js",
+            "OpenLayers/Protocol/WFS/v1_1_0.js",
             "OpenLayers/Layer/PointTrack.js",
             "OpenLayers/Layer/GML.js",
             "OpenLayers/Style.js",
@@ -320,16 +325,24 @@
             "OpenLayers/Format/KML.js",
             "OpenLayers/Format/GeoRSS.js",
             "OpenLayers/Format/WFS.js",
+            "OpenLayers/Format/WFSDescribeFeatureType.js",
+            "OpenLayers/Format/WMSDescribeLayer.js",
+            "OpenLayers/Format/WMSDescribeLayer/v1_1.js",
             "OpenLayers/Format/WKT.js",
             "OpenLayers/Format/OSM.js",
             "OpenLayers/Format/GPX.js",
+            "OpenLayers/Format/Filter.js",
+            "OpenLayers/Format/Filter/v1.js",
+            "OpenLayers/Format/Filter/v1_0_0.js",
+            "OpenLayers/Format/Filter/v1_1_0.js",
             "OpenLayers/Format/SLD.js",
             "OpenLayers/Format/SLD/v1.js",
             "OpenLayers/Format/SLD/v1_0_0.js",
             "OpenLayers/Format/SLD/v1.js",
-            "OpenLayers/Format/Filter.js",
-            "OpenLayers/Format/Filter/v1.js",
-            "OpenLayers/Format/Filter/v1_0_0.js",
+            "OpenLayers/Format/WFST.js",
+            "OpenLayers/Format/WFST/v1.js",
+            "OpenLayers/Format/WFST/v1_0_0.js",
+            "OpenLayers/Format/WFST/v1_1_0.js",
             "OpenLayers/Format/Text.js",
             "OpenLayers/Format/JSON.js",
             "OpenLayers/Format/GeoJSON.js",
@@ -337,7 +350,9 @@
             "OpenLayers/Format/WMC/v1.js",
             "OpenLayers/Format/WMC/v1_0_0.js",
             "OpenLayers/Format/WMC/v1_1_0.js",
+            "OpenLayers/Format/WMSGetFeatureInfo.js",
             "OpenLayers/Layer/WFS.js",
+            "OpenLayers/Control/GetFeature.js",
             "OpenLayers/Control/MouseToolbar.js",
             "OpenLayers/Control/NavToolbar.js",
             "OpenLayers/Control/PanPanel.js",
@@ -387,6 +402,9 @@
  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
+/**
+ * @requires OpenLayers/Console.js
+ */
 
 /**
  * Namespace: Util
@@ -724,7 +742,7 @@
     //
     if (!this.viewRequestID ||
         (this.map && this.viewRequestID == this.map.viewRequestID)) { 
-        this.style.backgroundColor = null;
+        this.style.backgroundColor ="transparent";
         this.style.display = "";  
     }
 };
@@ -1179,6 +1197,51 @@
 };
 
 /**
+ * Property: precision
+ * {Number} The number of significant digits to retain to avoid
+ * floating point precision errors.
+ *
+ * We use 14 as a "safe" default because, although IEEE 754 double floats
+ * (standard on most modern operating systems) support up to about 16
+ * significant digits, 14 significant digits are sufficient to represent
+ * sub-millimeter accuracy in any coordinate system that anyone is likely to
+ * use with OpenLayers.
+ *
+ * If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
+ * of OpenLayers <2.8 is preserved. Be aware that this will cause problems
+ * with certain projections, e.g. spherical Mercator.
+ *
+ */
+OpenLayers.Util.DEFAULT_PRECISION = 14;
+
+/**
+ * Function: toFloat
+ * Convenience method to cast an object to a Number, rounded to the
+ * desired floating point precision.
+ *
+ * Parameters:
+ * number    - {Number} The number to cast and round.
+ * precision - {Number} An integer suitable for use with
+ *      Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
+ *      If set to 0, no rounding is performed.
+ *
+ * Returns:
+ * {Number} The cast, rounded number.
+ */
+OpenLayers.Util.toFloat = function (number, precision) {
+    if (precision == null) {
+        precision = OpenLayers.Util.DEFAULT_PRECISION;
+    }
+    var number;
+    if (precision == 0) {
+        number = parseFloat(number);
+    } else {
+        number = parseFloat(parseFloat(number).toPrecision(precision));
+    }
+    return number;
+};
+
+/**
  * Function: rad
  * 
  * Parameters:
@@ -1267,6 +1330,8 @@
         var end = OpenLayers.String.contains(url, "#") ?
                     url.indexOf('#') : url.length;
         paramsString = url.substring(start, end);
+    } else {
+        paramsString = url;
     }
         
     var parameters = {};
@@ -1364,82 +1429,87 @@
 OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
 OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
 
-//CS-MAP units
+// Units from CS-Map
 OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
-OpenLayers.INCHES_PER_UNIT["Inch"] = OpenLayers.INCHES_PER_UNIT.inches;
-OpenLayers.INCHES_PER_UNIT["Meter"] = 1.0/OpenLayers.METERS_PER_INCH;   //EPSG:9001
-OpenLayers.INCHES_PER_UNIT["Foot"] = 0.30480060960121920243/OpenLayers.METERS_PER_INCH;   //EPSG:9003
-OpenLayers.INCHES_PER_UNIT["IFoot"] = 0.30480000000000000000/OpenLayers.METERS_PER_INCH;   //EPSG:9002
-OpenLayers.INCHES_PER_UNIT["ClarkeFoot"] = 0.3047972651151/OpenLayers.METERS_PER_INCH;   //EPSG:9005
-OpenLayers.INCHES_PER_UNIT["SearsFoot"] = 0.30479947153867624624/OpenLayers.METERS_PER_INCH;   //EPSG:9041
-OpenLayers.INCHES_PER_UNIT["GoldCoastFoot"] = 0.30479971018150881758/OpenLayers.METERS_PER_INCH;   //EPSG:9094
-OpenLayers.INCHES_PER_UNIT["IInch"] = 0.02540000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["MicroInch"] = 0.00002540000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Mil"] = 0.00000002540000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Centimeter"] = 0.01000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Kilometer"] = 1000.00000000000000000000/OpenLayers.METERS_PER_INCH;   //EPSG:9036
-OpenLayers.INCHES_PER_UNIT["Yard"] = 0.91440182880365760731/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["SearsYard"] = 0.914398414616029/OpenLayers.METERS_PER_INCH;   //EPSG:9040
-OpenLayers.INCHES_PER_UNIT["IndianYard"] = 0.91439853074444079983/OpenLayers.METERS_PER_INCH;   //EPSG:9084
-OpenLayers.INCHES_PER_UNIT["IndianYd37"] = 0.91439523/OpenLayers.METERS_PER_INCH;   //EPSG:9085
-OpenLayers.INCHES_PER_UNIT["IndianYd62"] = 0.9143988/OpenLayers.METERS_PER_INCH;   //EPSG:9086
-OpenLayers.INCHES_PER_UNIT["IndianYd75"] = 0.9143985/OpenLayers.METERS_PER_INCH;   //EPSG:9087
-OpenLayers.INCHES_PER_UNIT["IndianFoot"] = 0.30479951/OpenLayers.METERS_PER_INCH;   //EPSG:9080
-OpenLayers.INCHES_PER_UNIT["IndianFt37"] = 0.30479841/OpenLayers.METERS_PER_INCH;   //EPSG:9081
-OpenLayers.INCHES_PER_UNIT["IndianFt62"] = 0.3047996/OpenLayers.METERS_PER_INCH;   //EPSG:9082
-OpenLayers.INCHES_PER_UNIT["IndianFt75"] = 0.3047995/OpenLayers.METERS_PER_INCH;   //EPSG:9083
-OpenLayers.INCHES_PER_UNIT["Mile"] = 1609.34721869443738887477/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["IYard"] = 0.91440000000000000000/OpenLayers.METERS_PER_INCH;   //EPSG:9096
-OpenLayers.INCHES_PER_UNIT["IMile"] = 1609.34400000000000000000/OpenLayers.METERS_PER_INCH;   //EPSG:9093
-OpenLayers.INCHES_PER_UNIT["NautM"] = 1852.00000000000000000000/OpenLayers.METERS_PER_INCH;   //EPSG:9030
-OpenLayers.INCHES_PER_UNIT["Lat-66"] = 110943.316488932731/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Lat-83"] = 110946.25736872234125/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Decimeter"] = 0.10000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Millimeter"] = 0.00100000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Dekameter"] = 10.00000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Decameter"] = 10.00000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Hectometer"] = 100.00000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["GermanMeter"] = 1.0000135965/OpenLayers.METERS_PER_INCH;   //EPSG:9031
-OpenLayers.INCHES_PER_UNIT["CaGrid"] = 0.999738/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["ClarkeChain"] = 20.1166194976/OpenLayers.METERS_PER_INCH;   //EPSG:9038          20.11669506"
-OpenLayers.INCHES_PER_UNIT["GunterChain"] = 20.11684023368047/OpenLayers.METERS_PER_INCH;   //EPSG:9033
-OpenLayers.INCHES_PER_UNIT["BenoitChain"] = 20.116782494375872/OpenLayers.METERS_PER_INCH;   //EPSG:9062
-OpenLayers.INCHES_PER_UNIT["SearsChain"] = 20.11676512155/OpenLayers.METERS_PER_INCH;   //EPSG:9042
-OpenLayers.INCHES_PER_UNIT["ClarkeLink"] = 0.201166194976/OpenLayers.METERS_PER_INCH;   //EPSG:9039
-OpenLayers.INCHES_PER_UNIT["GunterLink"] = 0.2011684023368047/OpenLayers.METERS_PER_INCH;   //EPSG:9034
-OpenLayers.INCHES_PER_UNIT["BenoitLink"] = 0.20116782494375872/OpenLayers.METERS_PER_INCH;   //EPSG:9063
-OpenLayers.INCHES_PER_UNIT["SearsLink"] = 0.2011676512155/OpenLayers.METERS_PER_INCH;   //EPSG:9043
-OpenLayers.INCHES_PER_UNIT["Rod"] = 5.02921005842012/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["IntnlChain"] = 20.1168/OpenLayers.METERS_PER_INCH;   //EPSG:9097
-OpenLayers.INCHES_PER_UNIT["IntnlLink"] = 0.201168/OpenLayers.METERS_PER_INCH;   //EPSG:9098
-OpenLayers.INCHES_PER_UNIT["Perch"] = 5.02921005842012/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Pole"] = 5.02921005842012/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Furlong"] = 201.1684023368046/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Rood"] = 3.778266898/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["CapeFoot"] = 0.3047972615/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Brealey"] = 375.00000000000000000000/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["ModAmFt"] = 0.304812252984505969011938/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["Fathom"] = 1.8288/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["NautM-UK"] = 1853.184/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["50kilometers"] = 50000.0/OpenLayers.METERS_PER_INCH;
-OpenLayers.INCHES_PER_UNIT["150kilometers"] = 150000.0/OpenLayers.METERS_PER_INCH;
+OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
+    "Inch": OpenLayers.INCHES_PER_UNIT.inches,
+    "Meter": 1.0 / OpenLayers.METERS_PER_INCH,   //EPSG:9001
+    "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH,   //EPSG:9003
+    "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9002
+    "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH,   //EPSG:9005
+    "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH,   //EPSG:9041
+    "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH,   //EPSG:9094
+    "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
+    "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
+    "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
+    "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9036
+    "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
+    "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH,   //EPSG:9040
+    "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH,   //EPSG:9084
+    "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH,   //EPSG:9085
+    "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH,   //EPSG:9086
+    "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH,   //EPSG:9087
+    "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH,   //EPSG:9080
+    "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH,   //EPSG:9081
+    "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH,   //EPSG:9082
+    "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH,   //EPSG:9083
+    "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
+    "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9096
+    "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9093
+    "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9030
+    "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
+    "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
+    "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
+    "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH,   //EPSG:9031
+    "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
+    "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH,   //EPSG:9038
+    "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH,   //EPSG:9033
+    "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH,   //EPSG:9062
+    "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH,   //EPSG:9042
+    "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH,   //EPSG:9039
+    "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH,   //EPSG:9034
+    "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH,   //EPSG:9063
+    "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH,   //EPSG:9043
+    "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
+    "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH,   //EPSG:9097
+    "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH,   //EPSG:9098
+    "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
+    "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
+    "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
+    "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
+    "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
+    "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
+    "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
+    "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
+    "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
+    "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
+    "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
+});
+
 //unit abbreviations supported by PROJ.4
-OpenLayers.INCHES_PER_UNIT["mm"] = OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0;
-OpenLayers.INCHES_PER_UNIT["cm"] = OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0;
-OpenLayers.INCHES_PER_UNIT["dm"] = OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0;
-OpenLayers.INCHES_PER_UNIT["km"] = OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0;
-OpenLayers.INCHES_PER_UNIT["kmi"] = OpenLayers.INCHES_PER_UNIT["nmi"];    //International Nautical Mile
-OpenLayers.INCHES_PER_UNIT["fath"] = OpenLayers.INCHES_PER_UNIT["Fathom"]; //International Fathom
-OpenLayers.INCHES_PER_UNIT["ch"] = OpenLayers.INCHES_PER_UNIT["IntnlChain"];  //International Chain
-OpenLayers.INCHES_PER_UNIT["link"] = OpenLayers.INCHES_PER_UNIT["IntnlLink"]; //International Link
-OpenLayers.INCHES_PER_UNIT["us-in"] = OpenLayers.INCHES_PER_UNIT["inches"]; //U.S. Surveyor's Inch
-OpenLayers.INCHES_PER_UNIT["us-ft"] = OpenLayers.INCHES_PER_UNIT["Foot"];	//U.S. Surveyor's Foot
-OpenLayers.INCHES_PER_UNIT["us-yd"] = OpenLayers.INCHES_PER_UNIT["Yard"];	//U.S. Surveyor's Yard
-OpenLayers.INCHES_PER_UNIT["us-ch"] = OpenLayers.INCHES_PER_UNIT["GunterChain"]; //U.S. Surveyor's Chain
-OpenLayers.INCHES_PER_UNIT["us-mi"] = OpenLayers.INCHES_PER_UNIT["Mile"];   //U.S. Surveyor's Statute Mile
-OpenLayers.INCHES_PER_UNIT["ind-yd"] = OpenLayers.INCHES_PER_UNIT["IndianYd37"];  //Indian Yard
-OpenLayers.INCHES_PER_UNIT["ind-ft"] = OpenLayers.INCHES_PER_UNIT["IndianFt37"];  //Indian Foot
-OpenLayers.INCHES_PER_UNIT["ind-ch"] = 20.11669506/OpenLayers.METERS_PER_INCH;  //Indian Chain
+OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
+    "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
+    "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
+    "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
+    "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
+    "kmi": OpenLayers.INCHES_PER_UNIT["nmi"],    //International Nautical Mile
+    "fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom
+    "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"],  //International Chain
+    "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link
+    "us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch
+    "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"],	//U.S. Surveyor's Foot
+    "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"],	//U.S. Surveyor's Yard
+    "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain
+    "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"],   //U.S. Surveyor's Statute Mile
+    "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"],  //Indian Yard
+    "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"],  //Indian Foot
+    "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH  //Indian Chain
+});
 
 /** 
  * Constant: DOTS_PER_INCH
@@ -1448,7 +1518,7 @@
 OpenLayers.DOTS_PER_INCH = 72;
 
 /**
- * Function: normalzeScale
+ * Function: normalizeScale
  * 
  * Parameters:
  * scale - {float}
@@ -1858,6 +1928,8 @@
  * options - {Object}
  *     displayClass - {String} Optional parameter.  A CSS class name(s) string
  *         to provide the CSS context of the rendered content.
+ *     containerElement - {DOMElement} Optional parameter. Insert the HTML to 
+ *         this node instead of the body root when calculating dimensions. 
  * 
  * Returns:
  * {OpenLayers.Size}
@@ -1872,6 +1944,9 @@
     container.style.position = "absolute";
     container.style.left = "-9999px";
         
+    var containerElement = (options && options.containerElement) 
+    	? options.containerElement : document.body;
+
     //fix a dimension, if specified.
     if (size) {
         if (size.w) {
@@ -1896,7 +1971,7 @@
     container.appendChild(content);
     
     // append container to body for rendering
-    document.body.appendChild(container);
+    containerElement.appendChild(container);
     
     // calculate scroll width of content and add corners and shadow width
     if (!w) {
@@ -1912,7 +1987,7 @@
 
     // remove elements
     container.removeChild(content);
-    document.body.removeChild(container);
+    containerElement.removeChild(container);
     
     return new OpenLayers.Size(w, h);
 };
@@ -2245,6 +2320,7 @@
  * @requires OpenLayers/BaseTypes/Bounds.js
  * @requires OpenLayers/BaseTypes/Element.js
  * @requires OpenLayers/Lang/en.js
+ * @requires OpenLayers/Console.js
  */
  
 /** 
@@ -2351,25 +2427,52 @@
         if(!context) {
             context = window;
         }
-        var tokens = template.split("${");
-        var item, last, replacement;
-        for(var i=1, len=tokens.length; i<len; 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();
+
+        // Example matching: 
+        // str   = ${foo.bar}
+        // match = foo.bar
+        var replacer = function(str, match) {
+            var replacement;
+
+            // Loop through all subs. Example: ${a.b.c}
+            // 0 -> replacement = context[a];
+            // 1 -> replacement = context[a][b];
+            // 2 -> replacement = context[a][b][c];
+            var subs = match.split(/\.+/);
+            for (var i=0; i< subs.length; i++) {
+                if (i == 0) {
+                    replacement = context;
                 }
-                tokens[i] = replacement + item.substring(++last); 
+
+                replacement = replacement[subs[i]];
+            }
+
+            if(typeof replacement == "function") {
+                replacement = args ?
+                    replacement.apply(null, args) :
+                    replacement();
+            }
+
+            // If replacement is undefined, return the string 'undefined'.
+            // This is a workaround for a bugs in browsers not properly 
+            // dealing with non-participating groups in regular expressions:
+            // http://blog.stevenlevithan.com/archives/npcg-javascript
+            if (typeof replacement == 'undefined') {
+                return 'undefined';
             } else {
-                tokens[i] = "${" + item;
+                return replacement; 
             }
-        }
-        return tokens.join("");
+        };
+
+        return template.replace(OpenLayers.String.tokenRegEx, replacer);
     },
+
+    /**
+     * Property: OpenLayers.String.tokenRegEx
+     * Used to find tokens in a string.
+     * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
+     */
+    tokenRegEx:  /\${([\w.]+?)}/g,
     
     /**
      * Property: OpenLayers.String.numberRegEx
@@ -2394,6 +2497,18 @@
      */
     isNumeric: function(value) {
         return OpenLayers.String.numberRegEx.test(value);
+    },
+    
+    /**
+     * APIFunction: numericIf
+     * Converts a string that appears to be a numeric value into a number.
+     * 
+     * Returns
+     * {Number|String} a Number if the passed value is a number, a String
+     *     otherwise. 
+     */
+    numericIf: function(value) {
+        return OpenLayers.String.isNumeric(value) ? parseFloat(value) : value;
     }
 
 };
@@ -2745,6 +2860,8 @@
  * To create a new OpenLayers-style class with multiple inheritance, use the
  *     following syntax:
  * > var MyClass = OpenLayers.Class(Class1, Class2, prototype);
+ * Note that instanceof reflection will only reveil Class1 as superclass.
+ * Class2 ff are mixins.
  *
  */
 OpenLayers.Class = function() {
@@ -2761,9 +2878,25 @@
         }
     };
     var extended = {};
-    var parent;
+    var parent, initialize;
     for(var i=0, len=arguments.length; i<len; ++i) {
         if(typeof arguments[i] == "function") {
+            // make the class passed as the first argument the superclass
+            if(i == 0 && len > 1) {
+                initialize = arguments[i].prototype.initialize;
+                // replace the initialize method with an empty function,
+                // because we do not want to create a real instance here
+                arguments[i].prototype.initialize = function() {};
+                // the line below makes sure that the new class has a
+                // superclass
+                extended = new arguments[i];
+                // restore the original initialize method
+                if(initialize === undefined) {
+                    delete arguments[i].prototype.initialize;
+                } else {
+                    arguments[i].prototype.initialize = initialize;
+                }
+            }
             // get the prototype of the superclass
             parent = arguments[i].prototype;
         } else {
@@ -2919,6 +3052,10 @@
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/Console.js
+ */
+
+/**
  * 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,
@@ -2977,16 +3114,16 @@
      */
     initialize: function(left, bottom, right, top) {
         if (left != null) {
-            this.left = parseFloat(left);
+            this.left = OpenLayers.Util.toFloat(left);
         }
         if (bottom != null) {
-            this.bottom = parseFloat(bottom);
+            this.bottom = OpenLayers.Util.toFloat(bottom);
         }
         if (right != null) {
-            this.right = parseFloat(right);
+            this.right = OpenLayers.Util.toFloat(right);
         }
         if (top != null) {
-            this.top = parseFloat(top);
+            this.top = OpenLayers.Util.toFloat(top);
         }
     },
 
@@ -3070,7 +3207,7 @@
 
         return bbox;
     },
-    
+ 
     /**
      * APIMethod: toGeometry
      * Create a new polygon geometry based on this bounds.
@@ -3298,12 +3435,18 @@
      *     bounds.
      */
     contains:function(x, y, inclusive) {
-    
         //set default
         if (inclusive == null) {
             inclusive = true;
         }
-        
+
+        if (x == null || y == null) {
+            return false;
+        }
+
+        x = OpenLayers.Util.toFloat(x);
+        y = OpenLayers.Util.toFloat(y);
+
         var contains = false;
         if (inclusive) {
             contains = ((x >= this.left) && (x <= this.right) && 
@@ -3317,82 +3460,85 @@
 
     /**
      * APIMethod: intersectsBounds
+     * Determine whether the target bounds intersects this bounds.  Bounds are
+     *     considered intersecting if any of their edges intersect or if one
+     *     bounds contains the other.
      * 
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * inclusive - {Boolean} Whether or not to include the border.  Default
-     *     is true.
+     * bounds - {<OpenLayers.Bounds>} The target bounds.
+     * inclusive - {Boolean} Treat coincident borders as intersecting.  Default
+     *     is true.  If false, bounds that do not overlap but only touch at the
+     *     border will not be considered as intersecting.
      *
      * Returns:
-     * {Boolean} The passed-in OpenLayers.Bounds object intersects this bounds.
-     *     Simple math just check if either contains the other, allowing for
-     *     partial.
+     * {Boolean} The passed-in bounds object intersects this bounds.
      */
     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 )));
+        var intersects = false;
+        var mightTouch = (
+            this.left == bounds.right ||
+            this.right == bounds.left ||
+            this.top == bounds.bottom ||
+            this.bottom == bounds.top
+        );
+        
+        // if the two bounds only touch at an edge, and inclusive is false,
+        // then the bounds don't *really* intersect.
+        if (inclusive || !mightTouch) {
+            // otherwise, if one of the boundaries even partially contains another,
+            // inclusive of the edges, then they do intersect.
+            var inBottom = (
+                ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
+                ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
+            );
+            var inTop = (
+                ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
+                ((this.top > bounds.bottom) && (this.top < bounds.top))
+            );
+            var inLeft = (
+                ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
+                ((this.left >= bounds.left) && (this.left <= bounds.right))
+            );
+            var inRight = (
+                ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
+                ((this.right >= bounds.left) && (this.right <= bounds.right))
+            );
+            intersects = ((inBottom || inTop) && (inLeft || inRight));
+        }
+        return intersects;
     },
     
     /**
      * APIMethod: containsBounds
+     * Determine whether the target bounds is contained within this bounds.
      * 
-     * 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
+     * bounds - {<OpenLayers.Bounds>} The target bounds.
+     * partial - {Boolean} If any of the target corners is within this bounds
+     *     consider the bounds contained.  Default is false.  If true, the
+     *     entire target bounds must be contained within this bounds.
+     * inclusive - {Boolean} Treat shared edges as contained.  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;
+        var bottomLeft  = this.contains(bounds.left, bounds.bottom, inclusive);
+        var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
+        var topLeft  = this.contains(bounds.left, bounds.top, inclusive);
+        var topRight = this.contains(bounds.right, bounds.top, inclusive);
         
-        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);
+        return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
+                         : (bottomLeft && bottomRight && topLeft && topRight);
     },
 
     /** 
@@ -3829,6 +3975,10 @@
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/Console.js
+ */
+
+/**
  * Class: OpenLayers.LonLat
  * This class represents a longitude and latitude pair
  */
@@ -3859,8 +4009,8 @@
      *     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);
+        this.lon = OpenLayers.Util.toFloat(lon);
+        this.lat = OpenLayers.Util.toFloat(lat);
     },
     
     /**
@@ -4018,6 +4168,10 @@
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/Console.js
+ */
+
+/**
  * Class: OpenLayers.Pixel
  * This class represents a screen coordinate, in x and y coordinates
  */
@@ -4134,343 +4288,6 @@
     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) {
- * >         OpenLayers.Console.userError(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);
-        }
-        if (this.id == null) {
-            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
    ====================================================================== */
 
@@ -4689,139 +4506,6 @@
     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/Popup.js
    ====================================================================== */
 
@@ -5000,6 +4684,20 @@
      *     Default is false.
      */
     panMapIfOutOfView: false,
+    
+    /**
+     * APIProperty: keepInMap 
+     * {Boolean} If panMapIfOutOfView is false, and this property is true, 
+     *     contrain the popup such that it always fits in the available map
+     *     space. By default, this is not set on the base class. If you are
+     *     creating popups that are near map edges and not allowing pannning,
+     *     and especially if you have a popup which has a
+     *     fixedRelativePosition, setting this to false may be a smart thing to
+     *     do. Subclasses may want to override this setting.
+     *   
+     *     Default is false.
+     */
+    keepInMap: false,
 
     /**
      * APIProperty: closeOnMove
@@ -5311,8 +5009,14 @@
         var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" + 
             this.contentDiv.innerHTML + 
             "<div>";
+ 
+        var containerElement = (this.map) ? this.map.layerContainerDiv
+        								  : document.body;
         var realSize = OpenLayers.Util.getRenderedDimensions(
-            preparedHTML, null, { displayClass: this.displayClass }
+            preparedHTML, null,	{
+                displayClass: this.displayClass,
+                containerElement: containerElement
+            }
         );
 
         // is the "real" size of the div is safe to display in our map?
@@ -5341,8 +5045,10 @@
                 //content is clipped in only one direction, so we need to 
                 // run getRenderedDimensions() again with a fixed dimension
                 var clippedSize = OpenLayers.Util.getRenderedDimensions(
-                    preparedHTML, fixedSize, 
-                    { displayClass: this.contentDisplayClass }
+                    preparedHTML, fixedSize, {
+                        displayClass: this.contentDisplayClass,
+                        containerElement: containerElement
+                    }
                 );
                 
                 //if the clipped size is still the same as the safeSize, 
@@ -5562,22 +5268,44 @@
         // is bigger than the map's viewport.
         //
         if (this.map && this.map.size) {
-
-            // Note that there *was* a reference to a
-            // 'OpenLayers.Popup.SCROLL_BAR_WIDTH' constant here, with special
-            // tolerance for it and everything... but it was never defined in
-            // the first place, so I don't know what to think.
+            
+            var extraX = 0, extraY = 0;
+            if (this.keepInMap && !this.panMapIfOutOfView) {
+                var px = this.map.getPixelFromLonLat(this.lonlat);
+                switch (this.relativePosition) {
+                    case "tr":
+                        extraX = px.x;
+                        extraY = this.map.size.h - px.y;
+                        break;
+                    case "tl":
+                        extraX = this.map.size.w - px.x;
+                        extraY = this.map.size.h - px.y;
+                        break;
+                    case "bl":
+                        extraX = this.map.size.w - px.x;
+                        extraY = px.y;
+                        break;
+                    case "br":
+                        extraX = px.x;
+                        extraY = px.y;
+                        break;
+                    default:    
+                        extraX = px.x;
+                        extraY = this.map.size.h - px.y;
+                        break;
+                }
+            }    
           
             var maxY = this.map.size.h - 
                 this.map.paddingForPopups.top - 
                 this.map.paddingForPopups.bottom - 
-                hPadding;
-    
+                hPadding - extraY;
+            
             var maxX = this.map.size.w - 
                 this.map.paddingForPopups.left - 
                 this.map.paddingForPopups.right - 
-                wPadding;
-    
+                wPadding - extraX;
+            
             safeContentSize.w = Math.min(safeContentSize.w, maxX);
             safeContentSize.h = Math.min(safeContentSize.h, maxY);
         }
@@ -5604,10 +5332,13 @@
         //use cached value if we have it
         var contentDivPadding = this._contentDivPadding;
         if (!contentDivPadding) {
-            //make the div invisible and add it to the page        
-            this.div.style.display = "none";
-            document.body.appendChild(this.div);
-    
+
+        	if (this.div.parentNode == null) {
+	        	//make the div invisible and add it to the page        
+	            this.div.style.display = "none";
+	            document.body.appendChild(this.div);
+	    	}
+	            
             //read the padding settings from css, put them in an OL.Bounds        
             contentDivPadding = new OpenLayers.Bounds(
                 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
@@ -5618,10 +5349,12 @@
     
             //cache the value
             this._contentDivPadding = contentDivPadding;
-    
-            //remove the div from the page and make it visible again
-            document.body.removeChild(this.div);
-            this.div.style.display = "";
+
+            if (this.div.parentNode == document.body) {
+	            //remove the div from the page and make it visible again
+	            document.body.removeChild(this.div);
+	            this.div.style.display = "";
+            }
         }
         return contentDivPadding;
     },
@@ -5851,6 +5584,12 @@
      */
     container: null,
     
+    /**
+     * Property: root
+     * {DOMElement}
+     */
+    root: null,
+
     /** 
      * Property: extent
      * {<OpenLayers.Bounds>}
@@ -6061,11 +5800,35 @@
      * geometry - {<OpenLayers.Geometry>} 
      */
     eraseGeometry: function(geometry) {},
+    
+    /**
+     * Method: moveRoot
+     * moves this renderer's root to a (different) renderer.
+     * To be implemented by subclasses that require a common renderer root for
+     * feature selection.
+     * 
+     * Parameters:
+     * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
+     */
+    moveRoot: function(renderer) {},
 
+    /**
+     * Method: getRenderLayerId
+     * Gets the layer that this renderer's output appears on. If moveRoot was
+     * used, this will be different from the id of the layer containing the
+     * features rendered by this renderer.
+     * 
+     * Returns:
+     * {String} the id of the output layer.
+     */
+    getRenderLayerId: function() {
+        return this.container.id;
+    },
+
     CLASS_NAME: "OpenLayers.Renderer"
 });
 /* ======================================================================
-    OpenLayers/Request.js
+    OpenLayers/Control.js
    ====================================================================== */
 
 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
@@ -6073,283 +5836,2148 @@
  * full text of the license. */
 
 /**
- * Namespace: OpenLayers.Request
- * The OpenLayers.Request namespace contains convenience methods for working
- *     with XMLHttpRequests.  These methods work with a cross-browser
- *     W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
+ * @requires OpenLayers/Console.js
  */
-OpenLayers.Request = {
+
+/**
+ * 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) {
+ * >         OpenLayers.Console.userError(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: "",
+    
     /**
-     * Constant: DEFAULT_CONFIG
-     * {Object} Default configuration for all requests.
+    * Property: title  
+    * {string}  This property is used for showing a tooltip over the  
+    * Control.  
+    */ 
+    title: "",
+
+    /** 
+     * Property: active 
+     * {Boolean} The control is active.
      */
-    DEFAULT_CONFIG: {
-        method: "GET",
-        url: window.location.href,
-        async: true,
-        user: undefined,
-        password: undefined,
-        params: null,
-        proxy: OpenLayers.ProxyHost,
-        headers: {},
-        data: null,
-        callback: function() {},
-        success: null,
-        failure: null,
-        scope: null
+    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);
+        }
+        if (this.id == null) {
+            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        }
     },
-    
+
     /**
-     * APIMethod: issue
-     * Create a new XMLHttpRequest object, open it, set any headers, bind
-     *     a callback to done state, and send any data.  It is recommended that
-     *     you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
-     *     This method is only documented to provide detail on the configuration
-     *     options available to all request methods.
+     * 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:
-     * config - {Object} Object containing properties for configuring the
-     *     request.  Allowed configuration properties are described below.
-     *     This object is modified and should not be reused.
+     * 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. 
      *
-     * Allowed config properties:
-     * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
-     *     OPTIONS.  Default is GET.
-     * url - {String} URL for the request.
-     * async - {Boolean} Open an asynchronous request.  Default is true.
-     * user - {String} User for relevant authentication scheme.  Set
-     *     to null to clear current user.
-     * password - {String} Password for relevant authentication scheme.
-     *     Set to null to clear current password.
-     * proxy - {String} Optional proxy.  Defaults to
-     *     <OpenLayers.ProxyHost>.
-     * params - {Object} Any key:value pairs to be appended to the
-     *     url as a query string.  Assumes url doesn't already include a query
-     *     string or hash.  Typically, this is only appropriate for <GET>
-     *     requests where the query string will be appended to the url.
-     *     Parameter values that are arrays will be
-     *     concatenated with a comma (note that this goes against form-encoding)
-     *     as is done with <OpenLayers.Util.getParameterString>.
-     * headers - {Object} Object with header:value pairs to be set on
-     *     the request.
-     * data - {String | Document} Optional data to send with the request.
-     *     Typically, this is only used with <POST> and <PUT> requests.
-     *     Make sure to provide the appropriate "Content-Type" header for your
-     *     data.  For <POST> and <PUT> requests, the content type defaults to
-     *     "application-xml".  If your data is a different content type, or
-     *     if you are using a different HTTP method, set the "Content-Type"
-     *     header to match your data type.
-     * callback - {Function} Function to call when request is done.
-     *     To determine if the request failed, check request.status (200
-     *     indicates success).
-     * success - {Function} Optional function to call if request status is in
-     *     the 200s.  This will be called in addition to callback above and
-     *     would typically only be used as an alternative.
-     * failure - {Function} Optional function to call if request status is not
-     *     in the 200s.  This will be called in addition to callback above and
-     *     would typically only be used as an alternative.
-     * scope - {Object} If callback is a public method on some object,
-     *     set the scope to that object.
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
+     *      or null.
      *
      * Returns:
-     * {XMLHttpRequest} Request object.  To abort the request before a response
-     *     is received, call abort() on the request object.
+     * {DOMElement} A reference to the DIV DOMElement containing the control
      */
-    issue: function(config) {        
-        // apply default config - proxy host may have changed
-        var defaultConfig = OpenLayers.Util.extend(
-            this.DEFAULT_CONFIG,
-            {proxy: OpenLayers.ProxyHost}
-        );
-        config = OpenLayers.Util.applyDefaults(config, defaultConfig);
+    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;
+    },
 
-        // create request, open, and set headers
-        var request = new OpenLayers.Request.XMLHttpRequest();
-        var url = config.url;
-        if(config.params) {
-            var paramString = OpenLayers.Util.getParameterString(config.params);
-            if(paramString.length > 0) {
-                var separator = (url.indexOf('?') > -1) ? '&' : '?';
-                url += separator + paramString;
+    /**
+     * 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;
         }
-        if(config.proxy && (url.indexOf("http") == 0)) {
-            url = config.proxy + encodeURIComponent(url);
+        return false;
+    },
+
+    CLASS_NAME: "OpenLayers.Control"
+});
+
+OpenLayers.Control.TYPE_BUTTON = 1;
+OpenLayers.Control.TYPE_TOGGLE = 2;
+OpenLayers.Control.TYPE_TOOL   = 3;
+/* ======================================================================
+    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. */
+
+/**
+ * @requires OpenLayers/Console.js
+ */
+
+/**
+ * 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();
         }
-        request.open(
-            config.method, url, config.async, config.user, config.password
-        );
-        for(var header in config.headers) {
-            request.setRequestHeader(header, config.headers[header]);
+        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];
+        }
 
-        // bind callbacks to readyState 4 (done)
-        var complete = (config.scope) ?
-            OpenLayers.Function.bind(config.callback, config.scope) :
-            config.callback;
+        // 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;
+        }
         
-        // optional success callback
-        var success;
-        if(config.success) {
-            success = (config.scope) ?
-                OpenLayers.Function.bind(config.success, config.scope) :
-                config.success;
+        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;
+    }
+    
+};
 
-        // optional failure callback
-        var failure;
-        if(config.failure) {
-            failure = (config.scope) ?
-                OpenLayers.Function.bind(config.failure, config.scope) :
-                config.failure;
+
+/**
+ * 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/Popup/Anchored.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/Popup.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.Anchored
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Popup>
+ */
+OpenLayers.Popup.Anchored = 
+  OpenLayers.Class(OpenLayers.Popup, {
+
+    /** 
+     * Parameter: relativePosition
+     * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
+     */
+    relativePosition: null,
+    
+    /**
+     * APIProperty: keepInMap 
+     * {Boolean} If panMapIfOutOfView is false, and this property is true, 
+     *     contrain the popup such that it always fits in the available map
+     *     space. By default, this is set. If you are creating popups that are
+     *     near map edges and not allowing pannning, and especially if you have
+     *     a popup which has a fixedRelativePosition, setting this to false may
+     *     be a smart thing to do.
+     *   
+     *     For anchored popups, default is true, since subclasses will
+     *     usually want this functionality.
+     */
+    keepInMap: true,
+
+    /**
+     * Parameter: anchor
+     * {Object} Object to which we'll anchor the popup. Must expose a 
+     *     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
+     */
+    anchor: null,
+
+    /** 
+    * Constructor: OpenLayers.Popup.Anchored
+    * 
+    * Parameters:
+    * id - {String}
+    * lonlat - {<OpenLayers.LonLat>}
+    * contentSize - {<OpenLayers.Size>}
+    * contentHTML - {String}
+    * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> 
+    *     and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
+    * closeBox - {Boolean}
+    * closeBoxCallback - {Function} Function to be called on closeBox click.
+    */
+    initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
+                        closeBoxCallback) {
+        var newArguments = [
+            id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
+        ];
+        OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
+
+        this.anchor = (anchor != null) ? anchor 
+                                       : { size: new OpenLayers.Size(0,0),
+                                           offset: new OpenLayers.Pixel(0,0)};
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        this.anchor = null;
+        this.relativePosition = null;
+        
+        OpenLayers.Popup.prototype.destroy.apply(this, arguments);        
+    },
+
+    /**
+     * APIMethod: show
+     * Overridden from Popup since user might hide popup and then show() it 
+     *     in a new location (meaning we might want to update the relative
+     *     position on the show)
+     */
+    show: function() {
+        this.updatePosition();
+        OpenLayers.Popup.prototype.show.apply(this, arguments);
+    },
+
+    /**
+     * Method: moveTo
+     * Since the popup is moving to a new px, it might need also to be moved
+     *     relative to where the marker is. We first calculate the new 
+     *     relativePosition, and then we calculate the new px where we will 
+     *     put the popup, based on the new relative position. 
+     * 
+     *     If the relativePosition has changed, we must also call 
+     *     updateRelativePosition() to make any visual changes to the popup 
+     *     which are associated with putting it in a new relativePosition.
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     */
+    moveTo: function(px) {
+        var oldRelativePosition = this.relativePosition;
+        this.relativePosition = this.calculateRelativePosition(px);
+        
+        var newPx = this.calculateNewPx(px);
+        
+        var newArguments = new Array(newPx);        
+        OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
+        
+        //if this move has caused the popup to change its relative position, 
+        // we need to make the appropriate cosmetic changes.
+        if (this.relativePosition != oldRelativePosition) {
+            this.updateRelativePosition();
         }
-         
-        request.onreadystatechange = function() {
-            if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
-                complete(request);
-                if(success && (!request.status ||
-                   (request.status >= 200 && request.status < 300))) {
-                    success(request);
-                }
-                if(failure && (request.status &&
-                   (request.status < 200 || request.status >= 300))) {
-                    failure(request);
-                }
+    },
+
+    /**
+     * APIMethod: setSize
+     * 
+     * Parameters:
+     * contentSize - {<OpenLayers.Size>} the new size for the popup's 
+     *     contents div (in pixels).
+     */
+    setSize:function(contentSize) { 
+        OpenLayers.Popup.prototype.setSize.apply(this, arguments);
+
+        if ((this.lonlat) && (this.map)) {
+            var px = this.map.getLayerPxFromLonLat(this.lonlat);
+            this.moveTo(px);
+        }
+    },  
+    
+    /** 
+     * Method: calculateRelativePosition
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
+     *     should be placed.
+     */
+    calculateRelativePosition:function(px) {
+        var lonlat = this.map.getLonLatFromLayerPx(px);        
+        
+        var extent = this.map.getExtent();
+        var quadrant = extent.determineQuadrant(lonlat);
+        
+        return OpenLayers.Bounds.oppositeQuadrant(quadrant);
+    }, 
+
+    /**
+     * Method: updateRelativePosition
+     * The popup has been moved to a new relative location, so we may want to 
+     *     make some cosmetic adjustments to it. 
+     * 
+     *     Note that in the classic Anchored popup, there is nothing to do 
+     *     here, since the popup looks exactly the same in all four positions.
+     *     Subclasses such as the AnchoredBubble and Framed, however, will 
+     *     want to do something special here.
+     */
+    updateRelativePosition: function() {
+        //to be overridden by subclasses
+    },
+
+    /** 
+     * Method: calculateNewPx
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
+     *     relative to the passed-in px.
+     */
+    calculateNewPx:function(px) {
+        var newPx = px.offset(this.anchor.offset);
+        
+        //use contentSize if size is not already set
+        var size = this.size || this.contentSize;
+
+        var top = (this.relativePosition.charAt(0) == 't');
+        newPx.y += (top) ? -size.h : this.anchor.size.h;
+        
+        var left = (this.relativePosition.charAt(1) == 'l');
+        newPx.x += (left) ? -size.w : this.anchor.size.w;
+
+        return newPx;   
+    },
+
+    CLASS_NAME: "OpenLayers.Popup.Anchored"
+});
+/* ======================================================================
+    OpenLayers/Renderer/Canvas.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/Renderer.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.Canvas 
+ * A renderer based on the 2D 'canvas' drawing element.element
+ * 
+ * Inherits:
+ *  - <OpenLayers.Renderer>
+ */
+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
+
+    /**
+     * Property: canvas
+     * {Canvas} The canvas context object.
+     */
+    canvas: null, 
+    
+    /**
+     * Property: features
+     * {Object} Internal object of feature/style pairs for use in redrawing the layer.
+     */
+    features: null, 
+   
+    /**
+     * Property: geometryMap
+     * {Object} Geometry -> Feature lookup table. Used by eraseGeometry to
+     *     lookup features to remove from our internal table (this.features)
+     *     when erasing geoms.
+     */
+    geometryMap: null,
+ 
+    /**
+     * Constructor: OpenLayers.Renderer.Canvas
+     *
+     * Parameters:
+     * containerID - {<String>} 
+     */
+    initialize: function(containerID) {
+        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
+        this.root = document.createElement("canvas");
+        this.container.appendChild(this.root);
+        this.canvas = this.root.getContext("2d");
+        this.features = {};
+        this.geometryMap = {};
+    },
+    
+    /** 
+     * Method: eraseGeometry
+     * Erase a geometry from the renderer. Because the Canvas renderer has
+     *     'memory' of the features that it has drawn, we have to remove the
+     *     feature so it doesn't redraw.   
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    eraseGeometry: function(geometry) {
+        this.eraseFeatures(this.features[this.geometryMap[geometry.id]][0]);
+    },
+
+    /**
+     * APIMethod: supported
+     * 
+     * Returns:
+     * {Boolean} Whether or not the browser supports the renderer class
+     */
+    supported: function() {
+        var canvas = document.createElement("canvas");
+        return !!canvas.getContext;
+    },    
+    
+    /**
+     * Method: setExtent
+     * Set the visible part of the layer.
+     *
+     * Resolution has probably changed, so we nullify the resolution 
+     * cache (this.resolution), then redraw. 
+     *
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>} 
+     */
+    setExtent: function(extent) {
+        this.extent = extent.clone();
+        this.resolution = null;
+        this.redraw();
+    },
+    
+    /**
+     * Method: setSize
+     * Sets the size of the drawing surface.
+     *
+     * Once the size is updated, redraw the canvas.
+     *
+     * Parameters:
+     * size - {<OpenLayers.Size>} 
+     */
+    setSize: function(size) {
+        this.size = size.clone();
+        this.root.style.width = size.w + "px";
+        this.root.style.height = size.h + "px";
+        this.root.width = size.w;
+        this.root.height = size.h;
+        this.resolution = null;
+    },
+    
+    /**
+     * Method: drawFeature
+     * Draw the feature. Stores the feature in the features list,
+     * then redraws the layer. 
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     * style - {<Object>} 
+     */
+    drawFeature: function(feature, style) {
+        if(style == null) {
+            style = feature.style;
+        }
+        style = OpenLayers.Util.extend({
+          'fillColor': '#000000',
+          'strokeColor': '#000000',
+          'strokeWidth': 2,
+          'fillOpacity': 1,
+          'strokeOpacity': 1
+        }, style);  
+        this.features[feature.id] = [feature, style]; 
+        this.geometryMap[feature.geometry.id] = feature.id; 
+        this.redraw();
+    },
+
+
+    /** 
+     * Method: drawGeometry
+     * Used when looping (in redraw) over the features; draws
+     * the canvas. 
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * style - {Object} 
+     * featureId - {<String>} 
+     */
+    drawGeometry: function(geometry, style, featureId, isInner) {
+        var className = geometry.CLASS_NAME;
+        if ((className == "OpenLayers.Geometry.Collection") ||
+            (className == "OpenLayers.Geometry.MultiPoint") ||
+            (className == "OpenLayers.Geometry.MultiLineString") ||
+            (className == "OpenLayers.Geometry.MultiPolygon")) {
+            for (var i = 0; i < geometry.components.length; i++) {
+                this.drawGeometry(geometry.components[i], style, featureId, true);
             }
+            if (style.label !== undefined) {
+                this.drawText(geometry.getCentroid(), style);
+            }
+            return;
         };
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                this.drawPoint(geometry, style);
+                break;
+            case "OpenLayers.Geometry.LineString":
+                this.drawLineString(geometry, style);
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                this.drawLinearRing(geometry, style);
+                break;
+            case "OpenLayers.Geometry.Polygon":
+                this.drawPolygon(geometry, style);
+                break;
+            default:
+                break;
+        }
+        if ((style.label !== undefined) && !isInner) {
+            this.drawText(geometry.getCentroid(), style);
+        }
+    },
+
+    /**
+     * Method: drawExternalGraphic
+     * Called to draw External graphics. 
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawExternalGraphic: function(pt, style) {
+       var img = new Image();
+       img.src = style.externalGraphic;
+       
+       if(style.graphicTitle) {
+           img.title=style.graphicTitle;           
+       }
+
+       var width = style.graphicWidth || style.graphicHeight;
+       var height = style.graphicHeight || style.graphicWidth;
+       width = width ? width : style.pointRadius*2;
+       height = height ? height : style.pointRadius*2;
+       var xOffset = (style.graphicXOffset != undefined) ?
+           style.graphicXOffset : -(0.5 * width);
+       var yOffset = (style.graphicYOffset != undefined) ?
+           style.graphicYOffset : -(0.5 * height);
+       var opacity = style.graphicOpacity || style.fillOpacity;
+       
+       var context = { img: img, 
+                       x: (pt[0]+xOffset), 
+                       y: (pt[1]+yOffset), 
+                       width: width, 
+                       height: height, 
+                       canvas: this.canvas };
+
+       img.onload = OpenLayers.Function.bind( function() {
+           this.canvas.drawImage(this.img, this.x, 
+                                 this.y, this.width, this.height);
+       }, context);   
+    },
+
+    /**
+     * Method: setCanvasStyle
+     * Prepare the canvas for drawing by setting various global settings.
+     *
+     * Parameters:
+     * type - {String} one of 'stroke', 'fill', or 'reset'
+     * style - {Object} Symbolizer hash
+     */
+    setCanvasStyle: function(type, style) {
+        if (type == "fill") {     
+            this.canvas.globalAlpha = style['fillOpacity'];
+            this.canvas.fillStyle = style['fillColor'];
+        } else if (type == "stroke") {  
+            this.canvas.globalAlpha = style['strokeOpacity'];
+            this.canvas.strokeStyle = style['strokeColor'];
+            this.canvas.lineWidth = style['strokeWidth'];
+        } else {
+            this.canvas.globalAlpha = 0;
+            this.canvas.lineWidth = 1;
+        }
+    },
+
+    /**
+     * Method: drawPoint
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawPoint: function(geometry, style) {
+        var pt = this.getLocalXY(geometry);
         
-        // send request (optionally with data) and return
-        request.send(config.data);
-        return request;
+        if (style.externalGraphic) {
+            this.drawExternalGraphic(pt, style);
+        } else {
+            this.setCanvasStyle("fill", style);
+            this.canvas.beginPath();
+            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
+            this.canvas.fill();
+            
+            this.setCanvasStyle("stroke", style);
+            this.canvas.beginPath();
+            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
+            this.canvas.stroke();
+            this.setCanvasStyle("reset");
+        }
     },
+
+    /**
+     * Method: drawLineString
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawLineString: function(geometry, style) {
+        this.setCanvasStyle("stroke", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.stroke();
+        this.setCanvasStyle("reset");
+    },    
     
     /**
-     * APIMethod: GET
-     * Send an HTTP GET request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to GET.
+     * Method: drawLinearRing
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawLinearRing: function(geometry, style) {
+        this.setCanvasStyle("fill", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length - 1 ; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.fill();
+        
+        var oldWidth = this.canvas.lineWidth; 
+        this.setCanvasStyle("stroke", style);
+        this.canvas.beginPath();
+        var start = this.getLocalXY(geometry.components[0]);
+        this.canvas.moveTo(start[0], start[1]);
+        for(var i = 1; i < geometry.components.length; i++) {
+            var pt = this.getLocalXY(geometry.components[i]);
+            this.canvas.lineTo(pt[0], pt[1]);
+        }
+        this.canvas.stroke();
+        this.setCanvasStyle("reset");
+    },    
+    
+    /**
+     * Method: drawPolygon
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawPolygon: function(geometry, style) {
+        this.drawLinearRing(geometry.components[0], style);
+        for (var i = 1; i < geometry.components.length; i++) {
+            this.drawLinearRing(geometry.components[i], {
+                fillOpacity: 0, 
+                strokeWidth: 0, 
+                strokeOpacity: 0, 
+                strokeColor: '#000000', 
+                fillColor: '#000000'}
+            ); // inner rings are 'empty'  
+        }
+    },
+    
+    /**
+     * Method: drawText
+     * This method is only called by the renderer itself.
      *
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.
-     *     This object is modified and should not be reused.
+     * location - {<OpenLayers.Point>}
+     * style    - {Object}
+     */
+    drawText: function(location, style) {
+        var pt = this.getLocalXY(location);
+        
+        this.setCanvasStyle("reset");
+        this.canvas.fillStyle = style.fontColor;
+        this.canvas.globalAlpha = 1;
+        var fontStyle = style.fontWeight + " " + style.fontSize + " " + style.fontFamily;
+        var labelAlign = style.labelAlign || "middle";
+        if (this.canvas.fillText) {
+            // HTML5
+            this.canvas.font = fontStyle;
+            this.canvas.textAlign = labelAlign;
+            this.canvas.fillText(style.label, pt[0], pt[1]);
+        } else if (this.canvas.mozDrawText) {
+            // Mozilla pre-Gecko1.9.1 (<FF3.1)
+            this.canvas.mozTextStyle = fontStyle;
+            // No built-in text alignment, so we measure and adjust the position
+            var len = this.canvas.mozMeasureText(style.label);
+            switch(labelAlign) {
+                case "left":
+                    break;
+                case "right":
+                    pt[0] -= len;
+                    break;
+                case "middle":
+                default:
+                    pt[0] -= len / 2;
+            }
+            this.canvas.translate(pt[0], pt[1]);
+            
+            this.canvas.mozDrawText(style.label);
+            this.canvas.translate(-1*pt[0], -1*pt[1]);
+        }
+        this.setCanvasStyle("reset");
+    },
+
+    /**
+     * Method: getLocalXY
+     * transform geographic xy into pixel xy
+     *
+     * Parameters: 
+     * point - {<OpenLayers.Geometry.Point>}
+     */
+    getLocalXY: function(point) {
+        var resolution = this.getResolution();
+        var extent = this.extent;
+        var x = (point.x / resolution + (-extent.left / resolution));
+        var y = ((extent.top / resolution) - point.y / resolution);
+        return [x, y];
+    },
+        
+    /**
+     * Method: clear
+     * Clear all vectors from the renderer.
+     * virtual function.
+     */    
+    clear: function() {
+        this.canvas.clearRect(0, 0, this.root.width, this.root.height);
+    },
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * Returns a feature id from an event on the renderer.  
      * 
+     * Parameters:
+     * evt - {<OpenLayers.Event>} 
+     *
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {String} A feature id or null.
      */
-    GET: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "GET"});
-        return OpenLayers.Request.issue(config);
+    getFeatureIdFromEvent: function(evt) {
+        var loc = this.map.getLonLatFromPixel(evt.xy);
+        var resolution = this.getResolution();
+        var bounds = new OpenLayers.Bounds(loc.lon - resolution * 5, 
+                                           loc.lat - resolution * 5, 
+                                           loc.lon + resolution * 5, 
+                                           loc.lat + resolution * 5);
+        var geom = bounds.toGeometry();
+        for (var feat in this.features) {
+            if (!this.features.hasOwnProperty(feat)) { continue; }
+            if (this.features[feat][0].geometry.intersects(geom)) {
+                return feat;
+            }
+        }   
+        return null;
     },
     
     /**
-     * APIMethod: POST
-     * Send a POST request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to POST and "Content-Type" header set to "application/xml".
+     * Method: eraseFeatures 
+     * This is called by the layer to erase features; removes the feature from
+     *     the list, then redraws the layer.
+     * 
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    eraseFeatures: function(features) {
+        if(!(features instanceof Array)) {
+            features = [features];
+        }
+        for(var i=0; i<features.length; ++i) {
+            delete this.features[features[i].id];
+        }
+        this.redraw();
+    },
+
+    /**
+     * Method: redraw
+     * The real 'meat' of the function: any time things have changed,
+     *     redraw() can be called to loop over all the data and (you guessed
+     *     it) redraw it.  Unlike Elements-based Renderers, we can't interact
+     *     with things once they're drawn, to remove them, for example, so
+     *     instead we have to just clear everything and draw from scratch.
+     */
+    redraw: function() {
+        if (!this.locked) {
+            this.clear();
+            for (var id in this.features) {
+                if (!this.features.hasOwnProperty(id)) { continue; }
+                if (!this.features[id][0].geometry) { continue; }
+                this.drawGeometry(this.features[id][0].geometry, this.features[id][1]);
+            }
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.Canvas"
+});
+/* ======================================================================
+    OpenLayers/Renderer/Elements.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/Renderer.js
+ */
+
+/**
+ * Class: OpenLayers.ElementsIndexer
+ * This class takes care of figuring out which order elements should be
+ *     placed in the DOM based on given indexing methods. 
+ */
+OpenLayers.ElementsIndexer = OpenLayers.Class({
+   
+    /**
+     * Property: maxZIndex
+     * {Integer} This is the largest-most z-index value for a node
+     *     contained within the indexer.
+     */
+    maxZIndex: null,
+    
+    /**
+     * Property: order
+     * {Array<String>} This is an array of node id's stored in the
+     *     order that they should show up on screen. Id's higher up in the
+     *     array (higher array index) represent nodes with higher z-indeces.
+     */
+    order: null, 
+    
+    /**
+     * Property: indices
+     * {Object} This is a hash that maps node ids to their z-index value
+     *     stored in the indexer. This is done to make finding a nodes z-index 
+     *     value O(1).
+     */
+    indices: null,
+    
+    /**
+     * Property: compare
+     * {Function} This is the function used to determine placement of
+     *     of a new node within the indexer. If null, this defaults to to
+     *     the Z_ORDER_DRAWING_ORDER comparison method.
+     */
+    compare: null,
+   
+    /**
+     * APIMethod: initialize
+     * Create a new indexer with 
+     * 
+     * Parameters:
+     * yOrdering - {Boolean} Whether to use y-ordering.
+     */
+    initialize: function(yOrdering) {
+
+        this.compare = yOrdering ? 
+            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
+            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
+            
+        this.order = [];
+        this.indices = {};
+        this.maxZIndex = 0;
+    },
+    
+    /**
+     * APIMethod: insert
+     * Insert a new node into the indexer. In order to find the correct 
+     *     positioning for the node to be inserted, this method uses a binary 
+     *     search. This makes inserting O(log(n)). 
+     * 
+     * Parameters:
+     * newNode - {DOMElement} The new node to be inserted.
+     * 
+     * Returns
+     * {DOMElement} the node before which we should insert our newNode, or
+     *     null if newNode can just be appended.
+     */
+    insert: function(newNode) {
+        // If the node is known to the indexer, remove it so we can
+        // recalculate where it should go.
+        if (this.exists(newNode)) {
+            this.remove(newNode);
+        }
+        
+        var nodeId = newNode.id;
+        
+        this.determineZIndex(newNode);       
+
+        var leftIndex = -1;
+        var rightIndex = this.order.length;
+        var middle;
+
+        while (rightIndex - leftIndex > 1) {
+            middle = parseInt((leftIndex + rightIndex) / 2);
+            
+            var placement = this.compare(this, newNode,
+                OpenLayers.Util.getElement(this.order[middle]));
+            
+            if (placement > 0) {
+                leftIndex = middle;
+            } else {
+                rightIndex = middle;
+            } 
+        }
+        
+        this.order.splice(rightIndex, 0, nodeId);
+        this.indices[nodeId] = this.getZIndex(newNode);
+        
+        // If the new node should be before another in the index
+        // order, return the node before which we have to insert the new one;
+        // else, return null to indicate that the new node can be appended.
+        var nextIndex = rightIndex + 1;
+        return nextIndex < this.order.length ?
+                OpenLayers.Util.getElement(this.order[nextIndex]) : null;
+    },
+    
+    /**
+     * APIMethod: remove
+     * 
+     * Parameters:
+     * node - {DOMElement} The node to be removed.
+     */
+    remove: function(node) {
+        var nodeId = node.id;
+        var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
+        if (arrayIndex >= 0) {
+            // Remove it from the order array, as well as deleting the node
+            // from the indeces hash.
+            this.order.splice(arrayIndex, 1);
+            delete this.indices[nodeId];
+            
+            // Reset the maxium z-index based on the last item in the 
+            // order array.
+            if (this.order.length > 0) {
+                var lastId = this.order[this.order.length - 1];
+                this.maxZIndex = this.indices[lastId];
+            } else {
+                this.maxZIndex = 0;
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: clear
+     */
+    clear: function() {
+        this.order = [];
+        this.indices = {};
+        this.maxZIndex = 0;
+    },
+    
+    /**
+     * APIMethod: exists
      *
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.  The
-     *     default "Content-Type" header will be set to "application-xml" if
-     *     none is provided.  This object is modified and should not be reused.
+     * node- {DOMElement} The node to test for existence.
+     *
+     * Returns:
+     * {Boolean} Whether or not the node exists in the indexer?
+     */
+    exists: function(node) {
+        return (this.indices[node.id] != null);
+    },
+
+    /**
+     * APIMethod: getZIndex
+     * Get the z-index value for the current node from the node data itself.
      * 
+     * Parameters:
+     * node - {DOMElement} The node whose z-index to get.
+     * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {Integer} The z-index value for the specified node (from the node 
+     *     data itself).
      */
-    POST: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "POST"});
-        // set content type to application/xml if it isn't already set
-        config.headers = config.headers ? config.headers : {};
-        if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
-            config.headers["Content-Type"] = "application/xml";
+    getZIndex: function(node) {
+        return node._style.graphicZIndex;  
+    },
+    
+    /**
+     * Method: determineZIndex
+     * Determine the z-index for the current node if there isn't one, 
+     *     and set the maximum value if we've found a new maximum.
+     * 
+     * Parameters:
+     * node - {DOMElement} 
+     */
+    determineZIndex: function(node) {
+        var zIndex = node._style.graphicZIndex;
+        
+        // Everything must have a zIndex. If none is specified,
+        // this means the user *must* (hint: assumption) want this
+        // node to succomb to drawing order. To enforce drawing order
+        // over all indexing methods, we'll create a new z-index that's
+        // greater than any currently in the indexer.
+        if (zIndex == null) {
+            zIndex = this.maxZIndex;
+            node._style.graphicZIndex = zIndex; 
+        } else if (zIndex > this.maxZIndex) {
+            this.maxZIndex = zIndex;
         }
-        return OpenLayers.Request.issue(config);
     },
     
+    CLASS_NAME: "OpenLayers.ElementsIndexer"
+});
+
+/**
+ * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
+ * These are the compare methods for figuring out where a new node should be 
+ *     placed within the indexer. These methods are very similar to general 
+ *     sorting methods in that they return -1, 0, and 1 to specify the 
+ *     direction in which new nodes fall in the ordering.
+ */
+OpenLayers.ElementsIndexer.IndexingMethods = {
+    
     /**
-     * APIMethod: PUT
-     * Send an HTTP PUT request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to PUT and "Content-Type" header set to "application/xml".
-     *
+     * Method: Z_ORDER
+     * This compare method is used by other comparison methods.
+     *     It can be used individually for ordering, but is not recommended,
+     *     because it doesn't subscribe to drawing order.
+     * 
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.  The
-     *     default "Content-Type" header will be set to "application-xml" if
-     *     none is provided.  This object is modified and should not be reused.
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
      * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {Integer}
      */
-    PUT: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "PUT"});
-        // set content type to application/xml if it isn't already set
-        config.headers = config.headers ? config.headers : {};
-        if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
-            config.headers["Content-Type"] = "application/xml";
+    Z_ORDER: function(indexer, newNode, nextNode) {
+        var newZIndex = indexer.getZIndex(newNode);
+
+        var returnVal = 0;
+        if (nextNode) {
+            var nextZIndex = indexer.getZIndex(nextNode);
+            returnVal = newZIndex - nextZIndex; 
         }
-        return OpenLayers.Request.issue(config);
+        
+        return returnVal;
     },
+
+    /**
+     * APIMethod: Z_ORDER_DRAWING_ORDER
+     * This method orders nodes by their z-index, but does so in a way
+     *     that, if there are other nodes with the same z-index, the newest 
+     *     drawn will be the front most within that z-index. This is the 
+     *     default indexing method.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
+        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
+            indexer, 
+            newNode, 
+            nextNode
+        );
+        
+        // Make Z_ORDER subscribe to drawing order by pushing it above
+        // all of the other nodes with the same z-index.
+        if (nextNode && returnVal == 0) {
+            returnVal = 1;
+        }
+        
+        return returnVal;
+    },
+
+    /**
+     * APIMethod: Z_ORDER_Y_ORDER
+     * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
+     *     best describes which ordering methods have precedence (though, the 
+     *     name would be too long). This method orders nodes by their z-index, 
+     *     but does so in a way that, if there are other nodes with the same 
+     *     z-index, the nodes with the lower y position will be "closer" than 
+     *     those with a higher y position. If two nodes have the exact same y 
+     *     position, however, then this method will revert to using drawing  
+     *     order to decide placement.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
+        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
+            indexer, 
+            newNode, 
+            nextNode
+        );
+        
+        if (nextNode && returnVal == 0) {
+            var newLat = newNode._geometry.getBounds().bottom;
+            var nextLat = nextNode._geometry.getBounds().bottom;
+            
+            var result = nextLat - newLat;
+            returnVal = (result ==0) ? 1 : result;
+        }
+        
+        return returnVal;       
+    }
+};
+
+/**
+ * Class: OpenLayers.Renderer.Elements
+ * This is another virtual class in that it should never be instantiated by 
+ *  itself as a Renderer. It exists because there is *tons* of shared 
+ *  functionality between different vector libraries which use nodes/elements
+ *  as a base for rendering vectors. 
+ * 
+ * The highlevel bits of code that are implemented here are the adding and 
+ *  removing of geometries, which is essentially the same for any 
+ *  element-based renderer. The details of creating each node and drawing the
+ *  paths are of course different, but the machinery is the same. 
+ * 
+ * Inherits:
+ *  - <OpenLayers.Renderer>
+ */
+OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
+
+    /**
+     * Property: rendererRoot
+     * {DOMElement}
+     */
+    rendererRoot: null,
     
     /**
-     * APIMethod: DELETE
-     * Send an HTTP DELETE request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to DELETE.
+     * Property: xmlns
+     * {String}
+     */    
+    xmlns: null,
+    
+    /**
+     * Property: Indexer
+     * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer 
+     *     created upon initialization if the zIndexing or yOrdering options
+     *     passed to this renderer's constructor are set to true.
+     */
+    indexer: null, 
+    
+    /**
+     * Constant: BACKGROUND_ID_SUFFIX
+     * {String}
+     */
+    BACKGROUND_ID_SUFFIX: "_background",
+    
+    /**
+     * Constant: LABEL_ID_SUFFIX
+     * {String}
+     */
+    LABEL_ID_SUFFIX: "_label",
+    
+    /**
+     * Property: minimumSymbolizer
+     * {Object}
+     */
+    minimumSymbolizer: {
+        strokeLinecap: "round",
+        strokeOpacity: 1,
+        strokeDashstyle: "solid",
+        fillOpacity: 1,
+        pointRadius: 0
+    },
+    
+    /**
+     * Constructor: OpenLayers.Renderer.Elements
+     * 
+     * Parameters:
+     * containerID - {String}
+     * options - {Object} options for this renderer. Supported options are:
+     *     * yOrdering - {Boolean} Whether to use y-ordering
+     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
+     *         if yOrdering is set to true.
+     */
+    initialize: function(containerID, options) {
+        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
+
+        this.rendererRoot = this.createRenderRoot();
+        this.root = this.createRoot();
+        
+        this.rendererRoot.appendChild(this.root);
+        this.container.appendChild(this.rendererRoot);
+        
+        if(options && (options.zIndexing || options.yOrdering)) {
+            this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
+        }
+    },
+    
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+
+        this.clear(); 
+
+        this.rendererRoot = null;
+        this.root = null;
+        this.xmlns = null;
+
+        OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * Method: clear
+     * Remove all the elements from the root
+     */    
+    clear: function() {
+        if (this.root) {
+            while (this.root.childNodes.length > 0) {
+                this.root.removeChild(this.root.firstChild);
+            }
+        }
+        if (this.indexer) {
+            this.indexer.clear();
+        }
+    },
+
+    /** 
+     * Method: getNodeType
+     * This function is in charge of asking the specific renderer which type
+     *     of node to create for the given geometry and style. All geometries
+     *     in an Elements-based renderer consist of one node and some
+     *     attributes. We have the nodeFactory() function which creates a node
+     *     for us, but it takes a 'type' as input, and that is precisely what
+     *     this function tells us.  
+     *  
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
+     */
+    getNodeType: function(geometry, style) { },
+
+    /** 
+     * Method: drawGeometry 
+     * Draw the geometry, creating new nodes, setting paths, setting style,
+     *     setting featureId on the node.  This method should only be called
+     *     by the renderer itself.
      *
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.
-     *     This object is modified and should not be reused.
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
      * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {Boolean} true if the geometry has been drawn completely; null if
+     *     incomplete; false otherwise
      */
-    DELETE: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "DELETE"});
-        return OpenLayers.Request.issue(config);
+    drawGeometry: function(geometry, style, featureId, isInner) {
+        var className = geometry.CLASS_NAME;
+        var rendered = true;
+        if ((className == "OpenLayers.Geometry.Collection") ||
+            (className == "OpenLayers.Geometry.MultiPoint") ||
+            (className == "OpenLayers.Geometry.MultiLineString") ||
+            (className == "OpenLayers.Geometry.MultiPolygon")) {
+            for (var i = 0, len=geometry.components.length; i<len; i++) {
+                rendered = this.drawGeometry(
+                    geometry.components[i], style, featureId, true) && rendered;
+            }
+
+            if ((style.label !== undefined) && !isInner) {
+                if (rendered) {
+                    this.drawText(featureId, style, geometry.getCentroid());
+                } else {
+                    this.root.removeChild(document.getElementById(
+                    featureId + this.LABEL_ID_SUFFIX));
+                }
+            }
+            return rendered;
+        };
+
+        rendered = false;
+        if (style.display != "none") {
+            if (style.backgroundGraphic) {
+                this.redrawBackgroundNode(geometry.id, geometry, style,
+                    featureId);
+            }
+            rendered = this.redrawNode(geometry.id, geometry, style,
+                featureId, isInner);
+        }
+        if (rendered == false) {
+            var node = document.getElementById(geometry.id);
+            if (node) {
+                if (node._style.backgroundGraphic) {
+                    node.parentNode.removeChild(document.getElementById(
+                        geometry.id + this.BACKGROUND_ID_SUFFIX));
+                }
+                if (node._style.label !== undefined) {
+                    var labelId = featureId + this.LABEL_ID_SUFFIX;
+                    var lElem = OpenLayers.Util.getElement(labelId);
+                    if (lElem && lElem.parentNode) {
+                        lElem.parentNode.removeChild(lElem);
+                    }
+                }
+                node.parentNode.removeChild(node);
+            }
+        }
+        return rendered;
     },
-  
+    
     /**
-     * APIMethod: HEAD
-     * Send an HTTP HEAD request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to HEAD.
+     * Method: redrawNode
+     * 
+     * Parameters:
+     * id - {String}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * 
+     * Returns:
+     * {Boolean} true if the complete geometry could be drawn, null if parts of
+     *     the geometry could not be drawn, false otherwise
+     */
+    redrawNode: function(id, geometry, style, featureId, isInner) {
+        // Get the node if it's already on the map.
+        var node = this.nodeFactory(id, this.getNodeType(geometry, style));
+        
+        // Set the data for the node, then draw it.
+        node._featureId = featureId;
+        node._geometry = geometry;
+        node._geometryClass = geometry.CLASS_NAME;
+        node._style = style;
+
+        var drawResult = this.drawGeometryNode(node, geometry, style);
+        
+        if(drawResult === false) {
+            return false;
+        }
+         
+        node = drawResult.node;
+        
+        // Insert the node into the indexer so it can show us where to
+        // place it. Note that this operation is O(log(n)). If there's a
+        // performance problem (when dragging, for instance) this is
+        // likely where it would be.
+        if (this.indexer) {
+            var insert = this.indexer.insert(node);
+            if (insert) {
+                this.root.insertBefore(node, insert);
+            } else {
+                this.root.appendChild(node);
+            }
+        } else {
+            // if there's no indexer, simply append the node to root,
+            // but only if the node is a new one
+            if (node.parentNode !== this.root){ 
+                this.root.appendChild(node);
+            }
+        }
+        
+        if ((style.label !== undefined) && !isInner) {
+            this.drawText(featureId, style, geometry.getCentroid());
+        }
+        
+        this.postDraw(node);
+        
+        return drawResult.complete;
+    },
+    
+    /**
+     * Method: redrawBackgroundNode
+     * Redraws the node using special 'background' style properties. Basically
+     *     just calls redrawNode(), but instead of directly using the 
+     *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and 
+     *     'graphicZIndex' properties directly from the specified 'style' 
+     *     parameter, we create a new style object and set those properties 
+     *     from the corresponding 'background'-prefixed properties from 
+     *     specified 'style' parameter.
+     * 
+     * Parameters:
+     * id - {String}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * featureId - {String}
+     * 
+     * Returns:
+     * {Boolean} true if the complete geometry could be drawn, null if parts of
+     *     the geometry could not be drawn, false otherwise
+     */
+    redrawBackgroundNode: function(id, geometry, style, featureId) {
+        var backgroundStyle = OpenLayers.Util.extend({}, style);
+        
+        // Set regular style attributes to apply to the background styles.
+        backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
+        backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
+        backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
+        backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
+        backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
+        backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
+        
+        // Erase background styles.
+        backgroundStyle.backgroundGraphic = null;
+        backgroundStyle.backgroundXOffset = null;
+        backgroundStyle.backgroundYOffset = null;
+        backgroundStyle.backgroundGraphicZIndex = null;
+        
+        return this.redrawNode(
+            id + this.BACKGROUND_ID_SUFFIX, 
+            geometry, 
+            backgroundStyle, 
+            null, true
+        );
+    },
+
+    /**
+     * Method: drawGeometryNode
+     * Given a node, draw a geometry on the specified layer.
+     *     node and geometry are required arguments, style is optional.
+     *     This method is only called by the render itself.
      *
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.
-     *     This object is modified and should not be reused.
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
      * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {Object} a hash with properties "node" (the drawn node) and "complete"
+     *     (null if parts of the geometry could not be drawn, false if nothing
+     *     could be drawn)
      */
-    HEAD: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "HEAD"});
-        return OpenLayers.Request.issue(config);
+    drawGeometryNode: function(node, geometry, style) {
+        style = style || node._style;
+        OpenLayers.Util.applyDefaults(style, this.minimumSymbolizer);
+
+        var options = {
+            'isFilled': true,
+            'isStroked': !!style.strokeWidth
+        };
+        var drawn;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                drawn = this.drawPoint(node, geometry);
+                break;
+            case "OpenLayers.Geometry.LineString":
+                options.isFilled = false;
+                drawn = this.drawLineString(node, geometry);
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                drawn = this.drawLinearRing(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Polygon":
+                drawn = this.drawPolygon(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Surface":
+                drawn = this.drawSurface(node, geometry);
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                drawn = this.drawRectangle(node, geometry);
+                break;
+            default:
+                break;
+        }
+
+        node._style = style; 
+        node._options = options; 
+
+        //set style
+        //TBD simplify this
+        if (drawn != false) {
+            return {
+                node: this.setStyle(node, style, options, geometry),
+                complete: drawn
+            };
+        } else {
+            return false;
+        }
     },
     
     /**
-     * APIMethod: OPTIONS
-     * Send an HTTP OPTIONS request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to OPTIONS.
+     * Method: postDraw
+     * Things that have do be done after the geometry node is appended
+     *     to its parent node. To be overridden by subclasses.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     */
+    postDraw: function(node) {},
+    
+    /**
+     * Method: drawPoint
+     * Virtual function for drawing Point Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the point
+     */ 
+    drawPoint: function(node, geometry) {},
+
+    /**
+     * Method: drawLineString
+     * Virtual function for drawing LineString Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components of
+     *     the linestring, or false if nothing could be drawn
+     */ 
+    drawLineString: function(node, geometry) {},
+
+    /**
+     * Method: drawLinearRing
+     * Virtual function for drawing LinearRing Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the linear ring, or false if nothing could be drawn
+     */ 
+    drawLinearRing: function(node, geometry) {},
+
+    /**
+     * Method: drawPolygon
+     * Virtual function for drawing Polygon Geometry. 
+     *    Should be implemented by subclasses.
+     *    This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the polygon, or false if nothing could be drawn
+     */ 
+    drawPolygon: function(node, geometry) {},
+
+    /**
+     * Method: drawRectangle
+     * Virtual function for drawing Rectangle Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the rectangle
+     */ 
+    drawRectangle: function(node, geometry) {},
+
+    /**
+     * Method: drawCircle
+     * Virtual function for drawing Circle Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the circle
+     */ 
+    drawCircle: function(node, geometry) {},
+
+    /**
+     * Method: drawSurface
+     * Virtual function for drawing Surface Geometry. 
+     *     Should be implemented by subclasses.
+     *     This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the surface
+     */ 
+    drawSurface: function(node, geometry) {},
+
+    /**
+     * Method: drawText
+     * Virtual function for drawing text labels.
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String}
+     * style -
+     * location - {<OpenLayers.Geometry.Point>}
+     */
+    drawText: function(featureId, style, location) {},
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * 
+     * Parameters:
+     * evt - {Object} An <OpenLayers.Event> object
      *
+     * Returns:
+     * {<OpenLayers.Geometry>} A geometry from an event that 
+     *     happened on a layer.
+     */
+    getFeatureIdFromEvent: function(evt) {
+        var target = evt.target;
+        var useElement = target && target.correspondingUseElement;
+        var node = useElement ? useElement : (target || evt.srcElement);
+        var featureId = node._featureId;
+        return featureId;
+    },
+
+    /** 
+     * Method: eraseGeometry
+     * Erase a geometry from the renderer. In the case of a multi-geometry, 
+     *     we cycle through and recurse on ourselves. Otherwise, we look for a 
+     *     node with the geometry.id, destroy its geometry, and remove it from
+     *     the DOM.
+     * 
      * Parameters:
-     * config - {Object} Object with properties for configuring the request.
-     *     See the <issue> method for documentation of allowed properties.
-     *     This object is modified and should not be reused.
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    eraseGeometry: function(geometry) {
+        if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
+            (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
+            for (var i=0, len=geometry.components.length; i<len; i++) {
+                this.eraseGeometry(geometry.components[i]);
+            }
+        } else {    
+            var element = OpenLayers.Util.getElement(geometry.id);
+            if (element && element.parentNode) {
+                if (element.geometry) {
+                    element.geometry.destroy();
+                    element.geometry = null;
+                }
+                element.parentNode.removeChild(element);
+
+                if (this.indexer) {
+                    this.indexer.remove(element);
+                }
+                
+                if (element._style.backgroundGraphic) {
+                    var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
+                    var bElem = OpenLayers.Util.getElement(backgroundId);
+                    if (bElem && bElem.parentNode) {
+                        // No need to destroy the geometry since the element and the background
+                        // node share the same geometry.
+                        bElem.parentNode.removeChild(bElem);
+                    }
+                }
+                if (element._style.label !== undefined) {
+                    var labelId = element._featureId + this.LABEL_ID_SUFFIX;
+                    var lElem = OpenLayers.Util.getElement(labelId);
+                    if (lElem && lElem.parentNode) {
+                        lElem.parentNode.removeChild(lElem);
+            }
+        }
+            }
+        }
+    },
+
+    /** 
+     * Method: nodeFactory
+     * Create new node of the specified type, with the (optional) specified id.
      * 
+     * If node already exists with same ID and a different type, we remove it
+     *     and then call ourselves again to recreate it.
+     * 
+     * Parameters:
+     * id - {String}
+     * type - {String} type Kind of node to draw.
+     * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {DOMElement} A new node of the given type and id.
      */
-    OPTIONS: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
-        return OpenLayers.Request.issue(config);
-    }
+    nodeFactory: function(id, type) {
+        var node = OpenLayers.Util.getElement(id);
+        if (node) {
+            if (!this.nodeTypeCompare(node, type)) {
+                node.parentNode.removeChild(node);
+                node = this.nodeFactory(id, type);
+            }
+        } else {
+            node = this.createNode(type, id);
+        }
+        return node;
+    },
+    
+    /** 
+     * Method: nodeTypeCompare
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * type - {String} Kind of node
+     * 
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     *     This function must be overridden by subclasses.
+     */
+    nodeTypeCompare: function(node, type) {},
+    
+    /** 
+     * Method: createNode
+     * 
+     * Parameters:
+     * type - {String} Kind of node to draw.
+     * id - {String} Id for node.
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id.
+     *     This function must be overridden by subclasses.
+     */
+    createNode: function(type, id) {},
 
+    /**
+     * Method: moveRoot
+     * moves this renderer's root to a different renderer.
+     * 
+     * Parameters:
+     * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
+     */
+    moveRoot: function(renderer) {
+        var root = this.root;
+        if(renderer.root.parentNode == this.rendererRoot) {
+            root = renderer.root;
+        }
+        root.parentNode.removeChild(root);
+        renderer.rendererRoot.appendChild(root);
+    },
+    
+    /**
+     * Method: getRenderLayerId
+     * Gets the layer that this renderer's output appears on. If moveRoot was
+     * used, this will be different from the id of the layer containing the
+     * features rendered by this renderer.
+     * 
+     * Returns:
+     * {String} the id of the output layer.
+     */
+    getRenderLayerId: function() {
+        return this.root.parentNode.parentNode.id;
+    },
+    
+    /**
+     * Method: isComplexSymbol
+     * Determines if a symbol cannot be rendered using drawCircle
+     * 
+     * Parameters:
+     * graphicName - {String}
+     * 
+     * Returns
+     * {Boolean} true if the symbol is complex, false if not
+     */
+    isComplexSymbol: function(graphicName) {
+        return (graphicName != "circle") && !!graphicName;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.Elements"
+});
+
+
+/**
+ * Constant: OpenLayers.Renderer.symbol
+ * Coordinate arrays for well known (named) symbols.
+ */
+OpenLayers.Renderer.symbol = {
+    "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
+            303,215, 231,161, 321,161, 350,75],
+    "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
+            4,0],
+    "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
+    "square": [0,0, 0,1, 1,1, 1,0, 0,0],
+    "triangle": [0,10, 10,10, 5,0, 0,10]
 };
 /* ======================================================================
     OpenLayers/Tween.js
@@ -6360,6 +7988,10 @@
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/Console.js
+ */
+
+/**
  * Namespace: OpenLayers.Tween
  */
 OpenLayers.Tween = OpenLayers.Class({
@@ -6688,7 +8320,12 @@
 
 /**
  * Class: OpenLayers.Control.ArgParser
- * 
+ * The ArgParser control adds location bar querystring parsing functionality 
+ * to an OpenLayers Map.
+ * When added to a Map control, on a page load/refresh, the Map will 
+ * automatically take the href string and parse it for lon, lat, zoom, and 
+ * layers information. 
+ *
  * Inherits from:
  *  - <OpenLayers.Control>
  */
@@ -6716,6 +8353,7 @@
      * 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.
      *
@@ -6851,7 +8489,10 @@
 
 /**
  * Class: OpenLayers.Control.PanZoom
- * 
+ * The PanZoom is a visible control, composed of a
+ * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
+ * default it is drawn in the upper left corner of the map.
+ *
  * Inherits from:
  *  - <OpenLayers.Control>
  */
@@ -6860,11 +8501,21 @@
     /** 
      * APIProperty: slideFactor
      * {Integer} Number of pixels by which we'll pan the map in any direction 
-     *     on clicking the arrow buttons. 
+     *     on clicking the arrow buttons.  If you want to pan by some ratio
+     *     of the map dimensions, use <slideRatio> instead.
      */
     slideFactor: 50,
 
     /** 
+     * APIProperty: slideRatio
+     * {Number} The fraction of map width/height by which we'll pan the map            
+     *     on clicking the arrow buttons.  Default is null.  If set, will
+     *     override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
+     *     button will pan up half the map height. 
+     */
+    slideRatio: null,
+
+    /** 
      * Property: buttons
      * {Array(DOMElement)} Array of Button Divs 
      */
@@ -6893,11 +8544,7 @@
      */
     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.removeButtons();
         this.buttons = null;
         this.position = null;
     },
@@ -6967,14 +8614,49 @@
             OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
         btn.action = id;
         btn.map = this.map;
-        btn.slideFactor = this.slideFactor;
+    
+        if(!this.slideRatio){
+            var slideFactorPixels = this.slideFactor;
+            var getSlideFactor = function() {
+                return slideFactorPixels;
+            };
+        } else {
+            var slideRatio = this.slideRatio;
+            var getSlideFactor = function(dim) {
+                return this.map.getSize()[dim] * slideRatio;
+            };
+        }
 
+        btn.getSlideFactor = getSlideFactor;
+
         //we want to remember/reference the outer div
         this.buttons.push(btn);
         return btn;
     },
     
     /**
+     * Method: _removeButton
+     * 
+     * Parameters:
+     * btn - {Object}
+     */
+    _removeButton: function(btn) {
+        OpenLayers.Event.stopObservingElement(btn);
+        btn.map = null;
+        this.div.removeChild(btn);
+        OpenLayers.Util.removeItem(this.buttons, btn);
+    },
+    
+    /**
+     * Method: removeButtons
+     */
+    removeButtons: function() {
+        for(var i=this.buttons.length-1; i>=0; --i) {
+            this._removeButton(this.buttons[i]);
+        }
+    },
+    
+    /**
      * Method: doubleClick
      *
      * Parameters:
@@ -7001,16 +8683,16 @@
 
         switch (this.action) {
             case "panup": 
-                this.map.pan(0, -this.slideFactor);
+                this.map.pan(0, -this.getSlideFactor("h"));
                 break;
             case "pandown": 
-                this.map.pan(0, this.slideFactor);
+                this.map.pan(0, this.getSlideFactor("h"));
                 break;
             case "panleft": 
-                this.map.pan(-this.slideFactor, 0);
+                this.map.pan(-this.getSlideFactor("w"), 0);
                 break;
             case "panright": 
-                this.map.pan(this.slideFactor, 0);
+                this.map.pan(this.getSlideFactor("w"), 0);
                 break;
             case "zoomin": 
                 this.map.zoomIn(); 
@@ -7496,7 +9178,6 @@
     initialize: function (object, element, eventTypes, fallThrough, options) {
         OpenLayers.Util.extend(this, options);
         this.object     = object;
-        this.element    = element;
         this.fallThrough = fallThrough;
         this.listeners  = {};
 
@@ -7517,7 +9198,7 @@
         
         // if a dom element is specified, add a listeners list 
         // for browser events on the element and register them
-        if (this.element != null) {
+        if (element != null) {
             this.attachToElement(element);
         }
     },
@@ -7560,6 +9241,10 @@
      * element - {HTMLDOMElement} a DOM element to attach browser events to
      */
     attachToElement: function (element) {
+        if(this.element) {
+            OpenLayers.Event.stopObservingElement(this.element);
+        }
+        this.element = element;
         for (var i=0, len=this.BROWSER_EVENTS.length; i<len; i++) {
             var eventType = this.BROWSER_EVENTS[i];
 
@@ -7736,7 +9421,13 @@
      *     chain of listeners will stop getting called.
      */
     triggerEvent: function (type, evt) {
+        var listeners = this.listeners[type];
 
+        // fast path
+        if(!listeners || listeners.length == 0) {
+            return;
+        }
+
         // prep evt object with object & div references
         if (evt == null) {
             evt = {};
@@ -7750,25 +9441,21 @@
         // 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, len=listeners.length; i<len; 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;
-                }
+        var listeners = listeners.slice(), continueChain;
+        for (var i=0, len=listeners.length; i<len; 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);
-            }
         }
+        // don't fall through to other DOM elements
+        if (!this.fallThrough) {           
+            OpenLayers.Event.stop(evt, true);
+        }
         return continueChain;
     },
 
@@ -7820,17 +9507,19 @@
         }
         
         if (!this.element.scrolls) {
-            this.element.scrolls = [];
-            this.element.scrolls[0] = (document.documentElement.scrollLeft
-                         || document.body.scrollLeft);
-            this.element.scrolls[1] = (document.documentElement.scrollTop
-                         || document.body.scrollTop);
+            this.element.scrolls = [
+                (document.documentElement.scrollLeft
+                         || document.body.scrollLeft),
+                (document.documentElement.scrollTop
+                         || document.body.scrollTop)
+            ];
         }
 
         if (!this.element.lefttop) {
-            this.element.lefttop = [];
-            this.element.lefttop[0] = (document.documentElement.clientLeft || 0);
-            this.element.lefttop[1] = (document.documentElement.clientTop || 0);
+            this.element.lefttop = [
+                (document.documentElement.clientLeft || 0),
+                (document.documentElement.clientTop  || 0)
+            ];
         }
         
         if (!this.element.offsets) {
@@ -7858,6 +9547,7 @@
 
 /**
  * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Console.js
  */
 
 /**
@@ -7902,6 +9592,20 @@
     internalProjection: null,
 
     /**
+     * APIProperty: data
+     * {Object} When <keepData> is true, this is the parsed string sent to
+     *     <read>.
+     */
+    data: null,
+
+    /**
+     * APIProperty: keepData
+     * {Object} Maintain a reference (<data>) to the most recently read data.
+     *     Default is false.
+     */
+    keepData: false,
+
+    /**
      * Constructor: OpenLayers.Format
      * Instances of this class are not useful.  See one of the subclasses.
      *
@@ -7909,6 +9613,10 @@
      * options - {Object} An optional object with properties to set on the
      *           format
      *
+     * Valid options:
+     * keepData - {Boolean} If true, upon <read>, the data property will be
+     *     set to the parsed object (e.g. the json or xml object).
+     *
      * Returns:
      * An instance of OpenLayers.Format
      */
@@ -8083,7 +9791,7 @@
     'filterEvaluateNotImplemented': "evaluate is not implemented for this filter type."
 };
 /* ======================================================================
-    OpenLayers/Popup/Anchored.js
+    OpenLayers/Popup/AnchoredBubble.js
    ====================================================================== */
 
 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
@@ -8092,108 +9800,80 @@
 
 
 /**
- * @requires OpenLayers/Popup.js
+ * @requires OpenLayers/Popup/Anchored.js
  */
 
 /**
- * Class: OpenLayers.Popup.Anchored
+ * Class: OpenLayers.Popup.AnchoredBubble
  * 
- * Inherits from:
- *  - <OpenLayers.Popup>
+ * Inherits from: 
+ *  - <OpenLayers.Popup.Anchored>
  */
-OpenLayers.Popup.Anchored = 
-  OpenLayers.Class(OpenLayers.Popup, {
+OpenLayers.Popup.AnchoredBubble = 
+  OpenLayers.Class(OpenLayers.Popup.Anchored, {
 
-    /** 
-     * Parameter: relativePosition
-     * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
-     */
-    relativePosition: null,
-
     /**
-     * Parameter: anchor
-     * {Object} Object to which we'll anchor the popup. Must expose a 
-     *     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
+     * Property: rounded
+     * {Boolean} Has the popup been rounded yet?
      */
-    anchor: null,
-
+    rounded: false, 
+    
     /** 
-    * Constructor: OpenLayers.Popup.Anchored
-    * 
-    * Parameters:
-    * id - {String}
-    * lonlat - {<OpenLayers.LonLat>}
-    * contentSize - {<OpenLayers.Size>}
-    * contentHTML - {String}
-    * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> 
-    *     and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
-    * closeBox - {Boolean}
-    * closeBoxCallback - {Function} Function to be called on closeBox click.
-    */
+     * Constructor: OpenLayers.Popup.AnchoredBubble
+     * 
+     * Parameters:
+     * id - {String}
+     * lonlat - {<OpenLayers.LonLat>}
+     * contentSize - {<OpenLayers.Size>}
+     * contentHTML - {String}
+     * anchor - {Object} Object to which we'll anchor the popup. Must expose 
+     *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
+     *     (Note that this is generally an <OpenLayers.Icon>).
+     * closeBox - {Boolean}
+     * closeBoxCallback - {Function} Function to be called on closeBox click.
+     */
     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
                         closeBoxCallback) {
-        var newArguments = [
-            id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
-        ];
-        OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
-
-        this.anchor = (anchor != null) ? anchor 
-                                       : { size: new OpenLayers.Size(0,0),
-                                           offset: new OpenLayers.Pixel(0,0)};
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        this.anchor = null;
-        this.relativePosition = null;
         
-        OpenLayers.Popup.prototype.destroy.apply(this, arguments);        
+        this.padding = new OpenLayers.Bounds(
+            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
+            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
+        );
+        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
     },
 
-    /**
-     * APIMethod: show
-     * Overridden from Popup since user might hide popup and then show() it 
-     *     in a new location (meaning we might want to update the relative
-     *     position on the show)
-     */
-    show: function() {
-        this.updatePosition();
-        OpenLayers.Popup.prototype.show.apply(this, arguments);
-    },
-
-    /**
-     * Method: moveTo
-     * Since the popup is moving to a new px, it might need also to be moved
-     *     relative to where the marker is. We first calculate the new 
-     *     relativePosition, and then we calculate the new px where we will 
-     *     put the popup, based on the new relative position. 
+    /** 
+     * Method: draw
      * 
-     *     If the relativePosition has changed, we must also call 
-     *     updateRelativePosition() to make any visual changes to the popup 
-     *     which are associated with putting it in a new relativePosition.
-     * 
      * Parameters:
      * px - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {DOMElement} Reference to a div that contains the drawn popup.
      */
-    moveTo: function(px) {
-        var oldRelativePosition = this.relativePosition;
-        this.relativePosition = this.calculateRelativePosition(px);
+    draw: function(px) {
         
-        var newPx = this.calculateNewPx(px);
+        OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
+
+        this.setContentHTML();
         
-        var newArguments = new Array(newPx);        
-        OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
-        
-        //if this move has caused the popup to change its relative position, 
-        // we need to make the appropriate cosmetic changes.
-        if (this.relativePosition != oldRelativePosition) {
-            this.updateRelativePosition();
-        }
+        //set the popup color and opacity           
+        this.setBackgroundColor(); 
+        this.setOpacity();
+
+        return this.div;
     },
 
     /**
+     * Method: updateRelativePosition
+     * The popup has been moved to a new relative location, in which case
+     *     we will want to re-do the rico corners.
+     */
+    updateRelativePosition: function() {
+        this.setRicoCorners();
+    },
+
+    /**
      * APIMethod: setSize
      * 
      * Parameters:
@@ -8201,74 +9881,108 @@
      *     contents div (in pixels).
      */
     setSize:function(contentSize) { 
-        OpenLayers.Popup.prototype.setSize.apply(this, arguments);
+        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
 
-        if ((this.lonlat) && (this.map)) {
-            var px = this.map.getLayerPxFromLonLat(this.lonlat);
-            this.moveTo(px);
+        this.setRicoCorners();
+    },  
+
+    /**
+     * APIMethod: setBackgroundColor
+     * 
+     * Parameters:
+     * color - {String}
+     */
+    setBackgroundColor:function(color) { 
+        if (color != undefined) {
+            this.backgroundColor = color; 
         }
+        
+        if (this.div != null) {
+            if (this.contentDiv != null) {
+                this.div.style.background = "transparent";
+                OpenLayers.Rico.Corner.changeColor(this.groupDiv, 
+                                                   this.backgroundColor);
+            }
+        }
     },  
     
+    /**
+     * APIMethod: setOpacity
+     * 
+     * Parameters: 
+     * opacity - {float}
+     */
+    setOpacity:function(opacity) { 
+        OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
+        
+        if (this.div != null) {
+            if (this.groupDiv != null) {
+                OpenLayers.Rico.Corner.changeOpacity(this.groupDiv, 
+                                                     this.opacity);
+            }
+        }
+    },  
+ 
     /** 
-     * Method: calculateRelativePosition
+     * Method: setBorder
+     * Always sets border to 0. Bubble Popups can not have a border.
      * 
      * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
-     *     should be placed.
+     * border - {Integer}
      */
-    calculateRelativePosition:function(px) {
-        var lonlat = this.map.getLonLatFromLayerPx(px);        
-        
-        var extent = this.map.getExtent();
-        var quadrant = extent.determineQuadrant(lonlat);
-        
-        return OpenLayers.Bounds.oppositeQuadrant(quadrant);
-    }, 
+    setBorder:function(border) { 
+        this.border = 0;
+    },      
+ 
+    /** 
+     * Method: setRicoCorners
+     * Update RICO corners according to the popup's current relative postion.
+     */
+    setRicoCorners:function() {
+    
+        var corners = this.getCornersToRound(this.relativePosition);
+        var options = {corners: corners,
+                         color: this.backgroundColor,
+                       bgColor: "transparent",
+                         blend: false};
 
-    /**
-     * Method: updateRelativePosition
-     * The popup has been moved to a new relative location, so we may want to 
-     *     make some cosmetic adjustments to it. 
-     * 
-     *     Note that in the classic Anchored popup, there is nothing to do 
-     *     here, since the popup looks exactly the same in all four positions.
-     *     Subclasses such as the AnchoredBubble and Framed, however, will 
-     *     want to do something special here.
-     */
-    updateRelativePosition: function() {
-        //to be overridden by subclasses
+        if (!this.rounded) {
+            OpenLayers.Rico.Corner.round(this.div, options);
+            this.rounded = true;
+        } else {
+            OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
+            //set the popup color and opacity
+            this.setBackgroundColor(); 
+            this.setOpacity();
+        }
     },
 
     /** 
-     * Method: calculateNewPx
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     * 
+     * Method: getCornersToRound
+     *  
      * Returns:
-     * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
-     *     relative to the passed-in px.
+     * {String} The proper corners string ("tr tl bl br") for rico to round.
      */
-    calculateNewPx:function(px) {
-        var newPx = px.offset(this.anchor.offset);
-        
-        //use contentSize if size is not already set
-        var size = this.size || this.contentSize;
+    getCornersToRound:function() {
 
-        var top = (this.relativePosition.charAt(0) == 't');
-        newPx.y += (top) ? -size.h : this.anchor.size.h;
-        
-        var left = (this.relativePosition.charAt(1) == 'l');
-        newPx.x += (left) ? -size.w : this.anchor.size.w;
+        var corners = ['tl', 'tr', 'bl', 'br'];
 
-        return newPx;   
+        //we want to round all the corners _except_ the opposite one. 
+        var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
+        OpenLayers.Util.removeItem(corners, corner);
+
+        return corners.join(" ");
     },
 
-    CLASS_NAME: "OpenLayers.Popup.Anchored"
+    CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
 });
+
+/**
+ * Constant: CORNER_SIZE
+ * {Integer} 5. Border space for the RICO corners.
+ */
+OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
+
 /* ======================================================================
     OpenLayers/Projection.js
    ====================================================================== */
@@ -8307,8 +10021,10 @@
      *     pro4js projection object. 
      *
      * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *     format
+     * projCode - {String} A string identifying the Well Known Identifier for
+     *    the projection.
+     * options - {Object} An optional object to set additional properties
+     *     on the layer.
      *
      * Returns:
      * {<OpenLayers.Projection>} A projection object.
@@ -8449,7 +10165,7 @@
     return point;
 };
 /* ======================================================================
-    OpenLayers/Renderer/Canvas.js
+    OpenLayers/Renderer/SVG.js
    ====================================================================== */
 
 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
@@ -8457,1662 +10173,1830 @@
  * full text of the license. */
 
 /**
- * @requires OpenLayers/Renderer.js
+ * @requires OpenLayers/Renderer/Elements.js
  */
 
 /**
- * Class: OpenLayers.Renderer.Canvas 
- * A renderer based on the 2D 'canvas' drawing element.element
+ * Class: OpenLayers.Renderer.SVG
  * 
  * Inherits:
- *  - <OpenLayers.Renderer>
+ *  - <OpenLayers.Renderer.Elements>
  */
-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
+OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
 
+    /** 
+     * Property: xmlns
+     * {String}
+     */
+    xmlns: "http://www.w3.org/2000/svg",
+    
     /**
-     * Property: root
-     * {DOMElement} root element of canvas.
+     * Property: xlinkns
+     * {String}
      */
-    root: null,
+    xlinkns: "http://www.w3.org/1999/xlink",
 
     /**
-     * Property: canvas
-     * {Canvas} The canvas context object.
+     * Constant: MAX_PIXEL
+     * {Integer} Firefox has a limitation where values larger or smaller than  
+     *           about 15000 in an SVG document lock the browser up. This 
+     *           works around it.
      */
-    canvas: null, 
-    
+    MAX_PIXEL: 15000,
+
     /**
-     * Property: features
-     * {Object} Internal object of feature/style pairs for use in redrawing the layer.
+     * Property: translationParameters
+     * {Object} Hash with "x" and "y" properties
      */
-    features: null, 
-   
+    translationParameters: null,
+    
     /**
-     * Property: geometryMap
-     * {Object} Geometry -> Feature lookup table. Used by eraseGeometry to
-     *     lookup features to remove from our internal table (this.features)
-     *     when erasing geoms.
+     * Property: symbolSize
+     * {Object} Cache for symbol sizes according to their svg coordinate space
      */
-    geometryMap: null,
- 
+    symbolSize: {},
+
     /**
-     * Constructor: OpenLayers.Renderer.Canvas
-     *
+     * Constructor: OpenLayers.Renderer.SVG
+     * 
      * Parameters:
-     * containerID - {<String>} 
+     * containerID - {String}
      */
     initialize: function(containerID) {
-        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
-        this.root = document.createElement("canvas");
-        this.container.appendChild(this.root);
-        this.canvas = this.root.getContext("2d");
-        this.features = {};
-        this.geometryMap = {};
+        if (!this.supported()) { 
+            return; 
+        }
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.translationParameters = {x: 0, y: 0};
     },
-    
-    /** 
-     * Method: eraseGeometry
-     * Erase a geometry from the renderer. Because the Canvas renderer has
-     *     'memory' of the features that it has drawn, we have to remove the
-     *     feature so it doesn't redraw.   
-     * 
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
+
+    /**
+     * APIMethod: destroy
      */
-    eraseGeometry: function(geometry) {
-        this.eraseFeatures(this.features[this.geometryMap[geometry.id]][0]);
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
     },
-
+    
     /**
      * APIMethod: supported
      * 
      * Returns:
-     * {Boolean} Whether or not the browser supports the renderer class
+     * {Boolean} Whether or not the browser supports the SVG renderer
      */
     supported: function() {
-        var canvas = document.createElement("canvas");
-        return !!canvas.getContext;
+        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
+        return (document.implementation && 
+           (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
+            document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
+            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
     },    
-    
+
     /**
-     * Method: setExtent
-     * Set the visible part of the layer.
+     * Method: inValidRange
+     * See #669 for more information
      *
-     * Resolution has probably changed, so we nullify the resolution 
-     * cache (this.resolution), then redraw. 
-     *
      * Parameters:
-     * extent - {<OpenLayers.Bounds>} 
-     */
-    setExtent: function(extent) {
-        this.extent = extent.clone();
-        this.resolution = null;
-        this.redraw();
+     * x      - {Integer}
+     * y      - {Integer}
+     * xyOnly - {Boolean} whether or not to just check for x and y, which means
+     *     to not take the current translation parameters into account if true.
+     * 
+     * Returns:
+     * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
+     *           valid range.
+     */ 
+    inValidRange: function(x, y, xyOnly) {
+        var left = x + (xyOnly ? 0 : this.translationParameters.x);
+        var top = y + (xyOnly ? 0 : this.translationParameters.y);
+        return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
+                top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
     },
-    
+
     /**
-     * Method: setSize
-     * Sets the size of the drawing surface.
-     *
-     * Once the size is updated, redraw the canvas.
-     *
+     * Method: setExtent
+     * 
      * Parameters:
-     * size - {<OpenLayers.Size>} 
+     * extent - {<OpenLayers.Bounds>}
+     * resolutionChanged - {Boolean}
+     * 
+     * Returns:
+     * {Boolean} true to notify the layer that the new extent does not exceed
+     *     the coordinate range, and the features will not need to be redrawn.
+     *     False otherwise.
      */
-    setSize: function(size) {
-        this.size = size.clone();
-        this.root.style.width = size.w + "px";
-        this.root.style.height = size.h + "px";
-        this.root.width = size.w;
-        this.root.height = size.h;
-        this.resolution = null;
+    setExtent: function(extent, resolutionChanged) {
+        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
+                                                               arguments);
+        
+        var resolution = this.getResolution();
+        var left = -extent.left / resolution;
+        var top = extent.top / resolution;
+
+        // If the resolution has changed, start over changing the corner, because
+        // the features will redraw.
+        if (resolutionChanged) {
+            this.left = left;
+            this.top = top;
+            // Set the viewbox
+            var extentString = "0 0 " + this.size.w + " " + this.size.h;
+
+            this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
+            this.translate(0, 0);
+            return true;
+        } else {
+            var inRange = this.translate(left - this.left, top - this.top);
+            if (!inRange) {
+                // recenter the coordinate system
+                this.setExtent(extent, true);
+            }
+            return inRange;
+        }
     },
     
     /**
-     * Method: drawFeature
-     * Draw the feature. Stores the feature in the features list,
-     * then redraws the layer. 
-     *
+     * Method: translate
+     * Transforms the SVG coordinate system
+     * 
      * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>} 
-     * style - {<Object>} 
+     * x - {Float}
+     * y - {Float}
+     * 
+     * Returns:
+     * {Boolean} true if the translation parameters are in the valid coordinates
+     *     range, false otherwise.
      */
-    drawFeature: function(feature, style) {
-        if(style == null) {
-            style = feature.style;
+    translate: function(x, y) {
+        if (!this.inValidRange(x, y, true)) {
+            return false;
+        } else {
+            var transformString = "";
+            if (x || y) {
+                transformString = "translate(" + x + "," + y + ")";
+            }
+            this.root.setAttributeNS(null, "transform", transformString);
+            this.translationParameters = {x: x, y: y};
+            return true;
         }
-        style = OpenLayers.Util.extend({
-          'fillColor': '#000000',
-          'strokeColor': '#000000',
-          'strokeWidth': 2,
-          'fillOpacity': 1,
-          'strokeOpacity': 1
-        }, style);  
-        this.features[feature.id] = [feature, style]; 
-        this.geometryMap[feature.geometry.id] = feature.id; 
-        this.redraw();
     },
 
+    /**
+     * Method: setSize
+     * Sets the size of the drawing surface.
+     * 
+     * Parameters:
+     * size - {<OpenLayers.Size>} The size of the drawing surface
+     */
+    setSize: function(size) {
+        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
+        
+        this.rendererRoot.setAttributeNS(null, "width", this.size.w);
+        this.rendererRoot.setAttributeNS(null, "height", this.size.h);
+    },
 
     /** 
-     * Method: drawGeometry
-     * Used when looping (in redraw) over the features; draws
-     * the canvas. 
-     *
+     * Method: getNodeType 
+     * 
      * Parameters:
-     * geometry - {<OpenLayers.Geometry>} 
-     * style - {Object} 
-     * featureId - {<String>} 
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
      */
-    drawGeometry: function(geometry, style) {
-        var className = geometry.CLASS_NAME;
-        if ((className == "OpenLayers.Geometry.Collection") ||
-            (className == "OpenLayers.Geometry.MultiPoint") ||
-            (className == "OpenLayers.Geometry.MultiLineString") ||
-            (className == "OpenLayers.Geometry.MultiPolygon")) {
-            for (var i = 0; i < geometry.components.length; i++) {
-                this.drawGeometry(geometry.components[i], style);
-            }
-            return;
-        };
+    getNodeType: function(geometry, style) {
+        var nodeType = null;
         switch (geometry.CLASS_NAME) {
             case "OpenLayers.Geometry.Point":
-                this.drawPoint(geometry, style);
+                if (style.externalGraphic) {
+                    nodeType = "image";
+                } else if (this.isComplexSymbol(style.graphicName)) {
+                    nodeType = "use";
+                } else {
+                    nodeType = "circle";
+                }
                 break;
+            case "OpenLayers.Geometry.Rectangle":
+                nodeType = "rect";
+                break;
             case "OpenLayers.Geometry.LineString":
-                this.drawLineString(geometry, style);
+                nodeType = "polyline";
                 break;
             case "OpenLayers.Geometry.LinearRing":
-                this.drawLinearRing(geometry, style);
+                nodeType = "polygon";
                 break;
             case "OpenLayers.Geometry.Polygon":
-                this.drawPolygon(geometry, style);
+            case "OpenLayers.Geometry.Curve":
+            case "OpenLayers.Geometry.Surface":
+                nodeType = "path";
                 break;
             default:
                 break;
         }
+        return nodeType;
     },
 
-    /**
-     * Method: drawExternalGraphic
-     * Called to draw External graphics. 
+    /** 
+     * Method: setStyle
+     * Use to set all the style attributes to a SVG node.
      * 
-     * Parameters: 
-     * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
-     */ 
-    drawExternalGraphic: function(pt, style) {
-       var img = new Image();
-       img.src = style.externalGraphic;
+     * Takes care to adjust stroke width and point radius to be
+     * resolution-relative
+     *
+     * Parameters:
+     * node - {SVGDomElement} An SVG element to decorate
+     * style - {Object}
+     * options - {Object} Currently supported options include 
+     *                              'isFilled' {Boolean} and
+     *                              'isStroked' {Boolean}
+     */
+    setStyle: function(node, style, options) {
+        style = style  || node._style;
+        options = options || node._options;
+        var r = parseFloat(node.getAttributeNS(null, "r"));
+        var widthFactor = 1;
+        var pos;
+        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
+            if (style.externalGraphic) {
+                pos = this.getPosition(node);
+                
+        		if (style.graphicTitle) {
+                    node.setAttributeNS(null, "title", style.graphicTitle);
+                }
+                if (style.graphicWidth && style.graphicHeight) {
+                  node.setAttributeNS(null, "preserveAspectRatio", "none");
+                }
+                var width = style.graphicWidth || style.graphicHeight;
+                var height = style.graphicHeight || style.graphicWidth;
+                width = width ? width : style.pointRadius*2;
+                height = height ? height : style.pointRadius*2;
+                var xOffset = (style.graphicXOffset != undefined) ?
+                    style.graphicXOffset : -(0.5 * width);
+                var yOffset = (style.graphicYOffset != undefined) ?
+                    style.graphicYOffset : -(0.5 * height);
 
-       var width = style.graphicWidth || style.graphicHeight;
-       var height = style.graphicHeight || style.graphicWidth;
-       width = width ? width : style.pointRadius*2;
-       height = height ? height : style.pointRadius*2;
-       var xOffset = (style.graphicXOffset != undefined) ?
-           style.graphicXOffset : -(0.5 * width);
-       var yOffset = (style.graphicYOffset != undefined) ?
-           style.graphicYOffset : -(0.5 * height);
-       var opacity = style.graphicOpacity || style.fillOpacity;
-       
-       var context = { img: img, 
-                       x: (pt[0]+xOffset), 
-                       y: (pt[1]+yOffset), 
-                       width: width, 
-                       height: height, 
-                       canvas: this.canvas };
+                var opacity = style.graphicOpacity || style.fillOpacity;
+                
+                node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
+                node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
+                node.setAttributeNS(null, "width", width);
+                node.setAttributeNS(null, "height", height);
+                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
+                node.setAttributeNS(null, "style", "opacity: "+opacity);
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                // the symbol viewBox is three times as large as the symbol
+                var offset = style.pointRadius * 3;
+                var size = offset * 2;
+                var id = this.importSymbol(style.graphicName);
+                var href = "#" + id;
+                pos = this.getPosition(node);
+                widthFactor = this.symbolSize[id] / size;
+                // Only set the href if it is different from the current one.
+                // This is a workaround for strange rendering behavior in FF3.
+                if (node.getAttributeNS(this.xlinkns, "href") != href) {
+                    node.setAttributeNS(this.xlinkns, "href", href);
+                } else if (size != parseFloat(node.getAttributeNS(null, "width"))) {
+                    // hide the element (and force a reflow so it really gets
+                    // hidden. This workaround is needed for Safari.
+                    node.style.visibility = "hidden";
+                    this.container.scrollLeft = this.container.scrollLeft;
+                }
+                node.setAttributeNS(null, "width", size);
+                node.setAttributeNS(null, "height", size);
+                node.setAttributeNS(null, "x", pos.x - offset);
+                node.setAttributeNS(null, "y", pos.y - offset);
+                // set the visibility back to normal (after the Safari
+                // workaround above)
+                node.style.visibility = "";
+            } else {
+                node.setAttributeNS(null, "r", style.pointRadius);
+            }
 
-       img.onload = OpenLayers.Function.bind( function() {
-           this.canvas.drawImage(this.img, this.x, 
-                                 this.y, this.width, this.height);
-       }, context);   
+            if (typeof style.rotation != "undefined" && pos) {
+                var rotation = OpenLayers.String.format(
+                    "rotate(${0} ${1} ${2})", [style.rotation, pos.x, pos.y]);
+                node.setAttributeNS(null, "transform", rotation);
+            }
+        }
+        
+        if (options.isFilled) {
+            node.setAttributeNS(null, "fill", style.fillColor);
+            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
+        } else {
+            node.setAttributeNS(null, "fill", "none");
+        }
+
+        if (options.isStroked) {
+            node.setAttributeNS(null, "stroke", style.strokeColor);
+            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
+            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
+            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap);
+            // Hard-coded linejoin for now, to make it look the same as in VML.
+            // There is no strokeLinejoin property yet for symbolizers.
+            node.setAttributeNS(null, "stroke-linejoin", "round");
+            node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style,
+                widthFactor));
+        } else {
+            node.setAttributeNS(null, "stroke", "none");
+        }
+        
+        if (style.pointerEvents) {
+            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
+        }
+		        
+        if (style.cursor != null) {
+            node.setAttributeNS(null, "cursor", style.cursor);
+        }
+        
+        return node;
     },
 
-    /**
-     * Method: setCanvasStyle
-     * Prepare the canvas for drawing by setting various global settings.
-     *
+    /** 
+     * Method: dashStyle
+     * 
      * Parameters:
-     * type - {String} one of 'stroke', 'fill', or 'reset'
-     * style - {Object} Symbolizer hash
+     * style - {Object}
+     * widthFactor - {Number}
+     * 
+     * Returns:
+     * {String} A SVG compliant 'stroke-dasharray' value
      */
-    setCanvasStyle: function(type, style) {
-        if (type == "fill") {     
-            this.canvas.globalAlpha = style['fillOpacity'];
-            this.canvas.fillStyle = style['fillColor'];
-        } else if (type == "stroke") {  
-            this.canvas.globalAlpha = style['strokeOpacity'];
-            this.canvas.strokeStyle = style['strokeColor'];
-            this.canvas.lineWidth = style['strokeWidth'];
-        } else {
-            this.canvas.globalAlpha = 0;
-            this.canvas.lineWidth = 1;
+    dashStyle: function(style, widthFactor) {
+        var w = style.strokeWidth * widthFactor;
+
+        switch (style.strokeDashstyle) {
+            case 'solid':
+                return 'none';
+            case 'dot':
+                return [1, 4 * w].join();
+            case 'dash':
+                return [4 * w, 4 * w].join();
+            case 'dashdot':
+                return [4 * w, 4 * w, 1, 4 * w].join();
+            case 'longdash':
+                return [8 * w, 4 * w].join();
+            case 'longdashdot':
+                return [8 * w, 4 * w, 1, 4 * w].join();
+            default:
+                return style.strokeDashstyle.replace(/ /g, ",");
         }
     },
+    
+    /** 
+     * Method: createNode
+     * 
+     * Parameters:
+     * type - {String} Kind of node to draw
+     * id - {String} Id for node
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id
+     */
+    createNode: function(type, id) {
+        var node = document.createElementNS(this.xmlns, type);
+        if (id) {
+            node.setAttributeNS(null, "id", id);
+        }
+        return node;    
+    },
+    
+    /** 
+     * Method: nodeTypeCompare
+     * 
+     * Parameters:
+     * node - {SVGDomElement} An SVG element
+     * type - {String} Kind of node
+     * 
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     */
+    nodeTypeCompare: function(node, type) {
+        return (type == node.nodeName);
+    },
+   
+    /**
+     * Method: createRenderRoot
+     * 
+     * Returns:
+     * {DOMElement} The specific render engine's root element
+     */
+    createRenderRoot: function() {
+        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
+    },
 
     /**
+     * Method: createRoot
+     * 
+     * Returns:
+     * {DOMElement} The main root element to which we'll add vectors
+     */
+    createRoot: function() {
+        return this.nodeFactory(this.container.id + "_root", "g");
+    },
+
+    /**
+     * Method: createDefs
+     *
+     * Returns:
+     * {DOMElement} The element to which we'll add the symbol definitions
+     */
+    createDefs: function() {
+        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
+        this.rendererRoot.appendChild(defs);
+        return defs;
+    },
+
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+
+    /**
      * Method: drawPoint
      * This method is only called by the renderer itself.
      * 
      * Parameters: 
+     * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the point
      */ 
-    drawPoint: function(geometry, style) {
-        var pt = this.getLocalXY(geometry);
-        
-        if (style.externalGraphic) {
-            this.drawExternalGraphic(pt, style);
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
+
+    /**
+     * Method: drawCircle
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * radius - {Float}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the circle
+     */
+    drawCircle: function(node, geometry, radius) {
+        var resolution = this.getResolution();
+        var x = (geometry.x / resolution + this.left);
+        var y = (this.top - geometry.y / resolution);
+
+        if (this.inValidRange(x, y)) { 
+            node.setAttributeNS(null, "cx", x);
+            node.setAttributeNS(null, "cy", y);
+            node.setAttributeNS(null, "r", radius);
+            return node;
         } else {
-            this.setCanvasStyle("fill", style);
-            this.canvas.beginPath();
-            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
-            this.canvas.fill();
+            return false;
+        }    
             
-            this.setCanvasStyle("stroke", style);
-            this.canvas.beginPath();
-            this.canvas.arc(pt[0], pt[1], 6, 0, Math.PI*2, true);
-            this.canvas.stroke();
-            this.setCanvasStyle("reset");
-        }
     },
-
+    
     /**
      * Method: drawLineString
      * This method is only called by the renderer itself.
      * 
      * Parameters: 
+     * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components of
+     *     the linestring, or false if nothing could be drawn
      */ 
-    drawLineString: function(geometry, style) {
-        this.setCanvasStyle("stroke", style);
-        this.canvas.beginPath();
-        var start = this.getLocalXY(geometry.components[0]);
-        this.canvas.moveTo(start[0], start[1]);
-        for(var i = 1; i < geometry.components.length; i++) {
-            var pt = this.getLocalXY(geometry.components[i]);
-            this.canvas.lineTo(pt[0], pt[1]);
+    drawLineString: function(node, geometry) {
+        var componentsResult = this.getComponentsString(geometry.components);
+        if (componentsResult.path) {
+            node.setAttributeNS(null, "points", componentsResult.path);
+            return (componentsResult.complete ? node : null);  
+        } else {
+            return false;
         }
-        this.canvas.stroke();
-        this.setCanvasStyle("reset");
-    },    
+    },
     
     /**
      * Method: drawLinearRing
      * This method is only called by the renderer itself.
      * 
      * Parameters: 
+     * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the linear ring, or false if nothing could be drawn
      */ 
-    drawLinearRing: function(geometry, style) {
-        this.setCanvasStyle("fill", style);
-        this.canvas.beginPath();
-        var start = this.getLocalXY(geometry.components[0]);
-        this.canvas.moveTo(start[0], start[1]);
-        for(var i = 1; i < geometry.components.length - 1 ; i++) {
-            var pt = this.getLocalXY(geometry.components[i]);
-            this.canvas.lineTo(pt[0], pt[1]);
+    drawLinearRing: function(node, geometry) {
+        var componentsResult = this.getComponentsString(geometry.components);
+        if (componentsResult.path) {
+            node.setAttributeNS(null, "points", componentsResult.path);
+            return (componentsResult.complete ? node : null);  
+        } else {
+            return false;
         }
-        this.canvas.fill();
-        
-        var oldWidth = this.canvas.lineWidth; 
-        this.setCanvasStyle("stroke", style);
-        this.canvas.beginPath();
-        var start = this.getLocalXY(geometry.components[0]);
-        this.canvas.moveTo(start[0], start[1]);
-        for(var i = 1; i < geometry.components.length; i++) {
-            var pt = this.getLocalXY(geometry.components[i]);
-            this.canvas.lineTo(pt[0], pt[1]);
-        }
-        this.canvas.stroke();
-        this.setCanvasStyle("reset");
-    },    
+    },
     
     /**
      * Method: drawPolygon
      * This method is only called by the renderer itself.
      * 
      * Parameters: 
+     * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the polygon, or false if nothing could be drawn
      */ 
-    drawPolygon: function(geometry, style) {
-        this.drawLinearRing(geometry.components[0], style);
-        for (var i = 1; i < geometry.components.length; i++) {
-            this.drawLinearRing(geometry.components[i], {
-                fillOpacity: 0, 
-                strokeWidth: 0, 
-                strokeOpacity: 0, 
-                strokeColor: '#000000', 
-                fillColor: '#000000'}
-            ); // inner rings are 'empty'  
+    drawPolygon: function(node, geometry) {
+        var d = "";
+        var draw = true;
+        var complete = true;
+        var linearRingResult, path;
+        for (var j=0, len=geometry.components.length; j<len; j++) {
+            d += " M";
+            linearRingResult = this.getComponentsString(
+                geometry.components[j].components, " ");
+            path = linearRingResult.path;
+            if (path) {
+                d += " " + path;
+                complete = linearRingResult.complete && complete;
+            } else {
+                draw = false;
+            }
         }
+        d += " z";
+        if (draw) {
+            node.setAttributeNS(null, "d", d);
+            node.setAttributeNS(null, "fill-rule", "evenodd");
+            return complete ? node : null;
+        } else {
+            return false;
+        }    
     },
-
+    
     /**
-     * Method: getLocalXY
-     * transform geographic xy into pixel xy
-     *
+     * Method: drawRectangle
+     * This method is only called by the renderer itself.
+     * 
      * Parameters: 
-     * point - {<OpenLayers.Geometry.Point>}
-     */
-    getLocalXY: function(point) {
-        var resolution = this.getResolution();
-        var extent = this.extent;
-        var x = (point.x / resolution + (-extent.left / resolution));
-        var y = ((extent.top / resolution) - point.y / resolution);
-        return [x, y];
-    },
-        
-    /**
-     * Method: clear
-     * Clear all vectors from the renderer.
-     * virtual function.
-     */    
-    clear: function() {
-        this.canvas.clearRect(0, 0, this.root.width, this.root.height);
-    },
-
-    /**
-     * Method: getFeatureIdFromEvent
-     * Returns a feature id from an event on the renderer.  
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
      * 
-     * Parameters:
-     * evt - {<OpenLayers.Event>} 
-     *
      * Returns:
-     * {String} A feature id or null.
-     */
-    getFeatureIdFromEvent: function(evt) {
-        var loc = this.map.getLonLatFromPixel(evt.xy);
+     * {DOMElement} or false if the renderer could not draw the rectangle
+     */ 
+    drawRectangle: function(node, geometry) {
         var resolution = this.getResolution();
-        var bounds = new OpenLayers.Bounds(loc.lon - resolution * 5, 
-                                           loc.lat - resolution * 5, 
-                                           loc.lon + resolution * 5, 
-                                           loc.lat + resolution * 5);
-        var geom = bounds.toGeometry();
-        for (var feat in this.features) {
-            if (!this.features.hasOwnProperty(feat)) { continue; }
-            if (this.features[feat][0].geometry.intersects(geom)) {
-                return feat;
-            }
-        }   
-        return null;
+        var x = (geometry.x / resolution + this.left);
+        var y = (this.top - geometry.y / resolution);
+
+        if (this.inValidRange(x, y)) { 
+            node.setAttributeNS(null, "x", x);
+            node.setAttributeNS(null, "y", y);
+            node.setAttributeNS(null, "width", geometry.width / resolution);
+            node.setAttributeNS(null, "height", geometry.height / resolution);
+            return node;
+        } else {
+            return false;
+        }
     },
     
     /**
-     * Method: eraseFeatures 
-     * This is called by the layer to erase features; removes the feature from
-     *     the list, then redraws the layer.
+     * Method: drawSurface
+     * This method is only called by the renderer itself.
      * 
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} 
-     */
-    eraseFeatures: function(features) {
-        if(!(features instanceof Array)) {
-            features = [features];
-        }
-        for(var i=0; i<features.length; ++i) {
-            delete this.features[features[i].id];
-        }
-        this.redraw();
-    },
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the surface
+     */ 
+    drawSurface: function(node, geometry) {
 
-    /**
-     * Method: redraw
-     * The real 'meat' of the function: any time things have changed,
-     *     redraw() can be called to loop over all the data and (you guessed
-     *     it) redraw it.  Unlike Elements-based Renderers, we can't interact
-     *     with things once they're drawn, to remove them, for example, so
-     *     instead we have to just clear everything and draw from scratch.
-     */
-    redraw: function() {
-        if (!this.locked) {
-            this.clear();
-            for (var id in this.features) {
-                if (!this.features.hasOwnProperty(id)) { continue; }
-                if (!this.features[id][0].geometry) { continue; }
-                this.drawGeometry(this.features[id][0].geometry, this.features[id][1]);
+        // create the svg path string representation
+        var d = null;
+        var draw = true;
+        for (var i=0, len=geometry.components.length; i<len; i++) {
+            if ((i%3) == 0 && (i/3) == 0) {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d = "M " + component;
+            } else if ((i%3) == 1) {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d += " C " + component;
+            } else {
+                var component = this.getShortString(geometry.components[i]);
+                if (!component) { draw = false; }
+                d += " " + component;
             }
+        }
+        d += " Z";
+        if (draw) {
+            node.setAttributeNS(null, "d", d);
+            return node;
+        } else {
+            return false;
         }    
     },
-
-    CLASS_NAME: "OpenLayers.Renderer.Canvas"
-});
-/* ======================================================================
-    OpenLayers/Renderer/Elements.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/Renderer.js
- */
-
-/**
- * Class: OpenLayers.ElementsIndexer
- * This class takes care of figuring out which order elements should be
- *     placed in the DOM based on given indexing methods. 
- */
-OpenLayers.ElementsIndexer = OpenLayers.Class({
-   
-    /**
-     * Property: maxZIndex
-     * {Integer} This is the largest-most z-index value for a node
-     *     contained within the indexer.
-     */
-    maxZIndex: null,
     
     /**
-     * Property: order
-     * {Array<String>} This is an array of node id's stored in the
-     *     order that they should show up on screen. Id's higher up in the
-     *     array (higher array index) represent nodes with higher z-indeces.
-     */
-    order: null, 
-    
-    /**
-     * Property: indices
-     * {Object} This is a hash that maps node ids to their z-index value
-     *     stored in the indexer. This is done to make finding a nodes z-index 
-     *     value O(1).
-     */
-    indices: null,
-    
-    /**
-     * Property: compare
-     * {Function} This is the function used to determine placement of
-     *     of a new node within the indexer. If null, this defaults to to
-     *     the Z_ORDER_DRAWING_ORDER comparison method.
-     */
-    compare: null,
-   
-    /**
-     * APIMethod: initialize
-     * Create a new indexer with 
+     * Method: drawText
+     * This method is only called by the renderer itself.
      * 
-     * Parameters:
-     * yOrdering - {Boolean} Whether to use y-ordering.
+     * Parameters: 
+     * featureId - {String}
+     * style -
+     * location - {<OpenLayers.Geometry.Point>}
      */
-    initialize: function(yOrdering) {
-
-        this.compare = yOrdering ? 
-            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
-            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
-            
-        this.order = [];
-        this.indices = {};
-        this.maxZIndex = 0;
-    },
-    
-    /**
-     * APIMethod: insert
-     * Insert a new node into the indexer. In order to find the correct 
-     *     positioning for the node to be inserted, this method uses a binary 
-     *     search. This makes inserting O(log(n)). 
-     * 
-     * Parameters:
-     * newNode - {DOMElement} The new node to be inserted.
-     * 
-     * Returns
-     * {DOMElement} the node before which we should insert our newNode, or
-     *     null if newNode can just be appended.
-     */
-    insert: function(newNode) {
-        // If the node is known to the indexer, remove it so we can
-        // recalculate where it should go.
-        if (this.exists(newNode)) {
-            this.remove(newNode);
-        }
+    drawText: function(featureId, style, location) {
+        var resolution = this.getResolution();
         
-        var nodeId = newNode.id;
+        var x = (location.x / resolution + this.left);
+        var y = (location.y / resolution - this.top);
         
-        this.determineZIndex(newNode);       
-
-        var leftIndex = -1;
-        var rightIndex = this.order.length;
-        var middle;
-
-        while (rightIndex - leftIndex > 1) {
-            middle = parseInt((leftIndex + rightIndex) / 2);
-            
-            var placement = this.compare(this, newNode,
-                OpenLayers.Util.getElement(this.order[middle]));
-            
-            if (placement > 0) {
-                leftIndex = middle;
-            } else {
-                rightIndex = middle;
-            } 
+        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
+        label.setAttributeNS(null, "x", x);
+        label.setAttributeNS(null, "y", -y);
+        label.setAttributeNS(null, "pointer-events", "none");
+        
+        if (style.fontColor) {
+            label.setAttributeNS(null, "fill", style.fontColor);
         }
+        if (style.fontFamily) {
+            label.setAttributeNS(null, "font-family", style.fontFamily);
+        }
+        if (style.fontSize) {
+            label.setAttributeNS(null, "font-size", style.fontSize);
+        }
+        if (style.fontWeight) {
+            label.setAttributeNS(null, "font-weight", style.fontWeight);
+        }
+        label.setAttributeNS(null, "text-anchor", this._getLabelAlignment(style.labelAlign));
         
-        this.order.splice(rightIndex, 0, nodeId);
-        this.indices[nodeId] = this.getZIndex(newNode);
-        
-        // If the new node should be before another in the index
-        // order, return the node before which we have to insert the new one;
-        // else, return null to indicate that the new node can be appended.
-        var nextIndex = rightIndex + 1;
-        return nextIndex < this.order.length ?
-                OpenLayers.Util.getElement(this.order[nextIndex]) : null;
+        // prevent label duplication
+        if (label.firstChild) {
+            label.removeChild(label.firstChild);
+        }
+        var data = document.createTextNode(style.label);
+        label.appendChild(data);
+        this.root.appendChild(label);
     },
-    
-    /**
-     * APIMethod: remove
-     * 
-     * Parameters:
-     * node - {DOMElement} The node to be removed.
-     */
-    remove: function(node) {
-        var nodeId = node.id;
-        var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
-        if (arrayIndex >= 0) {
-            // Remove it from the order array, as well as deleting the node
-            // from the indeces hash.
-            this.order.splice(arrayIndex, 1);
-            delete this.indices[nodeId];
+    _getLabelAlignment : function(styleValue) {
+        switch(styleValue) {
+            case "left":
+                return "start";
             
-            // Reset the maxium z-index based on the last item in the 
-            // order array.
-            if (this.order.length > 0) {
-                var lastId = this.order[this.order.length - 1];
-                this.maxZIndex = this.indices[lastId];
-            } else {
-                this.maxZIndex = 0;
-            }
+            case "right":
+                return "end";
+            
+            case "middle":
+            case "center":
+            default:
+                return "middle";
         }
     },
-    
-    /**
-     * APIMethod: clear
-     */
-    clear: function() {
-        this.order = [];
-        this.indices = {};
-        this.maxZIndex = 0;
-    },
-    
-    /**
-     * APIMethod: exists
-     *
-     * Parameters:
-     * node- {DOMElement} The node to test for existence.
-     *
-     * Returns:
-     * {Boolean} Whether or not the node exists in the indexer?
-     */
-    exists: function(node) {
-        return (this.indices[node.id] != null);
-    },
 
-    /**
-     * APIMethod: getZIndex
-     * Get the z-index value for the current node from the node data itself.
+    /** 
+     * Method: getComponentString
      * 
      * Parameters:
-     * node - {DOMElement} The node whose z-index to get.
+     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
+     * separator - {String} character between coordinate pairs. Defaults to ","
      * 
      * Returns:
-     * {Integer} The z-index value for the specified node (from the node 
-     *     data itself).
+     * {Object} hash with properties "path" (the string created from the
+     *     components and "complete" (false if the renderer was unable to
+     *     draw all components)
      */
-    getZIndex: function(node) {
-        return node._style.graphicZIndex;  
+    getComponentsString: function(components, separator) {
+        var renderCmp = [];
+        var complete = true;
+        var len = components.length;
+        var strings = [];
+        var str, component, j;
+        for(var i=0; i<len; i++) {
+            component = components[i];
+            renderCmp.push(component);
+            str = this.getShortString(component);
+            if (str) {
+                strings.push(str);
+            } else {
+                // The current component is outside the valid range. Let's
+                // see if the previous or next component is inside the range.
+                // If so, add the coordinate of the intersection with the
+                // valid range bounds.
+                if (i > 0) {
+                    if (this.getShortString(components[i - 1])) {
+                        strings.push(this.clipLine(components[i],
+                            components[i-1]));
+                    }
+                }
+                if (i < len - 1) {
+                    if (this.getShortString(components[i + 1])) {
+                        strings.push(this.clipLine(components[i],
+                            components[i+1]));
+                    }
+                }
+                complete = false;
+            }
+        }
+
+        return {
+            path: strings.join(separator || ","),
+            complete: complete
+        };
     },
     
     /**
-     * Method: determineZIndex
-     * Determine the z-index for the current node if there isn't one, 
-     *     and set the maximum value if we've found a new maximum.
+     * Method: clipLine
+     * Given two points (one inside the valid range, and one outside),
+     * clips the line betweeen the two points so that the new points are both
+     * inside the valid range.
      * 
      * Parameters:
-     * node - {DOMElement} 
+     * badComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     *     invalid point
+     * goodComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     *     valid point
+     * Returns
+     * {String} the SVG coordinate pair of the clipped point (like
+     *     getShortString), or an empty string if both passed componets are at
+     *     the same point.
      */
-    determineZIndex: function(node) {
-        var zIndex = node._style.graphicZIndex;
-        
-        // Everything must have a zIndex. If none is specified,
-        // this means the user *must* (hint: assumption) want this
-        // node to succomb to drawing order. To enforce drawing order
-        // over all indexing methods, we'll create a new z-index that's
-        // greater than any currently in the indexer.
-        if (zIndex == null) {
-            zIndex = this.maxZIndex;
-            node._style.graphicZIndex = zIndex; 
-        } else if (zIndex > this.maxZIndex) {
-            this.maxZIndex = zIndex;
+    clipLine: function(badComponent, goodComponent) {
+        if (goodComponent.equals(badComponent)) {
+            return "";
         }
+        var resolution = this.getResolution();
+        var maxX = this.MAX_PIXEL - this.translationParameters.x;
+        var maxY = this.MAX_PIXEL - this.translationParameters.y;
+        var x1 = goodComponent.x / resolution + this.left;
+        var y1 = this.top - goodComponent.y / resolution;
+        var x2 = badComponent.x / resolution + this.left;
+        var y2 = this.top - badComponent.y / resolution;
+        var k;
+        if (x2 < -maxX || x2 > maxX) {
+            k = (y2 - y1) / (x2 - x1);
+            x2 = x2 < 0 ? -maxX : maxX;
+            y2 = y1 + (x2 - x1) * k;
+        }
+        if (y2 < -maxY || y2 > maxY) {
+            k = (x2 - x1) / (y2 - y1);
+            y2 = y2 < 0 ? -maxY : maxY;
+            x2 = x1 + (y2 - y1) * k;
+        }
+        return x2 + "," + y2;
     },
-    
-    CLASS_NAME: "OpenLayers.ElementsIndexer"
-});
 
-/**
- * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
- * These are the compare methods for figuring out where a new node should be 
- *     placed within the indexer. These methods are very similar to general 
- *     sorting methods in that they return -1, 0, and 1 to specify the 
- *     direction in which new nodes fall in the ordering.
- */
-OpenLayers.ElementsIndexer.IndexingMethods = {
-    
-    /**
-     * Method: Z_ORDER
-     * This compare method is used by other comparison methods.
-     *     It can be used individually for ordering, but is not recommended,
-     *     because it doesn't subscribe to drawing order.
+    /** 
+     * Method: getShortString
      * 
      * Parameters:
-     * indexer - {<OpenLayers.ElementsIndexer>}
-     * newNode - {DOMElement}
-     * nextNode - {DOMElement}
+     * point - {<OpenLayers.Geometry.Point>}
      * 
      * Returns:
-     * {Integer}
+     * {String} or false if point is outside the valid range
      */
-    Z_ORDER: function(indexer, newNode, nextNode) {
-        var newZIndex = indexer.getZIndex(newNode);
+    getShortString: function(point) {
+        var resolution = this.getResolution();
+        var x = (point.x / resolution + this.left);
+        var y = (this.top - point.y / resolution);
 
-        var returnVal = 0;
-        if (nextNode) {
-            var nextZIndex = indexer.getZIndex(nextNode);
-            returnVal = newZIndex - nextZIndex; 
+        if (this.inValidRange(x, y)) { 
+            return x + "," + y;
+        } else {
+            return false;
         }
-        
-        return returnVal;
     },
-
+    
     /**
-     * APIMethod: Z_ORDER_DRAWING_ORDER
-     * This method orders nodes by their z-index, but does so in a way
-     *     that, if there are other nodes with the same z-index, the newest 
-     *     drawn will be the front most within that z-index. This is the 
-     *     default indexing method.
+     * Method: getPosition
+     * Finds the position of an svg node.
      * 
      * Parameters:
-     * indexer - {<OpenLayers.ElementsIndexer>}
-     * newNode - {DOMElement}
-     * nextNode - {DOMElement}
+     * node - {DOMElement}
      * 
      * Returns:
-     * {Integer}
+     * {Object} hash with x and y properties, representing the coordinates
+     *     within the svg coordinate system
      */
-    Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
-        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
-            indexer, 
-            newNode, 
-            nextNode
-        );
-        
-        // Make Z_ORDER subscribe to drawing order by pushing it above
-        // all of the other nodes with the same z-index.
-        if (nextNode && returnVal == 0) {
-            returnVal = 1;
-        }
-        
-        return returnVal;
+    getPosition: function(node) {
+        return({
+            x: parseFloat(node.getAttributeNS(null, "cx")),
+            y: parseFloat(node.getAttributeNS(null, "cy"))
+        });
     },
 
     /**
-     * APIMethod: Z_ORDER_Y_ORDER
-     * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
-     *     best describes which ordering methods have precedence (though, the 
-     *     name would be too long). This method orders nodes by their z-index, 
-     *     but does so in a way that, if there are other nodes with the same 
-     *     z-index, the nodes with the lower y position will be "closer" than 
-     *     those with a higher y position. If two nodes have the exact same y 
-     *     position, however, then this method will revert to using drawing  
-     *     order to decide placement.
+     * Method: importSymbol
+     * add a new symbol definition from the rendererer's symbol hash
      * 
      * Parameters:
-     * indexer - {<OpenLayers.ElementsIndexer>}
-     * newNode - {DOMElement}
-     * nextNode - {DOMElement}
+     * graphicName - {String} name of the symbol to import
      * 
      * Returns:
-     * {Integer}
-     */
-    Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
-        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
-            indexer, 
-            newNode, 
-            nextNode
-        );
+     * {String} - id of the imported symbol
+     */      
+    importSymbol: function (graphicName)  {
+        if (!this.defs) {
+            // create svg defs tag
+            this.defs = this.createDefs();
+        }
+        var id = this.container.id + "-" + graphicName;
         
-        if (nextNode && returnVal == 0) {
-            var newLat = newNode._geometry.getBounds().bottom;
-            var nextLat = nextNode._geometry.getBounds().bottom;
-            
-            var result = nextLat - newLat;
-            returnVal = (result ==0) ? 1 : result;
+        // check if symbol already exists in the defs
+        if (document.getElementById(id) != null) {
+            return id;
         }
         
-        return returnVal;       
-    }
-};
+        var symbol = OpenLayers.Renderer.symbol[graphicName];
+        if (!symbol) {
+            throw new Error(graphicName + ' is not a valid symbol name');
+            return;
+        }
 
+        var symbolNode = this.nodeFactory(id, "symbol");
+        var node = this.nodeFactory(null, "polygon");
+        symbolNode.appendChild(node);
+        var symbolExtent = new OpenLayers.Bounds(
+                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
+
+        var points = "";
+        var x,y;
+        for (var i=0; i<symbol.length; i=i+2) {
+            x = symbol[i];
+            y = symbol[i+1];
+            symbolExtent.left = Math.min(symbolExtent.left, x);
+            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
+            symbolExtent.right = Math.max(symbolExtent.right, x);
+            symbolExtent.top = Math.max(symbolExtent.top, y);
+            points += " " + x + "," + y;
+        }
+        
+        node.setAttributeNS(null, "points", points);
+        
+        var width = symbolExtent.getWidth();
+        var height = symbolExtent.getHeight();
+        // create a viewBox three times as large as the symbol itself,
+        // to allow for strokeWidth being displayed correctly at the corners.
+        var viewBox = [symbolExtent.left - width,
+                        symbolExtent.bottom - height, width * 3, height * 3];
+        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
+        this.symbolSize[id] = Math.max(width, height) * 3;
+        
+        this.defs.appendChild(symbolNode);
+        return symbolNode.id;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.SVG"
+});
+/* ======================================================================
+    OpenLayers/Renderer/VML.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.Renderer.Elements
- * This is another virtual class in that it should never be instantiated by 
- *  itself as a Renderer. It exists because there is *tons* of shared 
- *  functionality between different vector libraries which use nodes/elements
- *  as a base for rendering vectors. 
+ * @requires OpenLayers/Renderer/Elements.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.VML
+ * Render vector features in browsers with VML capability.  Construct a new
+ * VML renderer with the <OpenLayers.Renderer.VML> constructor.
  * 
- * The highlevel bits of code that are implemented here are the adding and 
- *  removing of geometries, which is essentially the same for any 
- *  element-based renderer. The details of creating each node and drawing the
- *  paths are of course different, but the machinery is the same. 
- * 
- * Inherits:
- *  - <OpenLayers.Renderer>
+ * Note that for all calculations in this class, we use toFixed() to round a 
+ * float value to an integer. This is done because it seems that VML doesn't 
+ * support float values.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Renderer.Elements>
  */
-OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
+OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
 
     /**
-     * Property: rendererRoot
-     * {DOMElement}
+     * Property: xmlns
+     * {String} XML Namespace URN
      */
-    rendererRoot: null,
+    xmlns: "urn:schemas-microsoft-com:vml",
     
     /**
-     * Property: root
-     * {DOMElement}
+     * Property: symbolCache
+     * {DOMElement} node holding symbols. This hash is keyed by symbol name,
+     *     and each value is a hash with a "path" and an "extent" property.
      */
-    root: null,
+    symbolCache: {},
 
     /**
-     * Property: xmlns
-     * {String}
-     */    
-    xmlns: null,
-    
-    /**
-     * Property: Indexer
-     * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer 
-     *     created upon initialization if the zIndexing or yOrdering options
-     *     passed to this renderer's constructor are set to true.
+     * Property: offset
+     * {Object} Hash with "x" and "y" properties
      */
-    indexer: null, 
+    offset: null,
     
     /**
-     * Constant: BACKGROUND_ID_SUFFIX
-     * {String}
+     * Constructor: OpenLayers.Renderer.VML
+     * Create a new VML renderer.
+     *
+     * Parameters:
+     * containerID - {String} The id for the element that contains the renderer
      */
-    BACKGROUND_ID_SUFFIX: "_background",
-    
+    initialize: function(containerID) {
+        if (!this.supported()) { 
+            return; 
+        }
+        if (!document.namespaces.olv) {
+            document.namespaces.add("olv", this.xmlns);
+            var style = document.createStyleSheet();
+            style.addRule('olv\\:*', "behavior: url(#default#VML); " +
+                                   "position: absolute; display: inline-block;");
+        }
+        
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.offset = {x: 0, y: 0};
+    },
+
     /**
-     * Property: minimumSymbolizer
-     * {Object}
+     * APIMethod: destroy
+     * Deconstruct the renderer.
      */
-    minimumSymbolizer: {
-        strokeLinecap: "round",
-        strokeOpacity: 1,
-        strokeDashstyle: "solid",
-        fillOpacity: 1,
-        pointRadius: 0
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
     },
-    
+
     /**
-     * Constructor: OpenLayers.Renderer.Elements
+     * APIMethod: supported
+     * Determine whether a browser supports this renderer.
+     *
+     * Returns:
+     * {Boolean} The browser supports the VML renderer
+     */
+    supported: function() {
+        return !!(document.namespaces);
+    },    
+
+    /**
+     * Method: setExtent
+     * Set the renderer's extent
+     *
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>}
+     * resolutionChanged - {Boolean}
      * 
-     * Parameters:
-     * containerID - {String}
-     * options - {Object} options for this renderer. Supported options are:
-     *     * yOrdering - {Boolean} Whether to use y-ordering
-     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
-     *         if yOrdering is set to true.
+     * Returns:
+     * {Boolean} true to notify the layer that the new extent does not exceed
+     *     the coordinate range, and the features will not need to be redrawn.
      */
-    initialize: function(containerID, options) {
-        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
+    setExtent: function(extent, resolutionChanged) {
+        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
+                                                               arguments);
+        var resolution = this.getResolution();
+    
+        var left = extent.left/resolution;
+        var top = extent.top/resolution - this.size.h;
+        if (resolutionChanged) {
+            this.offset = {x: left, y: top};
+            left = 0;
+            top = 0;
+        } else {
+            left = left - this.offset.x;
+            top = top - this.offset.y;
+        }
+        
+        var org = left + " " + top;
+        this.root.setAttribute("coordorigin", org);
 
-        this.rendererRoot = this.createRenderRoot();
-        this.root = this.createRoot();
+        var size = this.size.w + " " + this.size.h;
+        this.root.setAttribute("coordsize", size);
         
-        this.rendererRoot.appendChild(this.root);
-        this.container.appendChild(this.rendererRoot);
+        // flip the VML display Y axis upside down so it 
+        // matches the display Y axis of the map
+        this.root.style.flip = "y";
         
-        if(options && (options.zIndexing || options.yOrdering)) {
-            this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
-        }
+        return true;
     },
-    
+
+
     /**
-     * Method: destroy
+     * Method: setSize
+     * Set the size of the drawing surface
+     *
+     * Parameters:
+     * size - {<OpenLayers.Size>} the size of the drawing surface
      */
-    destroy: function() {
+    setSize: function(size) {
+        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
 
-        this.clear(); 
+        this.rendererRoot.style.width = this.size.w + "px";
+        this.rendererRoot.style.height = this.size.h + "px";
 
-        this.rendererRoot = null;
-        this.root = null;
-        this.xmlns = null;
+        this.root.style.width = this.size.w + "px";
+        this.root.style.height = this.size.h + "px";
+    },
 
-        OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
-    },
-    
     /**
-     * Method: clear
-     * Remove all the elements from the root
-     */    
-    clear: function() {
-        if (this.root) {
-            while (this.root.childNodes.length > 0) {
-                this.root.removeChild(this.root.firstChild);
-            }
-        }
-        if (this.indexer) {
-            this.indexer.clear();
-        }
-    },
-
-    /** 
      * Method: getNodeType
-     * This function is in charge of asking the specific renderer which type
-     *     of node to create for the given geometry and style. All geometries
-     *     in an Elements-based renderer consist of one node and some
-     *     attributes. We have the nodeFactory() function which creates a node
-     *     for us, but it takes a 'type' as input, and that is precisely what
-     *     this function tells us.  
-     *  
+     * Get the node type for a geometry and style
+     *
      * Parameters:
      * geometry - {<OpenLayers.Geometry>}
      * style - {Object}
-     * 
+     *
      * Returns:
      * {String} The corresponding node type for the specified geometry
      */
-    getNodeType: function(geometry, style) { },
+    getNodeType: function(geometry, style) {
+        var nodeType = null;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                if (style.externalGraphic) {
+                    nodeType = "olv:rect";
+                } else if (this.isComplexSymbol(style.graphicName)) {
+                    nodeType = "olv:shape";
+                } else {
+                    nodeType = "olv:oval";
+                }
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                nodeType = "olv:rect";
+                break;
+            case "OpenLayers.Geometry.LineString":
+            case "OpenLayers.Geometry.LinearRing":
+            case "OpenLayers.Geometry.Polygon":
+            case "OpenLayers.Geometry.Curve":
+            case "OpenLayers.Geometry.Surface":
+                nodeType = "olv:shape";
+                break;
+            default:
+                break;
+        }
+        return nodeType;
+    },
 
-    /** 
-     * Method: drawGeometry 
-     * Draw the geometry, creating new nodes, setting paths, setting style,
-     *     setting featureId on the node.  This method should only be called
-     *     by the renderer itself.
+    /**
+     * Method: setStyle
+     * Use to set all the style attributes to a VML node.
      *
      * Parameters:
+     * node - {DOMElement} An VML element to decorate
+     * style - {Object}
+     * options - {Object} Currently supported options include 
+     *                              'isFilled' {Boolean} and
+     *                              'isStroked' {Boolean}
      * geometry - {<OpenLayers.Geometry>}
-     * style - {Object}
-     * featureId - {String}
-     * 
-     * Returns:
-     * {Boolean} true if the geometry has been drawn completely; null if
-     *     incomplete; false otherwise
      */
-    drawGeometry: function(geometry, style, featureId) {
-        var className = geometry.CLASS_NAME;
-        var rendered = true;
-        if ((className == "OpenLayers.Geometry.Collection") ||
-            (className == "OpenLayers.Geometry.MultiPoint") ||
-            (className == "OpenLayers.Geometry.MultiLineString") ||
-            (className == "OpenLayers.Geometry.MultiPolygon")) {
-            for (var i = 0, len=geometry.components.length; i<len; i++) {
-                rendered = this.drawGeometry(
-                    geometry.components[i], style, featureId) && rendered;
+    setStyle: function(node, style, options, geometry) {
+        style = style  || node._style;
+        options = options || node._options;
+        var widthFactor = 1;
+        
+        if (node._geometryClass == "OpenLayers.Geometry.Point") {
+            if (style.externalGraphic) {
+                this.drawGraphic(node, geometry, style);
+        		if (style.graphicTitle) {
+                    node.title=style.graphicTitle;
+                } 
+                // modify style/options for fill and stroke styling below
+                style.fillColor = "none";
+                options.isStroked = false;
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                var cache = this.importSymbol(style.graphicName);
+                var symbolExtent = cache.extent;
+                var width = symbolExtent.getWidth();
+                var height = symbolExtent.getHeight();
+                node.setAttribute("path", cache.path);
+                node.setAttribute("coordorigin", symbolExtent.left + "," +
+                                                                symbolExtent.bottom);
+                node.setAttribute("coordsize", width + "," + height);
+                var ratio = width / height;
+                this.drawGraphic(node, geometry, style, ratio);
+            } else {
+                this.drawCircle(node, geometry, style.pointRadius);
             }
-            return rendered;
-        };
+        }
 
-        rendered = false;
-        if (style.display != "none") {
-            if (style.backgroundGraphic) {
-                this.redrawBackgroundNode(geometry.id, geometry, style,
-                    featureId);
+        // fill 
+        if (options.isFilled) { 
+            node.setAttribute("fillcolor", style.fillColor); 
+        } else { 
+            node.setAttribute("filled", "false"); 
+        }
+        var fills = node.getElementsByTagName("fill");
+        var fill = (fills.length == 0) ? null : fills[0];
+        if (!options.isFilled) {
+            if (fill) {
+                node.removeChild(fill);
             }
-            rendered = this.redrawNode(geometry.id, geometry, style,
-                featureId);
-        }
-        if (rendered == false) {
-            var node = document.getElementById(geometry.id);
-            if (node) {
-                if (node._style.backgroundGraphic) {
-                    node.parentNode.removeChild(document.getElementById(
-                        geometry.id + this.BACKGROUND_ID_SUFFIX));
+        } else {
+            if (!fill) {
+                fill = this.createNode('olv:fill', node.id + "_fill");
+            }
+            fill.setAttribute("opacity", style.fillOpacity);
+
+            if (node._geometryClass == "OpenLayers.Geometry.Point" &&
+                    style.externalGraphic) {
+
+                // override fillOpacity
+                if (style.graphicOpacity) {
+                    fill.setAttribute("opacity", style.graphicOpacity);
                 }
-                node.parentNode.removeChild(node);
+                
+                fill.setAttribute("src", style.externalGraphic);
+                fill.setAttribute("type", "frame");
+                
+                if (!(style.graphicWidth && style.graphicHeight)) {
+                  fill.aspect = "atmost";
+                }                
             }
+            if (fill.parentNode != node) {
+                node.appendChild(fill);
+            }
         }
-        return rendered;
+
+        // additional rendering for rotated graphics or symbols
+        if (typeof style.rotation != "undefined") {
+            if (style.externalGraphic) {
+                this.graphicRotate(node, xOffset, yOffset);
+                // make the fill fully transparent, because we now have
+                // the graphic as imagedata element. We cannot just remove
+                // the fill, because this is part of the hack described
+                // in graphicRotate
+                fill.setAttribute("opacity", 0);
+            } else {
+                node.style.rotation = style.rotation;
+            }
+        }
+
+        // stroke 
+        if (options.isStroked) { 
+            node.setAttribute("strokecolor", style.strokeColor); 
+            node.setAttribute("strokeweight", style.strokeWidth + "px"); 
+        } else { 
+            node.setAttribute("stroked", "false"); 
+        }
+        var strokes = node.getElementsByTagName("stroke");
+        var stroke = (strokes.length == 0) ? null : strokes[0];
+        if (!options.isStroked) {
+            if (stroke) {
+                node.removeChild(stroke);
+            }
+        } else {
+            if (!stroke) {
+                stroke = this.createNode('olv:stroke', node.id + "_stroke");
+                node.appendChild(stroke);
+            }
+            stroke.setAttribute("opacity", style.strokeOpacity);
+            stroke.setAttribute("endcap", !style.strokeLinecap || style.strokeLinecap == 'butt' ? 'flat' : style.strokeLinecap);
+            stroke.setAttribute("dashstyle", this.dashStyle(style));
+        }
+        
+        if (style.cursor != "inherit" && style.cursor != null) {
+            node.style.cursor = style.cursor;
+        }
+        return node;
     },
-    
+
     /**
-     * Method: redrawNode
+     * Method: graphicRotate
+     * If a point is to be styled with externalGraphic and rotation, VML fills
+     * cannot be used to display the graphic, because rotation of graphic
+     * fills is not supported by the VML implementation of Internet Explorer.
+     * This method creates a olv:imagedata element inside the VML node,
+     * DXImageTransform.Matrix and BasicImage filters for rotation and
+     * opacity, and a 3-step hack to remove rendering artefacts from the
+     * graphic and preserve the ability of graphics to trigger events.
+     * Finally, OpenLayers methods are used to determine the correct
+     * insertion point of the rotated image, because DXImageTransform.Matrix
+     * does the rotation without the ability to specify a rotation center
+     * point.
      * 
      * Parameters:
-     * id - {String}
-     * geometry - {<OpenLayers.Geometry>}
-     * style - {Object}
-     * featureId - {String}
-     * 
-     * Returns:
-     * {Boolean} true if the complete geometry could be drawn, null if parts of
-     *     the geometry could not be drawn, false otherwise
+     * node    - {DOMElement}
+     * xOffset - {Number} rotation center relative to image, x coordinate
+     * yOffset - {Number} rotation center relative to image, y coordinate
      */
-    redrawNode: function(id, geometry, style, featureId) {
-        // Get the node if it's already on the map.
-        var node = this.nodeFactory(id, this.getNodeType(geometry, style));
+    graphicRotate: function(node, xOffset, yOffset) {
+        var style = style || node._style;
+        var options = node._options;
         
-        // Set the data for the node, then draw it.
-        node._featureId = featureId;
-        node._geometry = geometry;
-        node._geometryClass = geometry.CLASS_NAME;
-        node._style = style;
-
-        var drawResult = this.drawGeometryNode(node, geometry, style);
-        if(drawResult === false) {
-            return false;
+        var aspectRatio, size;
+        if (!(style.graphicWidth && style.graphicHeight)) {
+            // load the image to determine its size
+            var img = new Image();
+            img.onreadystatechange = OpenLayers.Function.bind(function() {
+                if(img.readyState == "complete" ||
+                        img.readyState == "interactive") {
+                    aspectRatio = img.width / img.height;
+                    size = Math.max(style.pointRadius * 2, 
+                        style.graphicWidth || 0,
+                        style.graphicHeight || 0);
+                    xOffset = xOffset * aspectRatio;
+                    style.graphicWidth = size * aspectRatio;
+                    style.graphicHeight = size;
+                    this.graphicRotate(node, xOffset, yOffset);
+                }
+            }, this);
+            img.src = style.externalGraphic;
+            
+            // will be called again by the onreadystate handler
+            return;
+        } else {
+            size = Math.max(style.graphicWidth, style.graphicHeight);
+            aspectRatio = style.graphicWidth / style.graphicHeight;
         }
-         
-        node = drawResult.node;
         
-        // Insert the node into the indexer so it can show us where to
-        // place it. Note that this operation is O(log(n)). If there's a
-        // performance problem (when dragging, for instance) this is
-        // likely where it would be.
-        var insert = this.indexer ? this.indexer.insert(node) : null;
+        var width = Math.round(style.graphicWidth || size * aspectRatio);
+        var height = Math.round(style.graphicHeight || size);
+        node.style.width = width + "px";
+        node.style.height = height + "px";
+        
+        // Three steps are required to remove artefacts for images with
+        // transparent backgrounds (resulting from using DXImageTransform
+        // filters on svg objects), while preserving awareness for browser
+        // events on images:
+        // - Use the fill as usual (like for unrotated images) to handle
+        //   events
+        // - specify an imagedata element with the same src as the fill
+        // - style the imagedata element with an AlphaImageLoader filter
+        //   with empty src
+        var image = document.getElementById(node.id + "_image");
+        if (!image) {
+            image = this.createNode("olv:imagedata", node.id + "_image");
+            node.appendChild(image);
+        }
+        image.style.width = width + "px";
+        image.style.height = height + "px";
+        image.src = style.externalGraphic;
+        image.style.filter =
+            "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
+            "src='', sizingMethod='scale')";
 
-        if(insert) {
-            this.root.insertBefore(node, insert);
-        } else {
-            this.root.appendChild(node);
+        var rotation = style.rotation * Math.PI / 180;
+        var sintheta = Math.sin(rotation);
+        var costheta = Math.cos(rotation);
+
+        // do the rotation on the image
+        var filter =
+            "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
+            ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
+            ",SizingMethod='auto expand')\n";
+
+        // set the opacity (needed for the imagedata)
+        var opacity = style.graphicOpacity || style.fillOpacity;
+        if (opacity && opacity != 1) {
+            filter += 
+                "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
+                opacity+")\n";
         }
-        
-        this.postDraw(node);
-        
-        return drawResult.complete;
+        node.style.filter = filter;
+
+        // do the rotation again on a box, so we know the insertion point
+        var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
+        var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
+        imgBox.rotate(style.rotation, centerPoint);
+        var imgBounds = imgBox.getBounds();
+
+        node.style.left = Math.round(
+            parseInt(node.style.left) + imgBounds.left) + "px";
+        node.style.top = Math.round(
+            parseInt(node.style.top) - imgBounds.bottom) + "px";
     },
-    
+
     /**
-     * Method: redrawBackgroundNode
-     * Redraws the node using special 'background' style properties. Basically
-     *     just calls redrawNode(), but instead of directly using the 
-     *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and 
-     *     'graphicZIndex' properties directly from the specified 'style' 
-     *     parameter, we create a new style object and set those properties 
-     *     from the corresponding 'background'-prefixed properties from 
-     *     specified 'style' parameter.
+     * Method: postDraw
+     * Some versions of Internet Explorer seem to be unable to set fillcolor
+     * and strokecolor to "none" correctly before the fill node is appended to
+     * a visible vml node. This method takes care of that and sets fillcolor
+     * and strokecolor again if needed.
      * 
      * Parameters:
-     * id - {String}
-     * geometry - {<OpenLayers.Geometry>}
-     * style - {Object}
-     * featureId - {String}
-     * 
-     * Returns:
-     * {Boolean} true if the complete geometry could be drawn, null if parts of
-     *     the geometry could not be drawn, false otherwise
+     * node - {DOMElement}
      */
-    redrawBackgroundNode: function(id, geometry, style, featureId) {
-        var backgroundStyle = OpenLayers.Util.extend({}, style);
-        
-        // Set regular style attributes to apply to the background styles.
-        backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
-        backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
-        backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
-        backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
-        
-        // Erase background styles.
-        backgroundStyle.backgroundGraphic = null;
-        backgroundStyle.backgroundXOffset = null;
-        backgroundStyle.backgroundYOffset = null;
-        backgroundStyle.backgroundGraphicZIndex = null;
-        
-        return this.redrawNode(
-            id + this.BACKGROUND_ID_SUFFIX, 
-            geometry, 
-            backgroundStyle, 
-            null
-        );
+    postDraw: function(node) {
+        var fillColor = node._style.fillColor;
+        var strokeColor = node._style.strokeColor;
+        if (fillColor == "none" &&
+                node.getAttribute("fillcolor") != fillColor) {
+            node.setAttribute("fillcolor", fillColor);
+        }
+        if (strokeColor == "none" &&
+                node.getAttribute("strokecolor") != strokeColor) {
+            node.setAttribute("strokecolor", strokeColor);
+        }
     },
 
+
     /**
-     * Method: drawGeometryNode
-     * Given a node, draw a geometry on the specified layer.
-     *     node and geometry are required arguments, style is optional.
-     *     This method is only called by the render itself.
-     *
+     * Method: setNodeDimension
+     * Get the geometry's bounds, convert it to our vml coordinate system, 
+     * then set the node's position, size, and local coordinate system.
+     *   
      * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
+     */
+    setNodeDimension: function(node, geometry) {
+
+        var bbox = geometry.getBounds();
+        if(bbox) {
+            var resolution = this.getResolution();
+        
+            var scaledBox = 
+                new OpenLayers.Bounds((bbox.left/resolution - this.offset.x).toFixed(),
+                                      (bbox.bottom/resolution - this.offset.y).toFixed(),
+                                      (bbox.right/resolution - this.offset.x).toFixed(),
+                                      (bbox.top/resolution - this.offset.y).toFixed());
+            
+            // Set the internal coordinate system to draw the path
+            node.style.left = scaledBox.left + "px";
+            node.style.top = scaledBox.top + "px";
+            node.style.width = scaledBox.getWidth() + "px";
+            node.style.height = scaledBox.getHeight() + "px";
+    
+            node.coordorigin = scaledBox.left + " " + scaledBox.top;
+            node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
+        }
+    },
+    
+    /** 
+     * Method: dashStyle
+     * 
+     * Parameters:
      * style - {Object}
      * 
      * Returns:
-     * {Object} a hash with properties "node" (the drawn node) and "complete"
-     *     (null if parts of the geometry could not be drawn, false if nothing
-     *     could be drawn)
+     * {String} A VML compliant 'stroke-dasharray' value
      */
-    drawGeometryNode: function(node, geometry, style) {
-        style = style || node._style;
-        OpenLayers.Util.applyDefaults(style, this.minimumSymbolizer);
-
-        var options = {
-            'isFilled': true,
-            'isStroked': !!style.strokeWidth
-        };
-        var drawn;
-        switch (geometry.CLASS_NAME) {
-            case "OpenLayers.Geometry.Point":
-                drawn = this.drawPoint(node, geometry);
-                break;
-            case "OpenLayers.Geometry.LineString":
-                options.isFilled = false;
-                drawn = this.drawLineString(node, geometry);
-                break;
-            case "OpenLayers.Geometry.LinearRing":
-                drawn = this.drawLinearRing(node, geometry);
-                break;
-            case "OpenLayers.Geometry.Polygon":
-                drawn = this.drawPolygon(node, geometry);
-                break;
-            case "OpenLayers.Geometry.Surface":
-                drawn = this.drawSurface(node, geometry);
-                break;
-            case "OpenLayers.Geometry.Rectangle":
-                drawn = this.drawRectangle(node, geometry);
-                break;
+    dashStyle: function(style) {
+        var dash = style.strokeDashstyle;
+        switch (dash) {
+            case 'solid':
+            case 'dot':
+            case 'dash':
+            case 'dashdot':
+            case 'longdash':
+            case 'longdashdot':
+                return dash;
             default:
-                break;
+                // very basic guessing of dash style patterns
+                var parts = dash.split(/[ ,]/);
+                if (parts.length == 2) {
+                    if (1*parts[0] >= 2*parts[1]) {
+                        return "longdash";
+                    }
+                    return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
+                } else if (parts.length == 4) {
+                    return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
+                        "dashdot";
+                }
+                return "solid";
         }
+    },
 
-        node._style = style; 
-        node._options = options; 
-
-        //set style
-        //TBD simplify this
-        if (drawn != false) {
-            return {
-                node: this.setStyle(node, style, options, geometry),
-                complete: drawn
-            };
-        } else {
-            return false;
+    /**
+     * Method: createNode
+     * Create a new node
+     *
+     * Parameters:
+     * type - {String} Kind of node to draw
+     * id - {String} Id for node
+     *
+     * Returns:
+     * {DOMElement} A new node of the given type and id
+     */
+    createNode: function(type, id) {
+        var node = document.createElement(type);
+        if (id) {
+            node.setAttribute('id', id);
         }
+        
+        // IE hack to make elements unselectable, to prevent 'blue flash'
+        // while dragging vectors; #1410
+        node.setAttribute('unselectable', 'on', 0);
+        node.onselectstart = function() { return(false); };
+        
+        return node;    
     },
     
     /**
-     * Method: postDraw
-     * Things that have do be done after the geometry node is appended
-     *     to its parent node. To be overridden by subclasses.
-     * 
+     * Method: nodeTypeCompare
+     * Determine whether a node is of a given type
+     *
      * Parameters:
-     * node - {DOMElement}
+     * node - {DOMElement} An VML element
+     * type - {String} Kind of node
+     *
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
      */
-    postDraw: function(node) {},
+    nodeTypeCompare: function(node, type) {
+
+        //split type
+        var subType = type;
+        var splitIndex = subType.indexOf(":");
+        if (splitIndex != -1) {
+            subType = subType.substr(splitIndex+1);
+        }
+
+        //split nodeName
+        var nodeName = node.nodeName;
+        splitIndex = nodeName.indexOf(":");
+        if (splitIndex != -1) {
+            nodeName = nodeName.substr(splitIndex+1);
+        }
+
+        return (subType == nodeName);
+    },
+
+    /**
+     * Method: createRenderRoot
+     * Create the renderer root
+     *
+     * Returns:
+     * {DOMElement} The specific render engine's root element
+     */
+    createRenderRoot: function() {
+        return this.nodeFactory(this.container.id + "_vmlRoot", "div");
+    },
+
+    /**
+     * Method: createRoot
+     * Create the main root element
+     *
+     * Returns:
+     * {DOMElement} The main root element to which we'll add vectors
+     */
+    createRoot: function() {
+        return this.nodeFactory(this.container.id + "_root", "olv:group");
+    },
     
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+    
     /**
      * Method: drawPoint
-     * Virtual function for drawing Point Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Render a point
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
      * 
      * Returns:
-     * {DOMElement} or false if the renderer could not draw the point
-     */ 
-    drawPoint: function(node, geometry) {},
+     * {DOMElement} or false if the point could not be drawn
+     */
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
 
     /**
-     * Method: drawLineString
-     * Virtual function for drawing LineString Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Method: drawCircle
+     * Render a circle.
+     * Size and Center a circle given geometry (x,y center) and radius
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
+     * radius - {float}
      * 
      * Returns:
-     * {DOMElement} or null if the renderer could not draw all components of
-     *     the linestring, or false if nothing could be drawn
-     */ 
-    drawLineString: function(node, geometry) {},
+     * {DOMElement} or false if the circle could not ne drawn
+     */
+    drawCircle: function(node, geometry, radius) {
+        if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
+            var resolution = this.getResolution();
 
+            node.style.left = ((geometry.x /resolution - this.offset.x).toFixed() - radius) + "px";
+            node.style.top = ((geometry.y /resolution - this.offset.y).toFixed() - radius) + "px";
+    
+            var diameter = radius * 2;
+            
+            node.style.width = diameter + "px";
+            node.style.height = diameter + "px";
+            return node;
+        }
+        return false;
+    },
+
     /**
-     * Method: drawLinearRing
-     * Virtual function for drawing LinearRing Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Method: drawGraphic
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or null if the renderer could not draw all components
-     *     of the linear ring, or false if nothing could be drawn
-     */ 
-    drawLinearRing: function(node, geometry) {},
+     * style - {Object}
+     * ratio - {Number}
+     */
+    drawGraphic: function(node, geometry, style, ratio) {
+        var diameter = style.pointRadius * 2;
+        var width = style.graphicWidth || diameter;
+        var height = style.graphicHeight || (ratio ? diameter / ratio : diameter);
 
+        var resolution = this.getResolution();
+        var xOffset = (style.graphicXOffset != undefined) ?
+            style.graphicXOffset : -(0.5 * width);
+        var yOffset = (style.graphicYOffset != undefined) ?
+            style.graphicYOffset : -(0.5 * height);
+        
+        node.style.left = ((geometry.x/resolution - this.offset.x)+xOffset).toFixed();
+        node.style.top = ((geometry.y/resolution - this.offset.y)-(yOffset+height)).toFixed();
+        node.style.width = width + "px";
+        node.style.height = height + "px";
+        node.style.flip = "y";
+    },
+    
     /**
-     * Method: drawPolygon
-     * Virtual function for drawing Polygon Geometry. 
-     *    Should be implemented by subclasses.
-     *    This method is only called by the renderer itself.
+     * Method: drawLineString
+     * Render a linestring.
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
      * 
      * Returns:
-     * {DOMElement} or null if the renderer could not draw all components
-     *     of the polygon, or false if nothing could be drawn
-     */ 
-    drawPolygon: function(node, geometry) {},
+     * {DOMElement}
+     */
+    drawLineString: function(node, geometry) {
+        return this.drawLine(node, geometry, false);
+    },
 
     /**
-     * Method: drawRectangle
-     * Virtual function for drawing Rectangle Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Method: drawLinearRing
+     * Render a linearring
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
      * 
      * Returns:
-     * {DOMElement} or false if the renderer could not draw the rectangle
-     */ 
-    drawRectangle: function(node, geometry) {},
+     * {DOMElement}
+     */
+    drawLinearRing: function(node, geometry) {
+        return this.drawLine(node, geometry, true);
+    },
 
     /**
-     * Method: drawCircle
-     * Virtual function for drawing Circle Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Method: DrawLine
+     * Render a line.
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
+     * closeLine - {Boolean} Close the line? (make it a ring?)
      * 
      * Returns:
-     * {DOMElement} or false if the renderer could not draw the circle
-     */ 
-    drawCircle: function(node, geometry) {},
+     * {DOMElement}
+     */
+    drawLine: function(node, geometry, closeLine) {
 
+        this.setNodeDimension(node, geometry);
+
+        var resolution = this.getResolution();
+        var numComponents = geometry.components.length;
+        var parts = new Array(numComponents);
+
+        var comp, x, y;
+        for (var i = 0; i < numComponents; i++) {
+            comp = geometry.components[i];
+            x = (comp.x/resolution - this.offset.x);
+            y = (comp.y/resolution - this.offset.y);
+            parts[i] = " " + x.toFixed() + "," + y.toFixed() + " l ";
+        }
+        var end = (closeLine) ? " x e" : " e";
+        node.path = "m" + parts.join("") + end;
+        return node;
+    },
+
     /**
-     * Method: drawSurface
-     * Virtual function for drawing Surface Geometry. 
-     *     Should be implemented by subclasses.
-     *     This method is only called by the renderer itself.
+     * Method: drawPolygon
+     * Render a polygon
      * 
-     * Parameters: 
+     * Parameters:
      * node - {DOMElement}
      * geometry - {<OpenLayers.Geometry>}
      * 
      * Returns:
-     * {DOMElement} or false if the renderer could not draw the surface
-     */ 
-    drawSurface: function(node, geometry) {},
+     * {DOMElement}
+     */
+    drawPolygon: function(node, geometry) {
+        this.setNodeDimension(node, geometry);
 
+        var resolution = this.getResolution();
+    
+        var path = [];
+        var linearRing, i, j, len, ilen, comp, x, y;
+        for (j = 0, len=geometry.components.length; j<len; j++) {
+            linearRing = geometry.components[j];
+
+            path.push("m");
+            for (i=0, ilen=linearRing.components.length; i<ilen; i++) {
+                comp = linearRing.components[i];
+                x = comp.x / resolution - this.offset.x;
+                y = comp.y / resolution - this.offset.y;
+                path.push(" " + x.toFixed() + "," + y.toFixed());
+                if (i==0) {
+                    path.push(" l");
+                }
+            }
+            path.push(" x ");
+        }
+        path.push("e");
+        node.path = path.join("");
+        return node;
+    },
+
     /**
-     * Method: getFeatureIdFromEvent
+     * Method: drawRectangle
+     * Render a rectangle
      * 
      * Parameters:
-     * evt - {Object} An <OpenLayers.Event> object
-     *
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
      * Returns:
-     * {<OpenLayers.Geometry>} A geometry from an event that 
-     *     happened on a layer.
+     * {DOMElement}
      */
-    getFeatureIdFromEvent: function(evt) {
-        var target = evt.target;
-        var useElement = target && target.correspondingUseElement;
-        var node = useElement ? useElement : (target || evt.srcElement);
-        var featureId = node._featureId;
-        return featureId;
+    drawRectangle: function(node, geometry) {
+        var resolution = this.getResolution();
+    
+        node.style.left = (geometry.x/resolution - this.offset.x) + "px";
+        node.style.top = (geometry.y/resolution - this.offset.y) + "px";
+        node.style.width = geometry.width/resolution + "px";
+        node.style.height = geometry.height/resolution + "px";
+        
+        return node;
     },
 
-    /** 
-     * Method: eraseGeometry
-     * Erase a geometry from the renderer. In the case of a multi-geometry, 
-     *     we cycle through and recurse on ourselves. Otherwise, we look for a 
-     *     node with the geometry.id, destroy its geometry, and remove it from
-     *     the DOM.
+    /**
+     * Method: drawText
+     * This method is only called by the renderer itself.
      * 
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
+     * Parameters: 
+     * featureId - {String}
+     * style -
+     * location - {<OpenLayers.Geometry.Point>}
      */
-    eraseGeometry: function(geometry) {
-        if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
-            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
-            (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
-            (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
-            for (var i=0, len=geometry.components.length; i<len; i++) {
-                this.eraseGeometry(geometry.components[i]);
-            }
-        } else {    
-            var element = OpenLayers.Util.getElement(geometry.id);
-            if (element && element.parentNode) {
-                if (element.geometry) {
-                    element.geometry.destroy();
-                    element.geometry = null;
-                }
-                element.parentNode.removeChild(element);
+    drawText: function(featureId, style, location) {
+        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:oval");
+        
+        var resolution = this.getResolution();
+        label.style.left = (location.x /resolution - this.offset.x).toFixed() + "px";
+        label.style.top = (location.y /resolution - this.offset.y).toFixed() + "px";
+        label.style.flip = "y";
 
-                if (this.indexer) {
-                    this.indexer.remove(element);
-                }
-                
-                if (element._style.backgroundGraphic) {
-                    var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
-                    var bElem = OpenLayers.Util.getElement(backgroundId);
-                    if (bElem && bElem.parentNode) {
-                        // No need to destroy the geometry since the element and the background
-                        // node share the same geometry.
-                        bElem.parentNode.removeChild(bElem);
-                    }
-                }
-            }
+        // FIXME: we need to use v:textpath & friends in order to get text 
+        // alignment working
+        var textbox = this.createNode("olv:textbox");
+
+        var span = document.createElement('span');
+        if (style.fillColor) {
+            span.style.color = style.fontColor;
         }
+        if (style.fontFamily) {
+            span.style.fontFamily = style.fontFamily;
+        }
+        if (style.fontSize) {
+            span.style.fontSize = style.fontSize;
+        }
+        if (style.fontWeight) {
+            span.style.fontWeight = style.fontWeight;
+        }
+
+        span.innerHTML = style.label;
+
+        textbox.appendChild(span);
+
+        // prevent label duplication
+        if (label.firstChild) {
+            label.removeChild(label.firstChild);
+        }
+
+        label.appendChild(textbox);
+        this.root.appendChild(label);
     },
 
-    /** 
-     * Method: nodeFactory
-     * Create new node of the specified type, with the (optional) specified id.
+    /**
+     * Method: drawSurface
      * 
-     * If node already exists with same ID and a different type, we remove it
-     *     and then call ourselves again to recreate it.
-     * 
      * Parameters:
-     * id - {String}
-     * type - {String} type Kind of node to draw.
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
      * 
      * Returns:
-     * {DOMElement} A new node of the given type and id.
+     * {DOMElement}
      */
-    nodeFactory: function(id, type) {
-        var node = OpenLayers.Util.getElement(id);
-        if (node) {
-            if (!this.nodeTypeCompare(node, type)) {
-                node.parentNode.removeChild(node);
-                node = this.nodeFactory(id, type);
+    drawSurface: function(node, geometry) {
+
+        this.setNodeDimension(node, geometry);
+
+        var resolution = this.getResolution();
+    
+        var path = [];
+        var comp, x, y;
+        for (var i=0, len=geometry.components.length; i<len; i++) {
+            comp = geometry.components[i];
+            x = comp.x / resolution - this.offset.x;
+            y = comp.y / resolution - this.offset.y;
+            if ((i%3)==0 && (i/3)==0) {
+                path.push("m");
+            } else if ((i%3)==1) {
+                path.push(" c");
             }
-        } else {
-            node = this.createNode(type, id);
+            path.push(" " + x + "," + y);
         }
+        path.push(" x e");
+
+        node.path = path.join("");
         return node;
     },
     
-    /** 
-     * Method: nodeTypeCompare
+    /**
+     * Method: moveRoot
+     * moves this renderer's root to a different renderer.
      * 
      * Parameters:
-     * node - {DOMElement}
-     * type - {String} Kind of node
+     * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
+     * root - {DOMElement} optional root node. To be used when this renderer
+     *     holds roots from multiple layers to tell this method which one to
+     *     detach
      * 
      * Returns:
-     * {Boolean} Whether or not the specified node is of the specified type
-     *     This function must be overridden by subclasses.
+     * {Boolean} true if successful, false otherwise
      */
-    nodeTypeCompare: function(node, type) {},
+    moveRoot: function(renderer) {
+        var layer = this.map.getLayer(this.container.id);
+        layer && this.clear();
+        OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
+        layer && layer.redraw();
+    },
     
-    /** 
-     * Method: createNode
+    /**
+     * Method: importSymbol
+     * add a new symbol definition from the rendererer's symbol hash
      * 
      * Parameters:
-     * type - {String} Kind of node to draw.
-     * id - {String} Id for node.
+     * graphicName - {String} name of the symbol to import
      * 
      * Returns:
-     * {DOMElement} A new node of the given type and id.
-     *     This function must be overridden by subclasses.
-     */
-    createNode: function(type, id) {},
-
-    /**
-     * Method: isComplexSymbol
-     * Determines if a symbol cannot be rendered using drawCircle
-     * 
-     * Parameters:
-     * graphicName - {String}
-     * 
-     * Returns
-     * {Boolean} true if the symbol is complex, false if not
-     */
-    isComplexSymbol: function(graphicName) {
-        return (graphicName != "circle") && !!graphicName;
-    },
-
-    CLASS_NAME: "OpenLayers.Renderer.Elements"
-});
-
-
-/**
- * Constant: OpenLayers.Renderer.symbol
- * Coordinate arrays for well known (named) symbols.
- */
-OpenLayers.Renderer.symbol = {
-    "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
-            303,215, 231,161, 321,161, 350,75],
-    "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
-            4,0],
-    "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
-    "square": [0,0, 0,1, 1,1, 1,0, 0,0],
-    "triangle": [0,10, 10,10, 5,0, 0,10]
-};
-/* ======================================================================
-    OpenLayers/Request/XMLHttpRequest.js
-   ====================================================================== */
-
-// Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
-//
-// 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.
-
-/**
- * @requires OpenLayers/Request.js
- */
-
-(function () {
-
-    // Save reference to earlier defined object implementation (if any)
-    var oXMLHttpRequest    = window.XMLHttpRequest;
-
-    // Define on browser type
-    var bGecko    = !!window.controllers,
-        bIE        = window.document.all && !window.opera;
-
-    // Constructor
-    function cXMLHttpRequest() {
-        this._object    = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP');
-    };
-
-    // BUGFIX: Firefox with Firebug installed would break pages if not executed
-    if (bGecko && oXMLHttpRequest.wrapped)
-        cXMLHttpRequest.wrapped    = oXMLHttpRequest.wrapped;
-
-    // Constants
-    cXMLHttpRequest.UNSENT                = 0;
-    cXMLHttpRequest.OPENED                = 1;
-    cXMLHttpRequest.HEADERS_RECEIVED    = 2;
-    cXMLHttpRequest.LOADING                = 3;
-    cXMLHttpRequest.DONE                = 4;
-
-    // Public Properties
-    cXMLHttpRequest.prototype.readyState    = cXMLHttpRequest.UNSENT;
-    cXMLHttpRequest.prototype.responseText    = "";
-    cXMLHttpRequest.prototype.responseXML    = null;
-    cXMLHttpRequest.prototype.status        = 0;
-    cXMLHttpRequest.prototype.statusText    = "";
-
-    // Instance-level Events Handlers
-    cXMLHttpRequest.prototype.onreadystatechange    = null;
-
-    // Class-level Events Handlers
-    cXMLHttpRequest.onreadystatechange    = null;
-    cXMLHttpRequest.onopen                = null;
-    cXMLHttpRequest.onsend                = null;
-    cXMLHttpRequest.onabort                = null;
-
-    // Public Methods
-    cXMLHttpRequest.prototype.open    = function(sMethod, sUrl, bAsync, sUser, sPassword) {
-
-        // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
-        this._async        = bAsync;
-
-        // Set the onreadystatechange handler
-        var oRequest    = this,
-            nState        = this.readyState;
-
-        // BUGFIX: IE - memory leak on page unload (inter-page leak)
-        if (bIE) {
-            var fOnUnload    = function() {
-                if (oRequest._object.readyState != cXMLHttpRequest.DONE)
-                    fCleanTransport(oRequest);
-            };
-            if (bAsync)
-                window.attachEvent("onunload", fOnUnload);
+     * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
+     */      
+    importSymbol: function (graphicName)  {
+        var id = this.container.id + "-" + graphicName;
+        
+        // check if symbol already exists in the cache
+        var cache = this.symbolCache[id];
+        if (cache) {
+            return cache;
         }
-
-        this._object.onreadystatechange    = function() {
-            if (bGecko && !bAsync)
-                return;
-
-            // Synchronize state
-            oRequest.readyState        = oRequest._object.readyState;
-
-            //
-            fSynchronizeValues(oRequest);
-
-            // BUGFIX: Firefox fires unneccesary DONE when aborting
-            if (oRequest._aborted) {
-                // Reset readyState to UNSENT
-                oRequest.readyState    = cXMLHttpRequest.UNSENT;
-
-                // Return now
-                return;
-            }
-
-            if (oRequest.readyState == cXMLHttpRequest.DONE) {
-                //
-                fCleanTransport(oRequest);
-// Uncomment this block if you need a fix for IE cache
-/*
-                // BUGFIX: IE - cache issue
-                if (!oRequest._object.getResponseHeader("Date")) {
-                    // Save object to cache
-                    oRequest._cached    = oRequest._object;
-
-                    // Instantiate a new transport object
-                    cXMLHttpRequest.call(oRequest);
-
-                    // Re-send request
-                    oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
-                    oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
-                    // Copy headers set
-                    if (oRequest._headers)
-                        for (var sHeader in oRequest._headers)
-                            if (typeof oRequest._headers[sHeader] == "string")    // Some frameworks prototype objects with functions
-                                oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
-
-                    oRequest._object.onreadystatechange    = function() {
-                        // Synchronize state
-                        oRequest.readyState        = oRequest._object.readyState;
-
-                        if (oRequest._aborted) {
-                            //
-                            oRequest.readyState    = cXMLHttpRequest.UNSENT;
-
-                            // Return
-                            return;
-                        }
-
-                        if (oRequest.readyState == cXMLHttpRequest.DONE) {
-                            // Clean Object
-                            fCleanTransport(oRequest);
-
-                            // get cached request
-                            if (oRequest.status == 304)
-                                oRequest._object    = oRequest._cached;
-
-                            //
-                            delete oRequest._cached;
-
-                            //
-                            fSynchronizeValues(oRequest);
-
-                            //
-                            fReadyStateChange(oRequest);
-
-                            // BUGFIX: IE - memory leak in interrupted
-                            if (bIE && bAsync)
-                                window.detachEvent("onunload", fOnUnload);
-                        }
-                    };
-                    oRequest._object.send(null);
-
-                    // Return now - wait untill re-sent request is finished
-                    return;
-                };
-*/
-                // BUGFIX: IE - memory leak in interrupted
-                if (bIE && bAsync)
-                    window.detachEvent("onunload", fOnUnload);
-            }
-
-            // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
-            if (nState != oRequest.readyState)
-                fReadyStateChange(oRequest);
-
-            nState    = oRequest.readyState;
-        };
-
-        // Add method sniffer
-        if (cXMLHttpRequest.onopen)
-            cXMLHttpRequest.onopen.apply(this, arguments);
-
-        this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
-
-        // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
-        if (!bAsync && bGecko) {
-            this.readyState    = cXMLHttpRequest.OPENED;
-
-            fReadyStateChange(this);
+        
+        var symbol = OpenLayers.Renderer.symbol[graphicName];
+        if (!symbol) {
+            throw new Error(graphicName + ' is not a valid symbol name');
+            return;
         }
-    };
-    cXMLHttpRequest.prototype.send    = function(vData) {
-        // Add method sniffer
-        if (cXMLHttpRequest.onsend)
-            cXMLHttpRequest.onsend.apply(this, arguments);
 
-        // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
-        // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
-        // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
-        if (vData && vData.nodeType) {
-            vData    = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
-            if (!this._headers["Content-Type"])
-                this._object.setRequestHeader("Content-Type", "application/xml");
-        }
+        var symbolExtent = new OpenLayers.Bounds(
+                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
+        
+        var pathitems = ["m"];
+        for (var i=0; i<symbol.length; i=i+2) {
+            x = symbol[i];
+            y = symbol[i+1];
+            symbolExtent.left = Math.min(symbolExtent.left, x);
+            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
+            symbolExtent.right = Math.max(symbolExtent.right, x);
+            symbolExtent.top = Math.max(symbolExtent.top, y);
 
-        this._object.send(vData);
-
-        // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
-        if (bGecko && !this._async) {
-            this.readyState    = cXMLHttpRequest.OPENED;
-
-            // Synchronize state
-            fSynchronizeValues(this);
-
-            // Simulate missing states
-            while (this.readyState < cXMLHttpRequest.DONE) {
-                this.readyState++;
-                fReadyStateChange(this);
-                // Check if we are aborted
-                if (this._aborted)
-                    return;
+            pathitems.push(x);
+            pathitems.push(y);
+            if (i == 0) {
+                pathitems.push("l");
             }
         }
-    };
-    cXMLHttpRequest.prototype.abort    = function() {
-        // Add method sniffer
-        if (cXMLHttpRequest.onabort)
-            cXMLHttpRequest.onabort.apply(this, arguments);
-
-        // BUGFIX: Gecko - unneccesary DONE when aborting
-        if (this.readyState > cXMLHttpRequest.UNSENT)
-            this._aborted    = true;
-
-        this._object.abort();
-
-        // BUGFIX: IE - memory leak
-        fCleanTransport(this);
-    };
-    cXMLHttpRequest.prototype.getAllResponseHeaders    = function() {
-        return this._object.getAllResponseHeaders();
-    };
-    cXMLHttpRequest.prototype.getResponseHeader    = function(sName) {
-        return this._object.getResponseHeader(sName);
-    };
-    cXMLHttpRequest.prototype.setRequestHeader    = function(sName, sValue) {
-        // BUGFIX: IE - cache issue
-        if (!this._headers)
-            this._headers    = {};
-        this._headers[sName]    = sValue;
-
-        return this._object.setRequestHeader(sName, sValue);
-    };
-    cXMLHttpRequest.prototype.toString    = function() {
-        return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
-    };
-    cXMLHttpRequest.toString    = function() {
-        return '[' + "XMLHttpRequest" + ']';
-    };
-
-    // Helper function
-    function fReadyStateChange(oRequest) {
-        // Execute onreadystatechange
-        if (oRequest.onreadystatechange)
-            oRequest.onreadystatechange.apply(oRequest);
-
-        // Sniffing code
-        if (cXMLHttpRequest.onreadystatechange)
-            cXMLHttpRequest.onreadystatechange.apply(oRequest);
-    };
-
-    function fGetDocument(oRequest) {
-        var oDocument    = oRequest.responseXML;
-        // Try parsing responseText
-        if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
-            oDocument    = new ActiveXObject('Microsoft.XMLDOM');
-            oDocument.loadXML(oRequest.responseText);
-        }
-        // Check if there is no error in document
-        if (oDocument)
-            if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
-                return null;
-        return oDocument;
-    };
-
-    function fSynchronizeValues(oRequest) {
-        try {    oRequest.responseText    = oRequest._object.responseText;    } catch (e) {}
-        try {    oRequest.responseXML    = fGetDocument(oRequest._object);    } catch (e) {}
-        try {    oRequest.status            = oRequest._object.status;            } catch (e) {}
-        try {    oRequest.statusText        = oRequest._object.statusText;        } catch (e) {}
-    };
-
-    function fCleanTransport(oRequest) {
-        // BUGFIX: IE - memory leak (on-page leak)
-        oRequest._object.onreadystatechange    = new window.Function;
-
-        // Delete private properties
-        delete oRequest._headers;
-    };
-
-    // Internet Explorer 5.0 (missing apply)
-    if (!window.Function.prototype.apply) {
-        window.Function.prototype.apply    = function(oRequest, oArguments) {
-            if (!oArguments)
-                oArguments    = [];
-            oRequest.__func    = this;
-            oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
-            delete oRequest.__func;
+        pathitems.push("x e");
+        var path = pathitems.join(" ");
+        
+        cache = {
+            path: path,
+            extent: symbolExtent
         };
-    };
-
-    // Register new object with window
-    /**
-     * Class: OpenLayers.Request.XMLHttpRequest
-     * Standard-compliant (W3C) cross-browser implementation of the
-     *     XMLHttpRequest object.  From
-     *     http://code.google.com/p/xmlhttprequest/.
-     */
-    OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
-})();
+        this.symbolCache[id] = cache;
+        
+        return cache;
+    },
+    
+    CLASS_NAME: "OpenLayers.Renderer.VML"
+});
 /* ======================================================================
     OpenLayers/Tile.js
    ====================================================================== */
@@ -10124,6 +12008,7 @@
 
 /*
  * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Console.js
  */
 
 /*
@@ -10397,686 +12282,6 @@
     CLASS_NAME: "OpenLayers.Tile"
 });
 /* ======================================================================
-    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. */
-
-/**
- * @requires OpenLayers/Request/XMLHttpRequest.js
- */
-
-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
- *
- */
-
-
-/**
- * Function: OpenLayers.nullHandler
- * @param {} request
- */
-OpenLayers.nullHandler = function(request) {
-    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
-};
-
-/** 
- * APIFunction: loadURL
- * Background load a document.  For more flexibility in using XMLHttpRequest,
- *     see the <OpenLayers.Request> methods.
- *
- * Parameters:
- * uri - {String} URI of source doc
- * params - {String} or {Object} GET params. Either a string in the form
- *     "?hello=world&foo=bar" (do not forget the leading question mark)
- *     or an object in the form {'hello': 'world', 'foo': 'bar}
- * 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.  Note that if you do not specify an onComplete
- *     function, <OpenLayers.nullHandler> will be called (which pops up a 
- *     user friendly error message dialog).
- * 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.  Note that if you do not
- *     specify an onComplete function, <OpenLayers.nullHandler> will be called
- *     (which pops up a user friendly error message dialog).
- *
- * Returns:
- * {<OpenLayers.Request.XMLHttpRequest>}  The request object. To abort loading,
- *     call request.abort().
- */
-OpenLayers.loadURL = function(uri, params, caller,
-                                  onComplete, onFailure) {
-    
-    if(typeof params == 'string') {
-        params = OpenLayers.Util.getParameters(params);
-    }
-    var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
-    var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
-    
-    return OpenLayers.Request.GET({
-        url: uri, params: params,
-        success: success, failure: failure, scope: caller
-    });
-};
-
-/** 
- * 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
- * *Deprecated*.  Use <OpenLayers.Request> method instead.
- *
- * 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] = extras[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/Handler.js
    ====================================================================== */
 
@@ -11377,6 +12582,7 @@
  * @requires OpenLayers/Util.js
  * @requires OpenLayers/Events.js
  * @requires OpenLayers/Tween.js
+ * @requires OpenLayers/Console.js
  */
 
 /**
@@ -11441,8 +12647,6 @@
      *  - *move* triggered after each drag, pan, or zoom
      *  - *moveend* triggered after a drag, pan, or zoom completes
      *  - *zoomend* triggered after a 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
@@ -11492,10 +12696,37 @@
      *                       events on the map
      */
     events: null,
+    
+    /**
+     * APIProperty: allOverlays
+     * {Boolean} Allow the map to function with "overlays" only.  Defaults to
+     *     false.  If true, the lowest layer in the draw order will act as
+     *     the base layer.  In addition, if set to true, all layers will
+     *     have isBaseLayer set to false when they are added to the map.
+     *
+     * Note:
+     * If you set map.allOverlays to true, then you *cannot* use
+     *     map.setBaseLayer or layer.setIsBaseLayer.  With allOverlays true,
+     *     the lowest layer in the draw layer is the base layer.  So, to change
+     *     the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
+     *     index to 0.
+     */
+    allOverlays: false,
 
     /**
      * APIProperty: div
-     * {DOMElement} The element that contains the map
+     * {DOMElement|String} The element that contains the map (or an id for
+     *     that element).  If the <OpenLayers.Map> constructor is called
+     *     with two arguments, this should be provided as the first argument.
+     *     Alternatively, the map constructor can be called with the options
+     *     object as the only argument.  In this case (one argument), a
+     *     div property may or may not be provided.  If the div property
+     *     is not provided, the map can be rendered to a container later
+     *     using the <render> method.
+     *     
+     * Note: If you calling <render> after map construction, do not use
+     *     <maxResolution>  auto.  Instead, divide your <maxExtent> by your
+     *     maximum expected dimension.
      */
     div: null,
     
@@ -11738,7 +12969,7 @@
     eventListeners: null,
 
     /**
-     * Property: panMethod
+     * APIProperty: panMethod
      * {Function} The Easing function to be used for tweening.  Default is
      * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
      * animated panning.
@@ -11746,6 +12977,15 @@
     panMethod: OpenLayers.Easing.Expo.easeOut,
     
     /**
+     * Property: panDuration
+     * {Integer} The number of steps to be passed to the
+     * OpenLayers.Tween.start() method when the map is
+     * panned.
+     * Default is 50.
+     */
+    panDuration: 50,
+    
+    /**
      * Property: paddingForPopups
      * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent 
      *     the popup from getting too close to the map border.
@@ -11754,13 +12994,16 @@
     
     /**
      * Constructor: OpenLayers.Map
-     * Constructor for a new OpenLayers.Map instance.
+     * Constructor for a new OpenLayers.Map instance.  There are two possible
+     *     ways to call the map constructor.  See the examples below.
      *
      * Parameters:
      * div - {String} Id of an element in your page that will contain the map.
+     *     May be omitted if the <div> option is provided or if you intend
+     *     to use <render> later.
      * options - {Object} Optional object with properties to tag onto the map.
      *
-     * Examples:
+     * Examples (method one):
      * (code)
      * // create a map with default options in an element with the id "map1"
      * var map = new OpenLayers.Map("map1");
@@ -11774,8 +13017,33 @@
      * };
      * var map = new OpenLayers.Map("map2", options);
      * (end)
+     *
+     * Examples (method two - single argument):
+     * (code)
+     * // create a map with non-default options
+     * var map = new OpenLayers.Map({
+     *     div: "map_id",
+     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
+     *     maxResolution: 156543,
+     *     units: 'm',
+     *     projection: "EPSG:41001"
+     * });
+     *
+     * // create a map without a reference to a container - call render later
+     * var map = new OpenLayers.Map({
+     *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
+     *     maxResolution: 156543,
+     *     units: 'm',
+     *     projection: "EPSG:41001"
+     * });
      */    
     initialize: function (div, options) {
+        
+        // If only one argument is provided, check if it is an object.
+        if(arguments.length === 1 && typeof div === "object") {
+            options = div;
+            div = options && options.div;
+        }
 
         // Simple-type defaults are set in class definition. 
         //  Now set complex-type defaults 
@@ -11795,6 +13063,12 @@
         this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
 
         this.div = OpenLayers.Util.getElement(div);
+        if(!this.div) {
+            this.div = document.createElement("div");
+            this.div.style.height = "1px";
+            this.div.style.width = "1px";
+        }
+        
         OpenLayers.Element.addClass(this.div, 'olMap');
 
         // the viewPortDiv is the outermost div we modify
@@ -11890,8 +13164,24 @@
 
         // always call map.destroy()
         OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
-
     },
+    
+    /**
+     * APIMethod: render
+     * Render the map to a specified container.
+     * 
+     * Parameters:
+     * div - {String|DOMElement} The container that the map should be rendered
+     *     to. If different than the current container, the map viewport
+     *     will be moved from the current to the new container.
+     */
+    render: function(div) {
+        this.div = OpenLayers.Util.getElement(div);
+        this.events.attachToElement(this.div);
+        this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
+        this.div.appendChild(this.viewPortDiv);
+        this.updateSize();
+    },
 
     /**
      * Method: unloadDestroy
@@ -12180,7 +13470,10 @@
                 OpenLayers.Console.warn(msg);
                 return false;
             }
-        }    
+        }
+        if(this.allOverlays) {
+            layer.isBaseLayer = false;
+        }
 
         this.events.triggerEvent("preaddlayer", {layer: layer});
         
@@ -12196,7 +13489,7 @@
         this.layers.push(layer);
         layer.setMap(this);
 
-        if (layer.isBaseLayer)  {
+        if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer))  {
             if (this.baseLayer == null) {
                 // set the first baselaye we add as the baselayer
                 this.setBaseLayer(layer);
@@ -12270,7 +13563,7 @@
             if(setNewBaseLayer) {
                 for(var i=0, len=this.layers.length; i<len; i++) {
                     var iLayer = this.layers[i];
-                    if (iLayer.isBaseLayer) {
+                    if (iLayer.isBaseLayer || this.allOverlays) {
                         this.setBaseLayer(iLayer);
                         break;
                     }
@@ -12335,6 +13628,13 @@
             this.events.triggerEvent("changelayer", {
                 layer: layer, property: "order"
             });
+            if(this.allOverlays) {
+                if(idx === 0) {
+                    this.setBaseLayer(layer);
+                } else if(this.baseLayer !== this.layers[0]) {
+                    this.setBaseLayer(this.layers[0]);
+                }
+            }
         }
     },
 
@@ -12783,7 +14083,7 @@
                 lon: lonlat.lon,
                 lat: lonlat.lat
             };
-            this.panTween.start(from, to, 50, {
+            this.panTween.start(from, to, this.panDuration, {
                 callbacks: {
                     start: OpenLayers.Function.bind(function(lonlat) {
                         this.events.triggerEvent("movestart");
@@ -12811,10 +14111,11 @@
 
     /**
      * APIMethod: setCenter
+     * Set the map center (and optionally, the zoom level).
      * 
      * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     * zoom - {Integer}
+     * lonlat - {<OpenLayers.LonLat>} The new center location.
+     * zoom - {Integer} Optional zoom level.
      * dragging - {Boolean} Specifies whether or not to trigger 
      *                      movestart/end events
      * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom 
@@ -12945,7 +14246,7 @@
             
             for (var i=0, len=this.layers.length; i<len; i++) {
                 var layer = this.layers[i];
-                if (!layer.isBaseLayer) {
+                if (layer !== this.baseLayer && !layer.isBaseLayer) {
                     var inRange = layer.calculateInRange();
                     if (layer.inRange != inRange) {
                         // the inRange property has changed. If the layer is
@@ -13837,1894 +15138,341 @@
     
 
 /* ======================================================================
-    OpenLayers/Popup/AnchoredBubble.js
+    OpenLayers/Request.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/Popup/Anchored.js
+ * @requires OpenLayers/Events.js
  */
 
 /**
- * Class: OpenLayers.Popup.AnchoredBubble
- * 
- * Inherits from: 
- *  - <OpenLayers.Popup.Anchored>
+ * Namespace: OpenLayers.Request
+ * The OpenLayers.Request namespace contains convenience methods for working
+ *     with XMLHttpRequests.  These methods work with a cross-browser
+ *     W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
  */
-OpenLayers.Popup.AnchoredBubble = 
-  OpenLayers.Class(OpenLayers.Popup.Anchored, {
-
-    /**
-     * Property: rounded
-     * {Boolean} Has the popup been rounded yet?
-     */
-    rounded: false, 
+OpenLayers.Request = {
     
-    /** 
-     * Constructor: OpenLayers.Popup.AnchoredBubble
-     * 
-     * Parameters:
-     * id - {String}
-     * lonlat - {<OpenLayers.LonLat>}
-     * contentSize - {<OpenLayers.Size>}
-     * contentHTML - {String}
-     * anchor - {Object} Object to which we'll anchor the popup. Must expose 
-     *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
-     *     (Note that this is generally an <OpenLayers.Icon>).
-     * closeBox - {Boolean}
-     * closeBoxCallback - {Function} Function to be called on closeBox click.
-     */
-    initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
-                        closeBoxCallback) {
-        
-        this.padding = new OpenLayers.Bounds(
-            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
-            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
-        );
-        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
-    },
-
-    /** 
-     * Method: draw
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {DOMElement} Reference to a div that contains the drawn popup.
-     */
-    draw: function(px) {
-        
-        OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
-
-        this.setContentHTML();
-        
-        //set the popup color and opacity           
-        this.setBackgroundColor(); 
-        this.setOpacity();
-
-        return this.div;
-    },
-
     /**
-     * Method: updateRelativePosition
-     * The popup has been moved to a new relative location, in which case
-     *     we will want to re-do the rico corners.
+     * Constant: DEFAULT_CONFIG
+     * {Object} Default configuration for all requests.
      */
-    updateRelativePosition: function() {
-        this.setRicoCorners();
+    DEFAULT_CONFIG: {
+        method: "GET",
+        url: window.location.href,
+        async: true,
+        user: undefined,
+        password: undefined,
+        params: null,
+        proxy: OpenLayers.ProxyHost,
+        headers: {},
+        data: null,
+        callback: function() {},
+        success: null,
+        failure: null,
+        scope: null
     },
-
-    /**
-     * APIMethod: setSize
-     * 
-     * Parameters:
-     * contentSize - {<OpenLayers.Size>} the new size for the popup's 
-     *     contents div (in pixels).
-     */
-    setSize:function(contentSize) { 
-        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
-
-        this.setRicoCorners();
-    },  
-
-    /**
-     * APIMethod: setBackgroundColor
-     * 
-     * Parameters:
-     * color - {String}
-     */
-    setBackgroundColor:function(color) { 
-        if (color != undefined) {
-            this.backgroundColor = color; 
-        }
-        
-        if (this.div != null) {
-            if (this.contentDiv != null) {
-                this.div.style.background = "transparent";
-                OpenLayers.Rico.Corner.changeColor(this.groupDiv, 
-                                                   this.backgroundColor);
-            }
-        }
-    },  
     
     /**
-     * APIMethod: setOpacity
+     * APIProperty: events
+     * {<OpenLayers.Events>} An events object that handles all 
+     *     events on the {<OpenLayers.Request>} object.
+     *
+     * All event listeners will receive an event object with three properties:
+     * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
+     * config - {Object} The config object sent to the specific request method.
+     * requestUrl - {String} The request url.
      * 
-     * Parameters: 
-     * opacity - {float}
+     * Supported event types:
+     * complete - Triggered when we have a response from the request, if a
+     *     listener returns false, no further response processing will take
+     *     place.
+     * success - Triggered when the HTTP response has a success code (200-299).
+     * failure - Triggered when the HTTP response does not have a success code.
      */
-    setOpacity:function(opacity) { 
-        OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
-        
-        if (this.div != null) {
-            if (this.groupDiv != null) {
-                OpenLayers.Rico.Corner.changeOpacity(this.groupDiv, 
-                                                     this.opacity);
-            }
-        }
-    },  
- 
-    /** 
-     * Method: setBorder
-     * Always sets border to 0. Bubble Popups can not have a border.
-     * 
-     * Parameters:
-     * border - {Integer}
-     */
-    setBorder:function(border) { 
-        this.border = 0;
-    },      
- 
-    /** 
-     * Method: setRicoCorners
-     * Update RICO corners according to the popup's current relative postion.
-     */
-    setRicoCorners:function() {
+    events: new OpenLayers.Events(this, null, ["complete", "success", "failure"]),
     
-        var corners = this.getCornersToRound(this.relativePosition);
-        var options = {corners: corners,
-                         color: this.backgroundColor,
-                       bgColor: "transparent",
-                         blend: false};
-
-        if (!this.rounded) {
-            OpenLayers.Rico.Corner.round(this.div, options);
-            this.rounded = true;
-        } else {
-            OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
-            //set the popup color and opacity
-            this.setBackgroundColor(); 
-            this.setOpacity();
-        }
-    },
-
-    /** 
-     * Method: getCornersToRound
-     *  
-     * Returns:
-     * {String} The proper corners string ("tr tl bl br") for rico to round.
-     */
-    getCornersToRound:function() {
-
-        var corners = ['tl', 'tr', 'bl', 'br'];
-
-        //we want to round all the corners _except_ the opposite one. 
-        var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
-        OpenLayers.Util.removeItem(corners, corner);
-
-        return corners.join(" ");
-    },
-
-    CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
-});
-
-/**
- * Constant: CORNER_SIZE
- * {Integer} 5. Border space for the RICO corners.
- */
-OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
-
-/* ======================================================================
-    OpenLayers/Renderer/SVG.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/Renderer/Elements.js
- */
-
-/**
- * Class: OpenLayers.Renderer.SVG
- * 
- * Inherits:
- *  - <OpenLayers.Renderer.Elements>
- */
-OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
-
-    /** 
-     * Property: xmlns
-     * {String}
-     */
-    xmlns: "http://www.w3.org/2000/svg",
-    
     /**
-     * Property: xlinkns
-     * {String}
-     */
-    xlinkns: "http://www.w3.org/1999/xlink",
-
-    /**
-     * Constant: MAX_PIXEL
-     * {Integer} Firefox has a limitation where values larger or smaller than  
-     *           about 15000 in an SVG document lock the browser up. This 
-     *           works around it.
-     */
-    MAX_PIXEL: 15000,
-
-    /**
-     * Property: translationParameters
-     * {Object} Hash with "x" and "y" properties
-     */
-    translationParameters: null,
-    
-    /**
-     * Property: symbolSize
-     * {Object} Cache for symbol sizes according to their svg coordinate space
-     */
-    symbolSize: {},
-
-    /**
-     * Constructor: OpenLayers.Renderer.SVG
-     * 
+     * APIMethod: issue
+     * Create a new XMLHttpRequest object, open it, set any headers, bind
+     *     a callback to done state, and send any data.  It is recommended that
+     *     you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
+     *     This method is only documented to provide detail on the configuration
+     *     options available to all request methods.
+     *
      * Parameters:
-     * containerID - {String}
-     */
-    initialize: function(containerID) {
-        if (!this.supported()) { 
-            return; 
-        }
-        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
-                                                                arguments);
-        this.translationParameters = {x: 0, y: 0};
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
-    },
-    
-    /**
-     * APIMethod: supported
-     * 
-     * Returns:
-     * {Boolean} Whether or not the browser supports the SVG renderer
-     */
-    supported: function() {
-        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
-        return (document.implementation && 
-           (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
-            document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
-            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
-    },    
-
-    /**
-     * Method: inValidRange
-     * See #669 for more information
+     * config - {Object} Object containing properties for configuring the
+     *     request.  Allowed configuration properties are described below.
+     *     This object is modified and should not be reused.
      *
-     * Parameters:
-     * x      - {Integer}
-     * y      - {Integer}
-     * xyOnly - {Boolean} whether or not to just check for x and y, which means
-     *     to not take the current translation parameters into account if true.
-     * 
+     * Allowed config properties:
+     * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
+     *     OPTIONS.  Default is GET.
+     * url - {String} URL for the request.
+     * async - {Boolean} Open an asynchronous request.  Default is true.
+     * user - {String} User for relevant authentication scheme.  Set
+     *     to null to clear current user.
+     * password - {String} Password for relevant authentication scheme.
+     *     Set to null to clear current password.
+     * proxy - {String} Optional proxy.  Defaults to
+     *     <OpenLayers.ProxyHost>.
+     * params - {Object} Any key:value pairs to be appended to the
+     *     url as a query string.  Assumes url doesn't already include a query
+     *     string or hash.  Typically, this is only appropriate for <GET>
+     *     requests where the query string will be appended to the url.
+     *     Parameter values that are arrays will be
+     *     concatenated with a comma (note that this goes against form-encoding)
+     *     as is done with <OpenLayers.Util.getParameterString>.
+     * headers - {Object} Object with header:value pairs to be set on
+     *     the request.
+     * data - {String | Document} Optional data to send with the request.
+     *     Typically, this is only used with <POST> and <PUT> requests.
+     *     Make sure to provide the appropriate "Content-Type" header for your
+     *     data.  For <POST> and <PUT> requests, the content type defaults to
+     *     "application-xml".  If your data is a different content type, or
+     *     if you are using a different HTTP method, set the "Content-Type"
+     *     header to match your data type.
+     * callback - {Function} Function to call when request is done.
+     *     To determine if the request failed, check request.status (200
+     *     indicates success).
+     * success - {Function} Optional function to call if request status is in
+     *     the 200s.  This will be called in addition to callback above and
+     *     would typically only be used as an alternative.
+     * failure - {Function} Optional function to call if request status is not
+     *     in the 200s.  This will be called in addition to callback above and
+     *     would typically only be used as an alternative.
+     * scope - {Object} If callback is a public method on some object,
+     *     set the scope to that object.
+     *
      * Returns:
-     * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
-     *           valid range.
-     */ 
-    inValidRange: function(x, y, xyOnly) {
-        var left = x + (xyOnly ? 0 : this.translationParameters.x);
-        var top = y + (xyOnly ? 0 : this.translationParameters.y);
-        return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
-                top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
-    },
-
-    /**
-     * Method: setExtent
-     * 
-     * Parameters:
-     * extent - {<OpenLayers.Bounds>}
-     * resolutionChanged - {Boolean}
-     * 
-     * Returns:
-     * {Boolean} true to notify the layer that the new extent does not exceed
-     *     the coordinate range, and the features will not need to be redrawn.
-     *     False otherwise.
+     * {XMLHttpRequest} Request object.  To abort the request before a response
+     *     is received, call abort() on the request object.
      */
-    setExtent: function(extent, resolutionChanged) {
-        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
-                                                               arguments);
-        
-        var resolution = this.getResolution();
-        var left = -extent.left / resolution;
-        var top = extent.top / resolution;
+    issue: function(config) {        
+        // apply default config - proxy host may have changed
+        var defaultConfig = OpenLayers.Util.extend(
+            this.DEFAULT_CONFIG,
+            {proxy: OpenLayers.ProxyHost}
+        );
+        config = OpenLayers.Util.applyDefaults(config, defaultConfig);
 
-        // If the resolution has changed, start over changing the corner, because
-        // the features will redraw.
-        if (resolutionChanged) {
-            this.left = left;
-            this.top = top;
-            // Set the viewbox
-            var extentString = "0 0 " + this.size.w + " " + this.size.h;
-
-            this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
-            this.translate(0, 0);
-            return true;
-        } else {
-            var inRange = this.translate(left - this.left, top - this.top);
-            if (!inRange) {
-                // recenter the coordinate system
-                this.setExtent(extent, true);
+        // create request, open, and set headers
+        var request = new OpenLayers.Request.XMLHttpRequest();
+        var url = config.url;
+        if(config.params) {
+            var paramString = OpenLayers.Util.getParameterString(config.params);
+            if(paramString.length > 0) {
+                var separator = (url.indexOf('?') > -1) ? '&' : '?';
+                url += separator + paramString;
             }
-            return inRange;
         }
-    },
-    
-    /**
-     * Method: translate
-     * Transforms the SVG coordinate system
-     * 
-     * Parameters:
-     * x - {Float}
-     * y - {Float}
-     * 
-     * Returns:
-     * {Boolean} true if the translation parameters are in the valid coordinates
-     *     range, false otherwise.
-     */
-    translate: function(x, y) {
-        if (!this.inValidRange(x, y, true)) {
-            return false;
-        } else {
-            var transformString = "";
-            if (x || y) {
-                transformString = "translate(" + x + "," + y + ")";
-            }
-            this.root.setAttributeNS(null, "transform", transformString);
-            this.translationParameters = {x: x, y: y};
-            return true;
+        if(config.proxy && (url.indexOf("http") == 0)) {
+            url = config.proxy + encodeURIComponent(url);
         }
-    },
-
-    /**
-     * Method: setSize
-     * Sets the size of the drawing surface.
-     * 
-     * Parameters:
-     * size - {<OpenLayers.Size>} The size of the drawing surface
-     */
-    setSize: function(size) {
-        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
-        
-        this.rendererRoot.setAttributeNS(null, "width", this.size.w);
-        this.rendererRoot.setAttributeNS(null, "height", this.size.h);
-    },
-
-    /** 
-     * Method: getNodeType 
-     * 
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     * style - {Object}
-     * 
-     * Returns:
-     * {String} The corresponding node type for the specified geometry
-     */
-    getNodeType: function(geometry, style) {
-        var nodeType = null;
-        switch (geometry.CLASS_NAME) {
-            case "OpenLayers.Geometry.Point":
-                if (style.externalGraphic) {
-                    nodeType = "image";
-                } else if (this.isComplexSymbol(style.graphicName)) {
-                    nodeType = "use";
-                } else {
-                    nodeType = "circle";
-                }
-                break;
-            case "OpenLayers.Geometry.Rectangle":
-                nodeType = "rect";
-                break;
-            case "OpenLayers.Geometry.LineString":
-                nodeType = "polyline";
-                break;
-            case "OpenLayers.Geometry.LinearRing":
-                nodeType = "polygon";
-                break;
-            case "OpenLayers.Geometry.Polygon":
-            case "OpenLayers.Geometry.Curve":
-            case "OpenLayers.Geometry.Surface":
-                nodeType = "path";
-                break;
-            default:
-                break;
+        request.open(
+            config.method, url, config.async, config.user, config.password
+        );
+        for(var header in config.headers) {
+            request.setRequestHeader(header, config.headers[header]);
         }
-        return nodeType;
-    },
 
-    /** 
-     * Method: setStyle
-     * Use to set all the style attributes to a SVG node.
-     * 
-     * Takes care to adjust stroke width and point radius to be
-     * resolution-relative
-     *
-     * Parameters:
-     * node - {SVGDomElement} An SVG element to decorate
-     * style - {Object}
-     * options - {Object} Currently supported options include 
-     *                              'isFilled' {Boolean} and
-     *                              'isStroked' {Boolean}
-     */
-    setStyle: function(node, style, options) {
-        style = style  || node._style;
-        options = options || node._options;
-        var r = parseFloat(node.getAttributeNS(null, "r"));
-        var widthFactor = 1;
-        var pos;
-        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
-            if (style.externalGraphic) {
-                pos = this.getPosition(node);
-                
-                if (style.graphicWidth && style.graphicHeight) {
-                  node.setAttributeNS(null, "preserveAspectRatio", "none");
-                }
-                var width = style.graphicWidth || style.graphicHeight;
-                var height = style.graphicHeight || style.graphicWidth;
-                width = width ? width : style.pointRadius*2;
-                height = height ? height : style.pointRadius*2;
-                var xOffset = (style.graphicXOffset != undefined) ?
-                    style.graphicXOffset : -(0.5 * width);
-                var yOffset = (style.graphicYOffset != undefined) ?
-                    style.graphicYOffset : -(0.5 * height);
-
-                var opacity = style.graphicOpacity || style.fillOpacity;
-                
-                node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
-                node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
-                node.setAttributeNS(null, "width", width);
-                node.setAttributeNS(null, "height", height);
-                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
-                node.setAttributeNS(null, "style", "opacity: "+opacity);
-            } else if (this.isComplexSymbol(style.graphicName)) {
-                // the symbol viewBox is three times as large as the symbol
-                var offset = style.pointRadius * 3;
-                var size = offset * 2;
-                var id = this.importSymbol(style.graphicName);
-                var href = "#" + id;
-                pos = this.getPosition(node);
-                widthFactor = this.symbolSize[id] / size;
-                // Only set the href if it is different from the current one.
-                // This is a workaround for strange rendering behavior in FF3.
-                if (node.getAttributeNS(this.xlinkns, "href") != href) {
-                    node.setAttributeNS(this.xlinkns, "href", href);
-                } else if (size != parseFloat(node.getAttributeNS(null, "width"))) {
-                    // hide the element (and force a reflow so it really gets
-                    // hidden. This workaround is needed for Safari.
-                    node.style.visibility = "hidden";
-                    this.container.scrollLeft = this.container.scrollLeft;
-                }
-                node.setAttributeNS(null, "width", size);
-                node.setAttributeNS(null, "height", size);
-                node.setAttributeNS(null, "x", pos.x - offset);
-                node.setAttributeNS(null, "y", pos.y - offset);
-                // set the visibility back to normal (after the Safari
-                // workaround above)
-                node.style.visibility = "";
-            } else {
-                node.setAttributeNS(null, "r", style.pointRadius);
-            }
-
-            if (typeof style.rotation != "undefined" && pos) {
-                var rotation = OpenLayers.String.format(
-                    "rotate(${0} ${1} ${2})", [style.rotation, pos.x, pos.y]);
-                node.setAttributeNS(null, "transform", rotation);
-            }
-        }
+        // bind callbacks to readyState 4 (done)
+        var complete = (config.scope) ?
+            OpenLayers.Function.bind(config.callback, config.scope) :
+            config.callback;
         
-        if (options.isFilled) {
-            node.setAttributeNS(null, "fill", style.fillColor);
-            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
-        } else {
-            node.setAttributeNS(null, "fill", "none");
+        // optional success callback
+        var success;
+        if(config.success) {
+            success = (config.scope) ?
+                OpenLayers.Function.bind(config.success, config.scope) :
+                config.success;
         }
 
-        if (options.isStroked) {
-            node.setAttributeNS(null, "stroke", style.strokeColor);
-            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
-            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
-            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap);
-            // Hard-coded linejoin for now, to make it look the same as in VML.
-            // There is no strokeLinejoin property yet for symbolizers.
-            node.setAttributeNS(null, "stroke-linejoin", "round");
-            node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style,
-                widthFactor));
-        } else {
-            node.setAttributeNS(null, "stroke", "none");
+        // optional failure callback
+        var failure;
+        if(config.failure) {
+            failure = (config.scope) ?
+                OpenLayers.Function.bind(config.failure, config.scope) :
+                config.failure;
         }
         
-        if (style.pointerEvents) {
-            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
-        }
-        
-        if (style.cursor != null) {
-            node.setAttributeNS(null, "cursor", style.cursor);
-        }
-        return node;
-    },
-
-    /** 
-     * Method: dashStyle
-     * 
-     * Parameters:
-     * style - {Object}
-     * widthFactor - {Number}
-     * 
-     * Returns:
-     * {String} A SVG compliant 'stroke-dasharray' value
-     */
-    dashStyle: function(style, widthFactor) {
-        var w = style.strokeWidth * widthFactor;
-
-        switch (style.strokeDashstyle) {
-            case 'solid':
-                return 'none';
-            case 'dot':
-                return [1, 4 * w].join();
-            case 'dash':
-                return [4 * w, 4 * w].join();
-            case 'dashdot':
-                return [4 * w, 4 * w, 1, 4 * w].join();
-            case 'longdash':
-                return [8 * w, 4 * w].join();
-            case 'longdashdot':
-                return [8 * w, 4 * w, 1, 4 * w].join();
-            default:
-                return style.strokeDashstyle.replace(/ /g, ",");
-        }
-    },
-    
-    /** 
-     * Method: createNode
-     * 
-     * Parameters:
-     * type - {String} Kind of node to draw
-     * id - {String} Id for node
-     * 
-     * Returns:
-     * {DOMElement} A new node of the given type and id
-     */
-    createNode: function(type, id) {
-        var node = document.createElementNS(this.xmlns, type);
-        if (id) {
-            node.setAttributeNS(null, "id", id);
-        }
-        return node;    
-    },
-    
-    /** 
-     * Method: nodeTypeCompare
-     * 
-     * Parameters:
-     * node - {SVGDomElement} An SVG element
-     * type - {String} Kind of node
-     * 
-     * Returns:
-     * {Boolean} Whether or not the specified node is of the specified type
-     */
-    nodeTypeCompare: function(node, type) {
-        return (type == node.nodeName);
-    },
-   
-    /**
-     * Method: createRenderRoot
-     * 
-     * Returns:
-     * {DOMElement} The specific render engine's root element
-     */
-    createRenderRoot: function() {
-        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
-    },
-
-    /**
-     * Method: createRoot
-     * 
-     * Returns:
-     * {DOMElement} The main root element to which we'll add vectors
-     */
-    createRoot: function() {
-        return this.nodeFactory(this.container.id + "_root", "g");
-    },
-
-    /**
-     * Method: createDefs
-     *
-     * Returns:
-     * {DOMElement} The element to which we'll add the symbol definitions
-     */
-    createDefs: function() {
-        var defs = this.nodeFactory("ol-renderer-defs", "defs");
-        this.rendererRoot.appendChild(defs);
-        return defs;
-    },
-
-    /**************************************
-     *                                    *
-     *     GEOMETRY DRAWING FUNCTIONS     *
-     *                                    *
-     **************************************/
-
-    /**
-     * Method: drawPoint
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or false if the renderer could not draw the point
-     */ 
-    drawPoint: function(node, geometry) {
-        return this.drawCircle(node, geometry, 1);
-    },
-
-    /**
-     * Method: drawCircle
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * radius - {Float}
-     * 
-     * Returns:
-     * {DOMElement} or false if the renderer could not draw the circle
-     */
-    drawCircle: function(node, geometry, radius) {
-        var resolution = this.getResolution();
-        var x = (geometry.x / resolution + this.left);
-        var y = (this.top - geometry.y / resolution);
-
-        if (this.inValidRange(x, y)) { 
-            node.setAttributeNS(null, "cx", x);
-            node.setAttributeNS(null, "cy", y);
-            node.setAttributeNS(null, "r", radius);
-            return node;
-        } else {
-            return false;
-        }    
-            
-    },
-    
-    /**
-     * Method: drawLineString
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or null if the renderer could not draw all components of
-     *     the linestring, or false if nothing could be drawn
-     */ 
-    drawLineString: function(node, geometry) {
-        var componentsResult = this.getComponentsString(geometry.components);
-        if (componentsResult.path) {
-            node.setAttributeNS(null, "points", componentsResult.path);
-            return (componentsResult.complete ? node : null);  
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Method: drawLinearRing
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or null if the renderer could not draw all components
-     *     of the linear ring, or false if nothing could be drawn
-     */ 
-    drawLinearRing: function(node, geometry) {
-        var componentsResult = this.getComponentsString(geometry.components);
-        if (componentsResult.path) {
-            node.setAttributeNS(null, "points", componentsResult.path);
-            return (componentsResult.complete ? node : null);  
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Method: drawPolygon
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or null if the renderer could not draw all components
-     *     of the polygon, or false if nothing could be drawn
-     */ 
-    drawPolygon: function(node, geometry) {
-        var d = "";
-        var draw = true;
-        var complete = true;
-        var linearRingResult, path;
-        for (var j=0, len=geometry.components.length; j<len; j++) {
-            d += " M";
-            linearRingResult = this.getComponentsString(
-                geometry.components[j].components, " ");
-            path = linearRingResult.path;
-            if (path) {
-                d += " " + path;
-                complete = linearRingResult.complete && complete;
-            } else {
-                draw = false;
-            }
-        }
-        d += " z";
-        if (draw) {
-            node.setAttributeNS(null, "d", d);
-            node.setAttributeNS(null, "fill-rule", "evenodd");
-            return complete ? node : null;
-        } else {
-            return false;
-        }    
-    },
-    
-    /**
-     * Method: drawRectangle
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or false if the renderer could not draw the rectangle
-     */ 
-    drawRectangle: function(node, geometry) {
-        var resolution = this.getResolution();
-        var x = (geometry.x / resolution + this.left);
-        var y = (this.top - geometry.y / resolution);
-
-        if (this.inValidRange(x, y)) { 
-            node.setAttributeNS(null, "x", x);
-            node.setAttributeNS(null, "y", y);
-            node.setAttributeNS(null, "width", geometry.width / resolution);
-            node.setAttributeNS(null, "height", geometry.height / resolution);
-            return node;
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Method: drawSurface
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or false if the renderer could not draw the surface
-     */ 
-    drawSurface: function(node, geometry) {
-
-        // create the svg path string representation
-        var d = null;
-        var draw = true;
-        for (var i=0, len=geometry.components.length; i<len; i++) {
-            if ((i%3) == 0 && (i/3) == 0) {
-                var component = this.getShortString(geometry.components[i]);
-                if (!component) { draw = false; }
-                d = "M " + component;
-            } else if ((i%3) == 1) {
-                var component = this.getShortString(geometry.components[i]);
-                if (!component) { draw = false; }
-                d += " C " + component;
-            } else {
-                var component = this.getShortString(geometry.components[i]);
-                if (!component) { draw = false; }
-                d += " " + component;
-            }
-        }
-        d += " Z";
-        if (draw) {
-            node.setAttributeNS(null, "d", d);
-            return node;
-        } else {
-            return false;
-        }    
-    },
-
-    /** 
-     * Method: getComponentString
-     * 
-     * Parameters:
-     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
-     * separator - {String} character between coordinate pairs. Defaults to ","
-     * 
-     * Returns:
-     * {Object} hash with properties "path" (the string created from the
-     *     components and "complete" (false if the renderer was unable to
-     *     draw all components)
-     */
-    getComponentsString: function(components, separator) {
-        var renderCmp = [];
-        var complete = true;
-        var len = components.length;
-        var strings = [];
-        var str, component, j;
-        for(var i=0; i<len; i++) {
-            component = components[i];
-            renderCmp.push(component);
-            str = this.getShortString(component);
-            if (str) {
-                strings.push(str);
-            } else {
-                // The current component is outside the valid range. Let's
-                // see if the previous or next component is inside the range.
-                // If so, add the coordinate of the intersection with the
-                // valid range bounds.
-                if (i > 0) {
-                    if (this.getShortString(components[i - 1])) {
-                        strings.push(this.clipLine(components[i],
-                            components[i-1]));
+        var events = this.events;
+         
+        request.onreadystatechange = function() {
+            if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
+                var proceed = events.triggerEvent(
+                    "complete",
+                    {request: request, config: config, requestUrl: url}
+                );
+                if(proceed !== false) {
+                    complete(request);
+                    if (!request.status || (request.status >= 200 && request.status < 300)) {
+                        events.triggerEvent(
+                            "success",
+                            {request: request, config: config, requestUrl: url}
+                        );
+                        if(success) {
+                            success(request);
+                        }
                     }
-                }
-                if (i < len - 1) {
-                    if (this.getShortString(components[i + 1])) {
-                        strings.push(this.clipLine(components[i],
-                            components[i+1]));
+                    if(request.status && (request.status < 200 || request.status >= 300)) {                    
+                        events.triggerEvent(
+                            "failure",
+                            {request: request, config: config, requestUrl: url}
+                        );
+                        if(failure) {
+                            failure(request);
+                        }
                     }
                 }
-                complete = false;
             }
-        }
-
-        return {
-            path: strings.join(separator || ","),
-            complete: complete
         };
-    },
-    
-    /**
-     * Method: clipLine
-     * Given two points (one inside the valid range, and one outside),
-     * clips the line betweeen the two points so that the new points are both
-     * inside the valid range.
-     * 
-     * Parameters:
-     * badComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
-     *     invalid point
-     * goodComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
-     *     valid point
-     * Returns
-     * {String} the SVG coordinate pair of the clipped point (like
-     *     getShortString), or an empty string if both passed componets are at
-     *     the same point.
-     */
-    clipLine: function(badComponent, goodComponent) {
-        if (goodComponent.equals(badComponent)) {
-            return "";
-        }
-        var resolution = this.getResolution();
-        var maxX = this.MAX_PIXEL - this.translationParameters.x;
-        var maxY = this.MAX_PIXEL - this.translationParameters.y;
-        var x1 = goodComponent.x / resolution + this.left;
-        var y1 = this.top - goodComponent.y / resolution;
-        var x2 = badComponent.x / resolution + this.left;
-        var y2 = this.top - badComponent.y / resolution;
-        var k;
-        if (x2 < -maxX || x2 > maxX) {
-            k = (y2 - y1) / (x2 - x1);
-            x2 = x2 < 0 ? -maxX : maxX;
-            y2 = y1 + (x2 - x1) * k;
-        }
-        if (y2 < -maxY || y2 > maxY) {
-            k = (x2 - x1) / (y2 - y1);
-            y2 = y2 < 0 ? -maxY : maxY;
-            x2 = x1 + (y2 - y1) * k;
-        }
-        return x2 + "," + y2;
-    },
-
-    /** 
-     * Method: getShortString
-     * 
-     * Parameters:
-     * point - {<OpenLayers.Geometry.Point>}
-     * 
-     * Returns:
-     * {String} or false if point is outside the valid range
-     */
-    getShortString: function(point) {
-        var resolution = this.getResolution();
-        var x = (point.x / resolution + this.left);
-        var y = (this.top - point.y / resolution);
-
-        if (this.inValidRange(x, y)) { 
-            return x + "," + y;
+        
+        // send request (optionally with data) and return
+        // call in a timeout for asynchronous requests so the return is
+        // available before readyState == 4 for cached docs
+        if(config.async === false) {
+            request.send(config.data);
         } else {
-            return false;
+            window.setTimeout(function(){
+                request.send(config.data);
+            }, 0);
         }
+        return request;
     },
     
     /**
-     * Method: getPosition
-     * Finds the position of an svg node.
-     * 
+     * APIMethod: GET
+     * Send an HTTP GET request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to GET.
+     *
      * Parameters:
-     * node - {DOMElement}
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.
+     *     This object is modified and should not be reused.
      * 
      * Returns:
-     * {Object} hash with x and y properties, representing the coordinates
-     *     within the svg coordinate system
+     * {XMLHttpRequest} Request object.
      */
-    getPosition: function(node) {
-        return({
-            x: parseFloat(node.getAttributeNS(null, "cx")),
-            y: parseFloat(node.getAttributeNS(null, "cy"))
-        });
+    GET: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "GET"});
+        return OpenLayers.Request.issue(config);
     },
-
-    /**
-     * Method: importSymbol
-     * add a new symbol definition from the rendererer's symbol hash
-     * 
-     * Parameters:
-     * graphicName - {String} name of the symbol to import
-     * 
-     * Returns:
-     * {String} - id of the imported symbol
-     */      
-    importSymbol: function (graphicName)  {
-        if (!this.defs) {
-            // create svg defs tag
-            this.defs = this.createDefs();
-        }
-        var id = this.container.id + "-" + graphicName;
-        
-        // check if symbol already exists in the defs
-        if (document.getElementById(id) != null) {
-            return id;
-        }
-        
-        var symbol = OpenLayers.Renderer.symbol[graphicName];
-        if (!symbol) {
-            throw new Error(graphicName + ' is not a valid symbol name');
-            return;
-        }
-
-        var symbolNode = this.nodeFactory(id, "symbol");
-        var node = this.nodeFactory(null, "polygon");
-        symbolNode.appendChild(node);
-        var symbolExtent = new OpenLayers.Bounds(
-                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
-
-        var points = "";
-        var x,y;
-        for (var i=0; i<symbol.length; i=i+2) {
-            x = symbol[i];
-            y = symbol[i+1];
-            symbolExtent.left = Math.min(symbolExtent.left, x);
-            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
-            symbolExtent.right = Math.max(symbolExtent.right, x);
-            symbolExtent.top = Math.max(symbolExtent.top, y);
-            points += " " + x + "," + y;
-        }
-        
-        node.setAttributeNS(null, "points", points);
-        
-        var width = symbolExtent.getWidth();
-        var height = symbolExtent.getHeight();
-        // create a viewBox three times as large as the symbol itself,
-        // to allow for strokeWidth being displayed correctly at the corners.
-        var viewBox = [symbolExtent.left - width,
-                        symbolExtent.bottom - height, width * 3, height * 3];
-        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
-        this.symbolSize[id] = Math.max(width, height) * 3;
-        
-        this.defs.appendChild(symbolNode);
-        return symbolNode.id;
-    },
-
-    CLASS_NAME: "OpenLayers.Renderer.SVG"
-});
-/* ======================================================================
-    OpenLayers/Renderer/VML.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/Renderer/Elements.js
- */
-
-/**
- * Class: OpenLayers.Renderer.VML
- * Render vector features in browsers with VML capability.  Construct a new
- * VML renderer with the <OpenLayers.Renderer.VML> constructor.
- * 
- * Note that for all calculations in this class, we use toFixed() to round a 
- * float value to an integer. This is done because it seems that VML doesn't 
- * support float values.
- *
- * Inherits from:
- *  - <OpenLayers.Renderer.Elements>
- */
-OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
-
-    /**
-     * Property: xmlns
-     * {String} XML Namespace URN
-     */
-    xmlns: "urn:schemas-microsoft-com:vml",
     
     /**
-     * Property: symbolCache
-     * {DOMElement} node holding symbols. This hash is keyed by symbol name,
-     *     and each value is a hash with a "path" and an "extent" property.
-     */
-    symbolCache: {},
-
-    /**
-     * Property: offset
-     * {Object} Hash with "x" and "y" properties
-     */
-    offset: null,
-    
-    /**
-     * Constructor: OpenLayers.Renderer.VML
-     * Create a new VML renderer.
+     * APIMethod: POST
+     * Send a POST request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to POST and "Content-Type" header set to "application/xml".
      *
      * Parameters:
-     * containerID - {String} The id for the element that contains the renderer
-     */
-    initialize: function(containerID) {
-        if (!this.supported()) { 
-            return; 
-        }
-        if (!document.namespaces.olv) {
-            document.namespaces.add("olv", this.xmlns);
-            var style = document.createStyleSheet();
-            style.addRule('olv\\:*', "behavior: url(#default#VML); " +
-                                   "position: absolute; display: inline-block;");
-        }
-        
-        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
-                                                                arguments);
-        this.offset = {x: 0, y: 0};
-    },
-
-    /**
-     * APIMethod: destroy
-     * Deconstruct the renderer.
-     */
-    destroy: function() {
-        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
-    },
-
-    /**
-     * APIMethod: supported
-     * Determine whether a browser supports this renderer.
-     *
-     * Returns:
-     * {Boolean} The browser supports the VML renderer
-     */
-    supported: function() {
-        return !!(document.namespaces);
-    },    
-
-    /**
-     * Method: setExtent
-     * Set the renderer's extent
-     *
-     * Parameters:
-     * extent - {<OpenLayers.Bounds>}
-     * resolutionChanged - {Boolean}
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.  The
+     *     default "Content-Type" header will be set to "application-xml" if
+     *     none is provided.  This object is modified and should not be reused.
      * 
      * Returns:
-     * {Boolean} true to notify the layer that the new extent does not exceed
-     *     the coordinate range, and the features will not need to be redrawn.
+     * {XMLHttpRequest} Request object.
      */
-    setExtent: function(extent, resolutionChanged) {
-        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
-                                                               arguments);
-        var resolution = this.getResolution();
-    
-        var left = extent.left/resolution;
-        var top = extent.top/resolution - this.size.h;
-        if (resolutionChanged) {
-            this.offset = {x: left, y: top};
-            left = 0;
-            top = 0;
-        } else {
-            left = left - this.offset.x;
-            top = top - this.offset.y;
+    POST: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "POST"});
+        // set content type to application/xml if it isn't already set
+        config.headers = config.headers ? config.headers : {};
+        if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+            config.headers["Content-Type"] = "application/xml";
         }
-        
-        var org = left + " " + top;
-        this.root.setAttribute("coordorigin", org);
-
-        var size = this.size.w + " " + this.size.h;
-        this.root.setAttribute("coordsize", size);
-        
-        // flip the VML display Y axis upside down so it 
-        // matches the display Y axis of the map
-        this.root.style.flip = "y";
-        
-        return true;
+        return OpenLayers.Request.issue(config);
     },
-
-
+    
     /**
-     * Method: setSize
-     * Set the size of the drawing surface
+     * APIMethod: PUT
+     * Send an HTTP PUT request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to PUT and "Content-Type" header set to "application/xml".
      *
      * Parameters:
-     * size - {<OpenLayers.Size>} the size of the drawing surface
-     */
-    setSize: function(size) {
-        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
-
-        this.rendererRoot.style.width = this.size.w + "px";
-        this.rendererRoot.style.height = this.size.h + "px";
-
-        this.root.style.width = this.size.w + "px";
-        this.root.style.height = this.size.h + "px";
-    },
-
-    /**
-     * Method: getNodeType
-     * Get the node type for a geometry and style
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     * style - {Object}
-     *
-     * Returns:
-     * {String} The corresponding node type for the specified geometry
-     */
-    getNodeType: function(geometry, style) {
-        var nodeType = null;
-        switch (geometry.CLASS_NAME) {
-            case "OpenLayers.Geometry.Point":
-                if (style.externalGraphic) {
-                    nodeType = "olv:rect";
-                } else if (this.isComplexSymbol(style.graphicName)) {
-                    nodeType = "olv:shape";
-                } else {
-                    nodeType = "olv:oval";
-                }
-                break;
-            case "OpenLayers.Geometry.Rectangle":
-                nodeType = "olv:rect";
-                break;
-            case "OpenLayers.Geometry.LineString":
-            case "OpenLayers.Geometry.LinearRing":
-            case "OpenLayers.Geometry.Polygon":
-            case "OpenLayers.Geometry.Curve":
-            case "OpenLayers.Geometry.Surface":
-                nodeType = "olv:shape";
-                break;
-            default:
-                break;
-        }
-        return nodeType;
-    },
-
-    /**
-     * Method: setStyle
-     * Use to set all the style attributes to a VML node.
-     *
-     * Parameters:
-     * node - {DOMElement} An VML element to decorate
-     * style - {Object}
-     * options - {Object} Currently supported options include 
-     *                              'isFilled' {Boolean} and
-     *                              'isStroked' {Boolean}
-     * geometry - {<OpenLayers.Geometry>}
-     */
-    setStyle: function(node, style, options, geometry) {
-        style = style  || node._style;
-        options = options || node._options;
-        var widthFactor = 1;
-        
-        if (node._geometryClass == "OpenLayers.Geometry.Point") {
-            if (style.externalGraphic) {
-                var width = style.graphicWidth || style.graphicHeight;
-                var height = style.graphicHeight || style.graphicWidth;
-                width = width ? width : style.pointRadius*2;
-                height = height ? height : style.pointRadius*2;
-
-                var resolution = this.getResolution();
-                var xOffset = (style.graphicXOffset != undefined) ?
-                    style.graphicXOffset : -(0.5 * width);
-                var yOffset = (style.graphicYOffset != undefined) ?
-                    style.graphicYOffset : -(0.5 * height);
-                
-                node.style.left = ((geometry.x/resolution - this.offset.x)+xOffset).toFixed();
-                node.style.top = ((geometry.y/resolution - this.offset.y)-(yOffset+height)).toFixed();
-                node.style.width = width + "px";
-                node.style.height = height + "px";
-                node.style.flip = "y";
-                
-                // modify style/options for fill and stroke styling below
-                style.fillColor = "none";
-                options.isStroked = false;
-            } else if (this.isComplexSymbol(style.graphicName)) {
-                var cache = this.importSymbol(style.graphicName);
-                var symbolExtent = cache.extent;
-                var width = symbolExtent.getWidth();
-                var height = symbolExtent.getHeight();
-                node.setAttribute("path", cache.path);
-                node.setAttribute("coordorigin", symbolExtent.left + "," +
-                                                                symbolExtent.bottom);
-                node.setAttribute("coordsize", width + "," + height);
-                node.style.left = symbolExtent.left + "px";
-                node.style.top = symbolExtent.bottom + "px";
-                node.style.width = width + "px";
-                node.style.height = height + "px";
-        
-                this.drawCircle(node, geometry, style.pointRadius);
-                node.style.flip = "y";
-            } else {
-                this.drawCircle(node, geometry, style.pointRadius);
-            }
-        }
-
-        // fill 
-        if (options.isFilled) { 
-            node.setAttribute("fillcolor", style.fillColor); 
-        } else { 
-            node.setAttribute("filled", "false"); 
-        }
-        var fills = node.getElementsByTagName("fill");
-        var fill = (fills.length == 0) ? null : fills[0];
-        if (!options.isFilled) {
-            if (fill) {
-                node.removeChild(fill);
-            }
-        } else {
-            if (!fill) {
-                fill = this.createNode('olv:fill', node.id + "_fill");
-            }
-            fill.setAttribute("opacity", style.fillOpacity);
-
-            if (node._geometryClass == "OpenLayers.Geometry.Point" &&
-                    style.externalGraphic) {
-
-                // override fillOpacity
-                if (style.graphicOpacity) {
-                    fill.setAttribute("opacity", style.graphicOpacity);
-                }
-                
-                fill.setAttribute("src", style.externalGraphic);
-                fill.setAttribute("type", "frame");
-                
-                if (!(style.graphicWidth && style.graphicHeight)) {
-                  fill.aspect = "atmost";
-                }                
-            }
-            if (fill.parentNode != node) {
-                node.appendChild(fill);
-            }
-        }
-
-        // additional rendering for rotated graphics or symbols
-        if (typeof style.rotation != "undefined") {
-            if (style.externalGraphic) {
-                this.graphicRotate(node, xOffset, yOffset);
-                // make the fill fully transparent, because we now have
-                // the graphic as imagedata element. We cannot just remove
-                // the fill, because this is part of the hack described
-                // in graphicRotate
-                fill.setAttribute("opacity", 0);
-            } else {
-                node.style.rotation = style.rotation;
-            }
-        }
-
-        // stroke 
-        if (options.isStroked) { 
-            node.setAttribute("strokecolor", style.strokeColor); 
-            node.setAttribute("strokeweight", style.strokeWidth + "px"); 
-        } else { 
-            node.setAttribute("stroked", "false"); 
-        }
-        var strokes = node.getElementsByTagName("stroke");
-        var stroke = (strokes.length == 0) ? null : strokes[0];
-        if (!options.isStroked) {
-            if (stroke) {
-                node.removeChild(stroke);
-            }
-        } else {
-            if (!stroke) {
-                stroke = this.createNode('olv:stroke', node.id + "_stroke");
-                node.appendChild(stroke);
-            }
-            stroke.setAttribute("opacity", style.strokeOpacity);
-            stroke.setAttribute("endcap", !style.strokeLinecap || style.strokeLinecap == 'butt' ? 'flat' : style.strokeLinecap);
-            stroke.setAttribute("dashstyle", this.dashStyle(style));
-        }
-        
-        if (style.cursor != "inherit" && style.cursor != null) {
-            node.style.cursor = style.cursor;
-        }
-        return node;
-    },
-
-    /**
-     * Method: graphicRotate
-     * If a point is to be styled with externalGraphic and rotation, VML fills
-     * cannot be used to display the graphic, because rotation of graphic
-     * fills is not supported by the VML implementation of Internet Explorer.
-     * This method creates a olv:imagedata element inside the VML node,
-     * DXImageTransform.Matrix and BasicImage filters for rotation and
-     * opacity, and a 3-step hack to remove rendering artefacts from the
-     * graphic and preserve the ability of graphics to trigger events.
-     * Finally, OpenLayers methods are used to determine the correct
-     * insertion point of the rotated image, because DXImageTransform.Matrix
-     * does the rotation without the ability to specify a rotation center
-     * point.
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.  The
+     *     default "Content-Type" header will be set to "application-xml" if
+     *     none is provided.  This object is modified and should not be reused.
      * 
-     * Parameters:
-     * node    - {DOMElement}
-     * xOffset - {Number} rotation center relative to image, x coordinate
-     * yOffset - {Number} rotation center relative to image, y coordinate
-     */
-    graphicRotate: function(node, xOffset, yOffset) {
-        var style = style || node._style;
-        var options = node._options;
-        
-        var aspectRatio, size;
-        if (!(style.graphicWidth && style.graphicHeight)) {
-            // load the image to determine its size
-            var img = new Image();
-            img.onreadystatechange = OpenLayers.Function.bind(function() {
-                if(img.readyState == "complete" ||
-                        img.readyState == "interactive") {
-                    aspectRatio = img.width / img.height;
-                    size = Math.max(style.pointRadius * 2, 
-                        style.graphicWidth || 0,
-                        style.graphicHeight || 0);
-                    xOffset = xOffset * aspectRatio;
-                    style.graphicWidth = size * aspectRatio;
-                    style.graphicHeight = size;
-                    this.graphicRotate(node, xOffset, yOffset);
-                }
-            }, this);
-            img.src = style.externalGraphic;
-            
-            // will be called again by the onreadystate handler
-            return;
-        } else {
-            size = Math.max(style.graphicWidth, style.graphicHeight);
-            aspectRatio = style.graphicWidth / style.graphicHeight;
-        }
-        
-        var width = Math.round(style.graphicWidth || size * aspectRatio);
-        var height = Math.round(style.graphicHeight || size);
-        node.style.width = width + "px";
-        node.style.height = height + "px";
-        
-        // Three steps are required to remove artefacts for images with
-        // transparent backgrounds (resulting from using DXImageTransform
-        // filters on svg objects), while preserving awareness for browser
-        // events on images:
-        // - Use the fill as usual (like for unrotated images) to handle
-        //   events
-        // - specify an imagedata element with the same src as the fill
-        // - style the imagedata element with an AlphaImageLoader filter
-        //   with empty src
-        var image = document.getElementById(node.id + "_image");
-        if (!image) {
-            image = this.createNode("olv:imagedata", node.id + "_image");
-            node.appendChild(image);
-        }
-        image.style.width = width + "px";
-        image.style.height = height + "px";
-        image.src = style.externalGraphic;
-        image.style.filter =
-            "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
-            "src='', sizingMethod='scale')";
-
-        var rotation = style.rotation * Math.PI / 180;
-        var sintheta = Math.sin(rotation);
-        var costheta = Math.cos(rotation);
-
-        // do the rotation on the image
-        var filter =
-            "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
-            ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
-            ",SizingMethod='auto expand')\n";
-
-        // set the opacity (needed for the imagedata)
-        var opacity = style.graphicOpacity || style.fillOpacity;
-        if (opacity && opacity != 1) {
-            filter += 
-                "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
-                opacity+")\n";
-        }
-        node.style.filter = filter;
-
-        // do the rotation again on a box, so we know the insertion point
-        var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
-        var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
-        imgBox.rotate(style.rotation, centerPoint);
-        var imgBounds = imgBox.getBounds();
-
-        node.style.left = Math.round(
-            parseInt(node.style.left) + imgBounds.left) + "px";
-        node.style.top = Math.round(
-            parseInt(node.style.top) - imgBounds.bottom) + "px";
-    },
-
-    /**
-     * Method: postDraw
-     * Some versions of Internet Explorer seem to be unable to set fillcolor
-     * and strokecolor to "none" correctly before the fill node is appended to
-     * a visible vml node. This method takes care of that and sets fillcolor
-     * and strokecolor again if needed.
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     */
-    postDraw: function(node) {
-        var fillColor = node._style.fillColor;
-        var strokeColor = node._style.strokeColor;
-        if (fillColor == "none" &&
-                node.getAttribute("fillcolor") != fillColor) {
-            node.setAttribute("fillcolor", fillColor);
-        }
-        if (strokeColor == "none" &&
-                node.getAttribute("strokecolor") != strokeColor) {
-            node.setAttribute("strokecolor", strokeColor);
-        }
-    },
-
-
-    /**
-     * Method: setNodeDimension
-     * Get the geometry's bounds, convert it to our vml coordinate system, 
-     * then set the node's position, size, and local coordinate system.
-     *   
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     */
-    setNodeDimension: function(node, geometry) {
-
-        var bbox = geometry.getBounds();
-        if(bbox) {
-            var resolution = this.getResolution();
-        
-            var scaledBox = 
-                new OpenLayers.Bounds((bbox.left/resolution - this.offset.x).toFixed(),
-                                      (bbox.bottom/resolution - this.offset.y).toFixed(),
-                                      (bbox.right/resolution - this.offset.x).toFixed(),
-                                      (bbox.top/resolution - this.offset.y).toFixed());
-            
-            // Set the internal coordinate system to draw the path
-            node.style.left = scaledBox.left + "px";
-            node.style.top = scaledBox.top + "px";
-            node.style.width = scaledBox.getWidth() + "px";
-            node.style.height = scaledBox.getHeight() + "px";
-    
-            node.coordorigin = scaledBox.left + " " + scaledBox.top;
-            node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
-        }
-    },
-    
-    /** 
-     * Method: dashStyle
-     * 
-     * Parameters:
-     * style - {Object}
-     * 
      * Returns:
-     * {String} A VML compliant 'stroke-dasharray' value
+     * {XMLHttpRequest} Request object.
      */
-    dashStyle: function(style) {
-        var dash = style.strokeDashstyle;
-        switch (dash) {
-            case 'solid':
-            case 'dot':
-            case 'dash':
-            case 'dashdot':
-            case 'longdash':
-            case 'longdashdot':
-                return dash;
-            default:
-                // very basic guessing of dash style patterns
-                var parts = dash.split(/[ ,]/);
-                if (parts.length == 2) {
-                    if (1*parts[0] >= 2*parts[1]) {
-                        return "longdash";
-                    }
-                    return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
-                } else if (parts.length == 4) {
-                    return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
-                        "dashdot";
-                }
-                return "solid";
+    PUT: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "PUT"});
+        // set content type to application/xml if it isn't already set
+        config.headers = config.headers ? config.headers : {};
+        if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
+            config.headers["Content-Type"] = "application/xml";
         }
+        return OpenLayers.Request.issue(config);
     },
-
-    /**
-     * Method: createNode
-     * Create a new node
-     *
-     * Parameters:
-     * type - {String} Kind of node to draw
-     * id - {String} Id for node
-     *
-     * Returns:
-     * {DOMElement} A new node of the given type and id
-     */
-    createNode: function(type, id) {
-        var node = document.createElement(type);
-        if (id) {
-            node.setAttribute('id', id);
-        }
-        
-        // IE hack to make elements unselectable, to prevent 'blue flash'
-        // while dragging vectors; #1410
-        node.setAttribute('unselectable', 'on', 0);
-        node.onselectstart = function() { return(false); };
-        
-        return node;    
-    },
     
     /**
-     * Method: nodeTypeCompare
-     * Determine whether a node is of a given type
+     * APIMethod: DELETE
+     * Send an HTTP DELETE request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to DELETE.
      *
      * Parameters:
-     * node - {DOMElement} An VML element
-     * type - {String} Kind of node
-     *
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.
+     *     This object is modified and should not be reused.
+     * 
      * Returns:
-     * {Boolean} Whether or not the specified node is of the specified type
+     * {XMLHttpRequest} Request object.
      */
-    nodeTypeCompare: function(node, type) {
-
-        //split type
-        var subType = type;
-        var splitIndex = subType.indexOf(":");
-        if (splitIndex != -1) {
-            subType = subType.substr(splitIndex+1);
-        }
-
-        //split nodeName
-        var nodeName = node.nodeName;
-        splitIndex = nodeName.indexOf(":");
-        if (splitIndex != -1) {
-            nodeName = nodeName.substr(splitIndex+1);
-        }
-
-        return (subType == nodeName);
+    DELETE: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "DELETE"});
+        return OpenLayers.Request.issue(config);
     },
-
+  
     /**
-     * Method: createRenderRoot
-     * Create the renderer root
+     * APIMethod: HEAD
+     * Send an HTTP HEAD request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to HEAD.
      *
-     * Returns:
-     * {DOMElement} The specific render engine's root element
-     */
-    createRenderRoot: function() {
-        return this.nodeFactory(this.container.id + "_vmlRoot", "div");
-    },
-
-    /**
-     * Method: createRoot
-     * Create the main root element
-     *
-     * Returns:
-     * {DOMElement} The main root element to which we'll add vectors
-     */
-    createRoot: function() {
-        return this.nodeFactory(this.container.id + "_root", "olv:group");
-    },
-    
-    /**************************************
-     *                                    *
-     *     GEOMETRY DRAWING FUNCTIONS     *
-     *                                    *
-     **************************************/
-    
-    /**
-     * Method: drawPoint
-     * Render a point
-     * 
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.
+     *     This object is modified and should not be reused.
      * 
      * Returns:
-     * {DOMElement} or false if the point could not be drawn
+     * {XMLHttpRequest} Request object.
      */
-    drawPoint: function(node, geometry) {
-        return this.drawCircle(node, geometry, 1);
+    HEAD: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "HEAD"});
+        return OpenLayers.Request.issue(config);
     },
-
-    /**
-     * Method: drawCircle
-     * Render a circle.
-     * Size and Center a circle given geometry (x,y center) and radius
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * radius - {float}
-     * 
-     * Returns:
-     * {DOMElement} or false if the circle could not ne drawn
-     */
-    drawCircle: function(node, geometry, radius) {
-        if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
-            var resolution = this.getResolution();
-
-            node.style.left = ((geometry.x /resolution - this.offset.x).toFixed() - radius) + "px";
-            node.style.top = ((geometry.y /resolution - this.offset.y).toFixed() - radius) + "px";
     
-            var diameter = radius * 2;
-            
-            node.style.width = diameter + "px";
-            node.style.height = diameter + "px";
-            return node;
-        }
-        return false;
-    },
-
-
     /**
-     * Method: drawLineString
-     * Render a linestring.
-     * 
+     * APIMethod: OPTIONS
+     * Send an HTTP OPTIONS request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to OPTIONS.
+     *
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
+     * config - {Object} Object with properties for configuring the request.
+     *     See the <issue> method for documentation of allowed properties.
+     *     This object is modified and should not be reused.
      * 
      * Returns:
-     * {DOMElement}
+     * {XMLHttpRequest} Request object.
      */
-    drawLineString: function(node, geometry) {
-        return this.drawLine(node, geometry, false);
-    },
+    OPTIONS: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
+        return OpenLayers.Request.issue(config);
+    }
 
-    /**
-     * Method: drawLinearRing
-     * Render a linearring
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    drawLinearRing: function(node, geometry) {
-        return this.drawLine(node, geometry, true);
-    },
-
-    /**
-     * Method: DrawLine
-     * Render a line.
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * closeLine - {Boolean} Close the line? (make it a ring?)
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    drawLine: function(node, geometry, closeLine) {
-
-        this.setNodeDimension(node, geometry);
-
-        var resolution = this.getResolution();
-        var numComponents = geometry.components.length;
-        var parts = new Array(numComponents);
-
-        var comp, x, y;
-        for (var i = 0; i < numComponents; i++) {
-            comp = geometry.components[i];
-            x = (comp.x/resolution - this.offset.x);
-            y = (comp.y/resolution - this.offset.y);
-            parts[i] = " " + x.toFixed() + "," + y.toFixed() + " l ";
-        }
-        var end = (closeLine) ? " x e" : " e";
-        node.path = "m" + parts.join("") + end;
-        return node;
-    },
-
-    /**
-     * Method: drawPolygon
-     * Render a polygon
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    drawPolygon: function(node, geometry) {
-        this.setNodeDimension(node, geometry);
-
-        var resolution = this.getResolution();
-    
-        var path = [];
-        var linearRing, i, j, len, ilen, comp, x, y;
-        for (j = 0, len=geometry.components.length; j<len; j++) {
-            linearRing = geometry.components[j];
-
-            path.push("m");
-            for (i=0, ilen=linearRing.components.length; i<ilen; i++) {
-                comp = linearRing.components[i];
-                x = comp.x / resolution - this.offset.x;
-                y = comp.y / resolution - this.offset.y;
-                path.push(" " + x.toFixed() + "," + y.toFixed());
-                if (i==0) {
-                    path.push(" l");
-                }
-            }
-            path.push(" x ");
-        }
-        path.push("e");
-        node.path = path.join("");
-        return node;
-    },
-
-    /**
-     * Method: drawRectangle
-     * Render a rectangle
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    drawRectangle: function(node, geometry) {
-        var resolution = this.getResolution();
-    
-        node.style.left = (geometry.x/resolution - this.offset.x) + "px";
-        node.style.top = (geometry.y/resolution - this.offset.y) + "px";
-        node.style.width = geometry.width/resolution + "px";
-        node.style.height = geometry.height/resolution + "px";
-        
-        return node;
-    },
-
-    /**
-     * Method: drawSurface
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    drawSurface: function(node, geometry) {
-
-        this.setNodeDimension(node, geometry);
-
-        var resolution = this.getResolution();
-    
-        var path = [];
-        var comp, x, y;
-        for (var i=0, len=geometry.components.length; i<len; i++) {
-            comp = geometry.components[i];
-            x = comp.x / resolution - this.offset.x;
-            y = comp.y / resolution - this.offset.y;
-            if ((i%3)==0 && (i/3)==0) {
-                path.push("m");
-            } else if ((i%3)==1) {
-                path.push(" c");
-            }
-            path.push(" " + x + "," + y);
-        }
-        path.push(" x e");
-
-        node.path = path.join("");
-        return node;
-    },
-    
-    /**
-     * Method: importSymbol
-     * add a new symbol definition from the rendererer's symbol hash
-     * 
-     * Parameters:
-     * graphicName - {String} name of the symbol to import
-     * 
-     * Returns:
-     * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
-     */      
-    importSymbol: function (graphicName)  {
-        var id = this.container.id + "-" + graphicName;
-        
-        // check if symbol already exists in the cache
-        var cache = this.symbolCache[id];
-        if (cache) {
-            return cache;
-        }
-        
-        var symbol = OpenLayers.Renderer.symbol[graphicName];
-        if (!symbol) {
-            throw new Error(graphicName + ' is not a valid symbol name');
-            return;
-        }
-
-        var symbolExtent = new OpenLayers.Bounds(
-                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
-        
-        var pathitems = ["m"];
-        for (var i=0; i<symbol.length; i=i+2) {
-            x = symbol[i];
-            y = symbol[i+1];
-            symbolExtent.left = Math.min(symbolExtent.left, x);
-            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
-            symbolExtent.right = Math.max(symbolExtent.right, x);
-            symbolExtent.top = Math.max(symbolExtent.top, y);
-
-            pathitems.push(x);
-            pathitems.push(y);
-            if (i == 0) {
-                pathitems.push("l");
-            }
-        }
-        pathitems.push("x e");
-        var path = pathitems.join(" ");
-        
-        cache = {
-            path: path,
-            extent: symbolExtent
-        };
-        this.symbolCache[id] = cache;
-        
-        return cache;
-    },
-    
-    CLASS_NAME: "OpenLayers.Renderer.VML"
-});
+};
 /* ======================================================================
     OpenLayers/Tile/Image.js
    ====================================================================== */
@@ -15853,6 +15601,7 @@
                 this.imgDiv.map = null;
             }
             this.imgDiv.urls = null;
+            this.imgDiv.src = null;
         }
         this.imgDiv = null;
         if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { 
@@ -15997,6 +15746,8 @@
                 this.backBufferTile.resolution = this.layer.getResolution();
                 this.backBufferTile.renderTile();
             }
+
+            this.backBufferTile.hide();
         }
     },
     
@@ -16022,13 +15773,12 @@
         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);
+                    null, null, this.size, this.url);
         } else {
             OpenLayers.Util.modifyDOMElement(this.imgDiv,
-                    null, null, imageSize) ;
+                    null, null, this.size) ;
             this.imgDiv.src = this.url;
         }
         return true;
@@ -16055,12 +15805,11 @@
     initImgDiv: function() {
         
         var offset = this.layer.imageOffset; 
-        var size = this.layer.getImageSize(); 
      
         if (this.layerAlphaHack) {
             this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
                                                            offset,
-                                                           size,
+                                                           this.size,
                                                            null,
                                                            "relative",
                                                            null,
@@ -16070,7 +15819,7 @@
         } else {
             this.imgDiv = OpenLayers.Util.createImage(null,
                                                       offset,
-                                                      size,
+                                                      this.size,
                                                       null,
                                                       "relative",
                                                       null,
@@ -16289,8 +16038,10 @@
 
 /**
  * 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
+ * The OverMap control creates a small overview map, useful to display the 
+ * extent of a zoomed map and your main map and provide additional 
+ * navigation options to the User.  By default the overview map is drawn in
+ * the lower right corner of the main map. Create a new overview map with the
  * <OpenLayers.Control.OverviewMap> constructor.
  *
  * Inerits from:
@@ -16357,24 +16108,33 @@
     /**
      * 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.
+     *     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.
+     *     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.
+     *     the overview map's map constructor.  These should include any
+     *     non-default options that the main map was constructed with.
      */
     mapOptions: null,
+
+    /**
+     * APIProperty: autoPan
+     * {Boolean} Always pan the overview map, so the extent marker remains in
+     *     the center.  Default is false.  If true, when you drag the extent
+     *     marker, the overview map will update itself so the marker returns
+     *     to the center.
+     */
+    autoPan: false,
     
     /**
      * Property: handlers
@@ -16666,7 +16426,7 @@
             this.createMap();
         }
         
-        if(!this.isSuitableOverview()) {
+        if(this.autoPan || !this.isSuitableOverview()) {
             this.updateOverview();
         }
         
@@ -17483,6 +17243,10 @@
             window.clearTimeout(this.timerId);
             this.timerId = null;
         }
+        if(this.rightclickTimerId != null) {
+            window.clearTimeout(this.rightclickTimerId);
+            this.rightclickTimerId = null;
+        }
     },
     
     /**
@@ -17975,7 +17739,7 @@
      *
      * Parameters:
      * control - {<OpenLayers.Control>} 
-     * layers - {Array(<OpenLayers.Layer.Vector>)}
+     * layer - {<OpenLayers.Layer.Vector>}
      * callbacks - {Object} An object with a 'over' property whos value is
      *     a function to be called when the mouse is over a feature. The 
      *     callback should expect to recieve a single argument, the feature.
@@ -18088,18 +17852,32 @@
      * {Boolean} The event occurred over a relevant feature.
      */
     handle: function(evt) {
+        if(this.feature && !this.feature.layer) {
+            // feature has been destroyed
+            this.feature = null;
+        }
         var type = evt.type;
         var handled = false;
         var previouslyIn = !!(this.feature); // previously in a feature
         var click = (type == "click" || type == "dblclick");
         this.feature = this.layer.getFeatureFromEvent(evt);
-        if(this.feature && this.feature.layer) {
+        if(this.feature && !this.feature.layer) {
+            // feature has been destroyed
+            this.feature = null;
+        }
+        if(this.lastFeature && !this.lastFeature.layer) {
+            // last feature has been destroyed
+            this.lastFeature = null;
+        }
+        if(this.feature) {
             var inNew = (this.feature != this.lastFeature);
             if(this.geometryTypeMatches(this.feature)) {
                 // in to a feature
                 if(previouslyIn && inNew) {
                     // out of last feature and in to another
-                    this.triggerCallback(type, 'out', [this.lastFeature]);
+                    if(this.lastFeature) {
+                        this.triggerCallback(type, 'out', [this.lastFeature]);
+                    }
                     this.triggerCallback(type, 'in', [this.feature]);
                 } else if(!previouslyIn || click) {
                     // in feature for the first time
@@ -18109,7 +17887,7 @@
                 handled = true;
             } else {
                 // not in to a feature
-                if(previouslyIn && inNew || (click && this.lastFeature)) {
+                if(this.lastFeature && (previouslyIn && inNew || click)) {
                     // out of last feature for the first time
                     this.triggerCallback(type, 'out', [this.lastFeature]);
                 }
@@ -18121,8 +17899,7 @@
                 this.feature = null;
             }
         } else {
-            if(this.lastFeature && this.lastFeature.layer &&
-               (previouslyIn || click)) {
+            if(this.lastFeature && (previouslyIn || click)) {
                 this.triggerCallback(type, 'out', [this.lastFeature]);
             }
         }
@@ -18282,21 +18059,21 @@
 
     /**
      * APIProperty: stopMove
-     * {Boolean} Stop other listeners from being notified on mousemoves.
+     * {Boolean} - Stop other listeners from being notified on mousemoves.
      *      Default is false.
      */
     stopMove: false,
 
     /**
      * Property: px
-     * {<OpenLayers.Pixel>} The location of the last mousemove, expressed
+     * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed
      *      in pixels.
      */
     px: null,
 
     /**
      * Property: timerId
-     * {Number} The id of the timer.
+     * {Number} - The id of the timer.
      */
     timerId: null,
  
@@ -18308,10 +18085,11 @@
      * 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.
+     * callbacks - {Object} An object with keys corresponding to callbacks
+     *     that will be called by the handler. The callbacks should
+     *     expect to receive a single argument, the event. Callbacks for
+     *     'move', the mouse is moving, and 'pause', the mouse is pausing,
+     *     are supported.
      * options - {Object} An optional object whose properties will be set on
      *     the handler.
      */
@@ -18745,19 +18523,19 @@
      *     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.
+     * 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.
-     *  - *move* Triggered when layer moves (triggered with every mousemove
-     *      during a drag).
-     *  - *moveend* Triggered when layer is done moving, object passed as
-     *      argument has a zoomChanged boolean property which tells that the
-     *      zoom has changed.
+     * 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.
+     * move - Triggered when layer moves (triggered with every mousemove
+     *     during a drag).
+     * moveend - Triggered when layer is done moving, object passed as
+     *     argument has a zoomChanged boolean property which tells that the
+     *     zoom has changed.
      */
     EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
                   "move", "moveend"],
@@ -19000,6 +18778,7 @@
             this.div = OpenLayers.Util.createDiv(this.id);
             this.div.style.width = "100%";
             this.div.style.height = "100%";
+            this.div.dir = "ltr";
 
             this.events = new OpenLayers.Events(this, this.div, 
                                                 this.EVENT_TYPES);
@@ -19313,7 +19092,7 @@
     },
 
     /**
-     * Method: calculateInRange
+     * APIMethod: calculateInRange
      * 
      * Returns:
      * {Boolean} The layer is displayable at the current map's current
@@ -19631,7 +19410,7 @@
         if(this.map.fractionalZoom) {
             var low = Math.floor(zoom);
             var high = Math.ceil(zoom);
-            resolution = this.resolutions[high] +
+            resolution = this.resolutions[low] -
                 ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
         } else {
             resolution = this.resolutions[Math.round(zoom)];
@@ -19679,7 +19458,7 @@
             }
             var dRes = highRes - lowRes;
             if(dRes > 0) {
-                zoom = lowZoom + ((resolution - lowRes) / dRes);
+                zoom = lowZoom + ((highRes - resolution) / dRes);
             } else {
                 zoom = lowZoom;
             }
@@ -19959,6 +19738,1004 @@
 });
 
 /* ======================================================================
+    OpenLayers/Request/XMLHttpRequest.js
+   ====================================================================== */
+
+// Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
+//
+// 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.
+
+/**
+ * @requires OpenLayers/Request.js
+ */
+
+(function () {
+
+    // Save reference to earlier defined object implementation (if any)
+    var oXMLHttpRequest    = window.XMLHttpRequest;
+
+    // Define on browser type
+    var bGecko    = !!window.controllers,
+        bIE        = window.document.all && !window.opera;
+
+    // Constructor
+    function cXMLHttpRequest() {
+        this._object    = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP');
+    };
+
+    // BUGFIX: Firefox with Firebug installed would break pages if not executed
+    if (bGecko && oXMLHttpRequest.wrapped)
+        cXMLHttpRequest.wrapped    = oXMLHttpRequest.wrapped;
+
+    // Constants
+    cXMLHttpRequest.UNSENT                = 0;
+    cXMLHttpRequest.OPENED                = 1;
+    cXMLHttpRequest.HEADERS_RECEIVED    = 2;
+    cXMLHttpRequest.LOADING                = 3;
+    cXMLHttpRequest.DONE                = 4;
+
+    // Public Properties
+    cXMLHttpRequest.prototype.readyState    = cXMLHttpRequest.UNSENT;
+    cXMLHttpRequest.prototype.responseText    = "";
+    cXMLHttpRequest.prototype.responseXML    = null;
+    cXMLHttpRequest.prototype.status        = 0;
+    cXMLHttpRequest.prototype.statusText    = "";
+
+    // Instance-level Events Handlers
+    cXMLHttpRequest.prototype.onreadystatechange    = null;
+
+    // Class-level Events Handlers
+    cXMLHttpRequest.onreadystatechange    = null;
+    cXMLHttpRequest.onopen                = null;
+    cXMLHttpRequest.onsend                = null;
+    cXMLHttpRequest.onabort                = null;
+
+    // Public Methods
+    cXMLHttpRequest.prototype.open    = function(sMethod, sUrl, bAsync, sUser, sPassword) {
+
+        // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
+        this._async        = bAsync;
+
+        // Set the onreadystatechange handler
+        var oRequest    = this,
+            nState        = this.readyState;
+
+        // BUGFIX: IE - memory leak on page unload (inter-page leak)
+        if (bIE) {
+            var fOnUnload    = function() {
+                if (oRequest._object.readyState != cXMLHttpRequest.DONE)
+                    fCleanTransport(oRequest);
+            };
+            if (bAsync)
+                window.attachEvent("onunload", fOnUnload);
+        }
+
+        this._object.onreadystatechange    = function() {
+            if (bGecko && !bAsync)
+                return;
+
+            // Synchronize state
+            oRequest.readyState        = oRequest._object.readyState;
+
+            //
+            fSynchronizeValues(oRequest);
+
+            // BUGFIX: Firefox fires unneccesary DONE when aborting
+            if (oRequest._aborted) {
+                // Reset readyState to UNSENT
+                oRequest.readyState    = cXMLHttpRequest.UNSENT;
+
+                // Return now
+                return;
+            }
+
+            if (oRequest.readyState == cXMLHttpRequest.DONE) {
+                //
+                fCleanTransport(oRequest);
+// Uncomment this block if you need a fix for IE cache
+/*
+                // BUGFIX: IE - cache issue
+                if (!oRequest._object.getResponseHeader("Date")) {
+                    // Save object to cache
+                    oRequest._cached    = oRequest._object;
+
+                    // Instantiate a new transport object
+                    cXMLHttpRequest.call(oRequest);
+
+                    // Re-send request
+                    oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+                    oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
+                    // Copy headers set
+                    if (oRequest._headers)
+                        for (var sHeader in oRequest._headers)
+                            if (typeof oRequest._headers[sHeader] == "string")    // Some frameworks prototype objects with functions
+                                oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
+
+                    oRequest._object.onreadystatechange    = function() {
+                        // Synchronize state
+                        oRequest.readyState        = oRequest._object.readyState;
+
+                        if (oRequest._aborted) {
+                            //
+                            oRequest.readyState    = cXMLHttpRequest.UNSENT;
+
+                            // Return
+                            return;
+                        }
+
+                        if (oRequest.readyState == cXMLHttpRequest.DONE) {
+                            // Clean Object
+                            fCleanTransport(oRequest);
+
+                            // get cached request
+                            if (oRequest.status == 304)
+                                oRequest._object    = oRequest._cached;
+
+                            //
+                            delete oRequest._cached;
+
+                            //
+                            fSynchronizeValues(oRequest);
+
+                            //
+                            fReadyStateChange(oRequest);
+
+                            // BUGFIX: IE - memory leak in interrupted
+                            if (bIE && bAsync)
+                                window.detachEvent("onunload", fOnUnload);
+                        }
+                    };
+                    oRequest._object.send(null);
+
+                    // Return now - wait untill re-sent request is finished
+                    return;
+                };
+*/
+                // BUGFIX: IE - memory leak in interrupted
+                if (bIE && bAsync)
+                    window.detachEvent("onunload", fOnUnload);
+            }
+
+            // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
+            if (nState != oRequest.readyState)
+                fReadyStateChange(oRequest);
+
+            nState    = oRequest.readyState;
+        };
+
+        // Add method sniffer
+        if (cXMLHttpRequest.onopen)
+            cXMLHttpRequest.onopen.apply(this, arguments);
+
+        this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+
+        // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+        if (!bAsync && bGecko) {
+            this.readyState    = cXMLHttpRequest.OPENED;
+
+            fReadyStateChange(this);
+        }
+    };
+    cXMLHttpRequest.prototype.send    = function(vData) {
+        // Add method sniffer
+        if (cXMLHttpRequest.onsend)
+            cXMLHttpRequest.onsend.apply(this, arguments);
+
+        // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
+        // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
+        // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
+        if (vData && vData.nodeType) {
+            vData    = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
+            if (!this._headers["Content-Type"])
+                this._object.setRequestHeader("Content-Type", "application/xml");
+        }
+
+        this._object.send(vData);
+
+        // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+        if (bGecko && !this._async) {
+            this.readyState    = cXMLHttpRequest.OPENED;
+
+            // Synchronize state
+            fSynchronizeValues(this);
+
+            // Simulate missing states
+            while (this.readyState < cXMLHttpRequest.DONE) {
+                this.readyState++;
+                fReadyStateChange(this);
+                // Check if we are aborted
+                if (this._aborted)
+                    return;
+            }
+        }
+    };
+    cXMLHttpRequest.prototype.abort    = function() {
+        // Add method sniffer
+        if (cXMLHttpRequest.onabort)
+            cXMLHttpRequest.onabort.apply(this, arguments);
+
+        // BUGFIX: Gecko - unneccesary DONE when aborting
+        if (this.readyState > cXMLHttpRequest.UNSENT)
+            this._aborted    = true;
+
+        this._object.abort();
+
+        // BUGFIX: IE - memory leak
+        fCleanTransport(this);
+    };
+    cXMLHttpRequest.prototype.getAllResponseHeaders    = function() {
+        return this._object.getAllResponseHeaders();
+    };
+    cXMLHttpRequest.prototype.getResponseHeader    = function(sName) {
+        return this._object.getResponseHeader(sName);
+    };
+    cXMLHttpRequest.prototype.setRequestHeader    = function(sName, sValue) {
+        // BUGFIX: IE - cache issue
+        if (!this._headers)
+            this._headers    = {};
+        this._headers[sName]    = sValue;
+
+        return this._object.setRequestHeader(sName, sValue);
+    };
+    cXMLHttpRequest.prototype.toString    = function() {
+        return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
+    };
+    cXMLHttpRequest.toString    = function() {
+        return '[' + "XMLHttpRequest" + ']';
+    };
+
+    // Helper function
+    function fReadyStateChange(oRequest) {
+        // Execute onreadystatechange
+        if (oRequest.onreadystatechange)
+            oRequest.onreadystatechange.apply(oRequest);
+
+        // Sniffing code
+        if (cXMLHttpRequest.onreadystatechange)
+            cXMLHttpRequest.onreadystatechange.apply(oRequest);
+    };
+
+    function fGetDocument(oRequest) {
+        var oDocument    = oRequest.responseXML;
+        // Try parsing responseText
+        if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
+            oDocument    = new ActiveXObject('Microsoft.XMLDOM');
+            oDocument.loadXML(oRequest.responseText);
+        }
+        // Check if there is no error in document
+        if (oDocument)
+            if ((bIE && oDocument.parseError != 0) || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
+                return null;
+        return oDocument;
+    };
+
+    function fSynchronizeValues(oRequest) {
+        try {    oRequest.responseText    = oRequest._object.responseText;    } catch (e) {}
+        try {    oRequest.responseXML    = fGetDocument(oRequest._object);    } catch (e) {}
+        try {    oRequest.status            = oRequest._object.status;            } catch (e) {}
+        try {    oRequest.statusText        = oRequest._object.statusText;        } catch (e) {}
+    };
+
+    function fCleanTransport(oRequest) {
+        // BUGFIX: IE - memory leak (on-page leak)
+        oRequest._object.onreadystatechange    = new window.Function;
+
+        // Delete private properties
+        delete oRequest._headers;
+    };
+
+    // Internet Explorer 5.0 (missing apply)
+    if (!window.Function.prototype.apply) {
+        window.Function.prototype.apply    = function(oRequest, oArguments) {
+            if (!oArguments)
+                oArguments    = [];
+            oRequest.__func    = this;
+            oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
+            delete oRequest.__func;
+        };
+    };
+
+    // Register new object with window
+    /**
+     * Class: OpenLayers.Request.XMLHttpRequest
+     * Standard-compliant (W3C) cross-browser implementation of the
+     *     XMLHttpRequest object.  From
+     *     http://code.google.com/p/xmlhttprequest/.
+     */
+    OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
+})();
+/* ======================================================================
+    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. */
+
+/**
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ * @requires OpenLayers/Console.js
+ */
+
+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
+ *
+ */
+
+
+/**
+ * Function: OpenLayers.nullHandler
+ * @param {} request
+ */
+OpenLayers.nullHandler = function(request) {
+    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
+};
+
+/** 
+ * APIFunction: loadURL
+ * Background load a document.  For more flexibility in using XMLHttpRequest,
+ *     see the <OpenLayers.Request> methods.
+ *
+ * Parameters:
+ * uri - {String} URI of source doc
+ * params - {String} or {Object} GET params. Either a string in the form
+ *     "?hello=world&foo=bar" (do not forget the leading question mark)
+ *     or an object in the form {'hello': 'world', 'foo': 'bar}
+ * 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.  Note that if you do not specify an onComplete
+ *     function, <OpenLayers.nullHandler> will be called (which pops up a 
+ *     user friendly error message dialog).
+ * 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.  Note that if you do not
+ *     specify an onComplete function, <OpenLayers.nullHandler> will be called
+ *     (which pops up a user friendly error message dialog).
+ *
+ * Returns:
+ * {<OpenLayers.Request.XMLHttpRequest>}  The request object. To abort loading,
+ *     call request.abort().
+ */
+OpenLayers.loadURL = function(uri, params, caller,
+                                  onComplete, onFailure) {
+    
+    if(typeof params == 'string') {
+        params = OpenLayers.Util.getParameters(params);
+    }
+    var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
+    var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
+    
+    return OpenLayers.Request.GET({
+        url: uri, params: params,
+        success: success, failure: failure, scope: caller
+    });
+};
+
+/** 
+ * 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
+ * *Deprecated*.  Use <OpenLayers.Request> method instead.
+ *
+ * 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] = extras[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/Control/DragPan.js
    ====================================================================== */
 
@@ -19973,7 +20750,7 @@
 
 /**
  * Class: OpenLayers.Control.DragPan
- * DragPan control.
+ * The DragPan control pans the map with a drag of the mouse.
  *
  * Inherits from:
  *  - <OpenLayers.Control>
@@ -20100,6 +20877,15 @@
      */
     attributes: null,
 
+    /**
+     * Property: bounds
+     * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
+     *     property can be set by an <OpenLayers.Format> object when
+     *     deserializing the feature, so in most cases it represents an
+     *     information set by the server. 
+     */
+    bounds: null,
+
     /** 
      * Property: state 
      * {String} 
@@ -20350,38 +21136,40 @@
 /**
  * Constant: OpenLayers.Feature.Vector.style
  * OpenLayers features can have a number of style attributes. The 'default' 
- *     style will typically be used if no other style is specified.
+ *     style will typically be used if no other style is specified. These
+ *     styles correspond for the most part, to the styling properties defined
+ *     by the SVG standard. 
+ *     Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
+ *     Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
  *
- * Default style properties:
- *
- *  - fillColor: "#ee9900",
- *  - fillOpacity: 0.4, 
- *  - hoverFillColor: "white",
- *  - hoverFillOpacity: 0.8,
- *  - strokeColor: "#ee9900",
- *  - strokeOpacity: 1,
- *  - strokeWidth: 1,
- *  - strokeLinecap: "round",  [butt | round | square]
- *  - strokeDashstyle: "solid", [dot | dash | dashdot | longdash | longdashdot | solid]
- *  - hoverStrokeColor: "red",
- *  - hoverStrokeOpacity: 1,
- *  - hoverStrokeWidth: 0.2,
- *  - pointRadius: 6,
- *  - hoverPointRadius: 1,
- *  - hoverPointUnit: "%",
- *  - pointerEvents: "visiblePainted"
- *  - cursor: ""
- *
- * Other style properties that have no default values:
- *
- *  - externalGraphic,
- *  - graphicWidth,
- *  - graphicHeight,
- *  - graphicOpacity,
- *  - graphicXOffset,
- *  - graphicYOffset,
- *  - graphicName,
- *  - display
+ * Symbolizer properties:
+ * fillColor - {String} Hex fill color.  Default is "#ee9900".
+ * fillOpacity - {Number} Fill opacity (0-1).  Default is 0.4 
+ * strokeColor - {String} Hex stroke color.  Default is "#ee9900".
+ * strokeOpacity - {Number} Stroke opacity (0-1).  Default is 1.
+ * strokeWidth - {Number} Pixel stroke width.  Default is 1.
+ * strokeLinecap - {String} Stroke cap type.  Default is "round".  [butt | round | square]
+ * strokeDashstyle - {String} Stroke dash style.  Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
+ * pointRadius - {Number} Pixel point radius.  Default is 6.
+ * pointerEvents - {String}  Default is "visiblePainted".
+ * cursor - {String} Default is "".
+ * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
+ * graphicWidth - {Number} Pixel width for sizing an external graphic.
+ * graphicHeight - {Number} Pixel height for sizing an external graphic.
+ * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
+ * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
+ * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
+ * graphicZIndex - {Number} The integer z-index value to use in rendering.
+ * graphicName - {String} Named graphic to use when rendering points.  Supported values include "circle" (default),
+ *     "square", "star", "x", "cross", "triangle".
+ * graphicTitle - {String} Tooltip for an external graphic. Only supported in Firefox and Internet Explorer.
+ * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
+ * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
+ * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
+ * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
+ * backgroundHeight - {Number} The height of the background graphic.  If not provided, the graphicHeight will be used.
+ * backgroundWidth - {Number} The width of the background width.  If not provided, the graphicWidth will be used.
+ * display - {String} Symbolizers will have no effect if display is set to "none".  All other values have no effect.
  */ 
 OpenLayers.Feature.Vector.style = {
     'default': {
@@ -20440,6 +21228,9 @@
         hoverPointUnit: "%",
         pointerEvents: "visiblePainted",
         cursor: "inherit"
+    },
+    'delete': {
+        display: "none"
     }
 };    
 /* ======================================================================
@@ -20550,7 +21341,7 @@
 
         // depending on the box model, modify width and height to take borders
         // of the box into account
-        var box = this.getBoxCharacteristics(deltaX, deltaY);
+        var box = this.getBoxCharacteristics();
         if (box.newBoxModel) {
             if (xy.x > startX) {
                 this.zoomBox.style.width =
@@ -20621,7 +21412,17 @@
         }
     },
     
-    getBoxCharacteristics: function(dx, dy) {
+    /**
+     * Method: getCharacteristics
+     * Determines offset and box model for a box.
+     * 
+     * Returns:
+     * {Object} a hash with the following properties:
+     *     - xOffset - Corner offset in x-direction
+     *     - yOffset - Corner offset in y-direction
+     *     - newBoxModel - true for all browsers except IE in quirks mode
+     */
+    getBoxCharacteristics: function() {
         if (!this.boxCharacteristics) {
             var xOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
                 "border-left-width")) + parseInt(OpenLayers.Element.getStyle(
@@ -20805,14 +21606,14 @@
         var activated = false;
         if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
             // create temporary vector layer for rendering geometry sketch
-            var options = OpenLayers.Util.extend({
+            var options = {
                 displayInLayerSwitcher: false,
                 // indicate that the temp vector layer will never be out of range
                 // without this, resolution properties must be specified at the
                 // map-level for this temporary layer to init its resolutions
                 // correctly
                 calculateInRange: function() { return true; }
-            }, this.layerOptions);
+            };
             this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
             this.map.addLayer(this.layer);
             activated = true;
@@ -20923,6 +21724,12 @@
      */
     up: function(evt) {
         this.finalize();
+        // the mouseup method of superclass doesn't call the
+        // "done" callback if there's been no move between
+        // down and up
+        if (this.start == this.last) {
+            this.callback("done", [evt.xy]);
+        }
     },
 
     /**
@@ -22058,7 +22865,7 @@
     isBaseLayer: false,
     
     /** 
-     * Property: markers 
+     * APIProperty: markers 
      * {Array(<OpenLayers.Marker>)} internal marker list 
      */
     markers: null,
@@ -22402,7 +23209,8 @@
 
 /**
  * Class: OpenLayers.Control.DrawFeature
- * Draws features on a vector layer when active.
+ * The DrawFeature control draws point, line or polygon features on a vector
+ * layer when active.
  *
  * Inherits from:
  *  - <OpenLayers.Control>
@@ -22425,7 +23233,7 @@
      * Constant: EVENT_TYPES
      *
      * Supported event types:
-     *  - *featureadded* Triggered when a feature is added
+     * featureadded - Triggered when a feature is added
      */
     EVENT_TYPES: ["featureadded"],
     
@@ -22458,9 +23266,26 @@
         );
         
         OpenLayers.Control.prototype.initialize.apply(this, [options]);
-        this.callbacks = OpenLayers.Util.extend({done: this.drawFeature},
-                                                this.callbacks);
+        this.callbacks = OpenLayers.Util.extend(
+            {
+                done: this.drawFeature,
+                modify: function(vertex, feature) {
+                    this.layer.events.triggerEvent(
+                        "sketchmodified", {vertex: vertex, feature: feature}
+                    );
+                }
+            },
+            this.callbacks
+        );
         this.layer = layer;
+        var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
+        if(sketchStyle) {
+            this.handlerOptions = this.handlerOptions || {};
+            this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
+                this.handlerOptions.layerOptions,
+                {styleMap: new OpenLayers.StyleMap({"default": sketchStyle})}
+            );
+        }
         this.handler = new handler(this, this.callbacks, this.handlerOptions);
     },
 
@@ -22469,10 +23294,15 @@
      */
     drawFeature: function(geometry) {
         var feature = new OpenLayers.Feature.Vector(geometry);
-        feature.state = OpenLayers.State.INSERT;
-        this.layer.addFeatures([feature]);
-        this.featureAdded(feature);
-        this.events.triggerEvent("featureadded",{feature : feature});
+        var proceed = this.layer.events.triggerEvent(
+            "sketchcomplete", {feature: feature}
+        );
+        if(proceed !== false) {
+            feature.state = OpenLayers.State.INSERT;
+            this.layer.addFeatures([feature]);
+            this.featureAdded(feature);
+            this.events.triggerEvent("featureadded",{feature : feature});
+        }
     },
 
     CLASS_NAME: "OpenLayers.Control.DrawFeature"
@@ -22511,10 +23341,10 @@
      *     properties of this event depends on exactly what happened.
      *
      * Supported control event types (in addition to those from <OpenLayers.Control>):
-     *  - *measure* Triggered when a measurement sketch is complete.  Listeners
+     * measure - Triggered when a measurement sketch is complete.  Listeners
      *      will receive an event with measure, units, order, and geometry
      *      properties.
-     *  - *measurepartial* Triggered when a new point is added to the
+     * measurepartial - Triggered when a new point is added to the
      *      measurement sketch.  Listeners receive an event with measure,
      *      units, order, and geometry.
      */
@@ -22742,6 +23572,9 @@
 
 /**
  * Class: OpenLayers.Control.ZoomBox
+ * The ZoomBox control enables zooming directly to a given extent, by drawing 
+ * a box on the map. The box is drawn by holding down shift, whilst dragging 
+ * the mouse.
  *
  * Inherits from:
  *  - <OpenLayers.Control>
@@ -22760,6 +23593,12 @@
     out: false,
 
     /**
+     * Property: alwaysZoom
+     * {Boolean} Always zoom in/out, when box drawed 
+     */
+    alwaysZoom: false,
+
+    /**
      * Method: draw
      */    
     draw: function() {
@@ -22796,7 +23635,12 @@
                 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
                 var bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
             }
+            // always zoom in/out 
+            var lastZoom = this.map.getZoom(); 
             this.map.zoomToExtent(bounds);
+            if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ 
+                this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); 
+            }
         } else { // it's a pixel
             if (!this.out) {
                 this.map.setCenter(this.map.getLonLatFromPixel(position),
@@ -23180,6 +24024,7 @@
  * @requires OpenLayers/Layer/SphericalMercator.js
  * @requires OpenLayers/Layer/EventPane.js
  * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/Console.js
  */
 
 /**
@@ -23297,6 +24142,13 @@
 
             // move the ToS and branding stuff up to the pane
             // thanks a *mil* Erik for thinking of this
+            var termsOfUse = this.div.lastChild;
+            this.div.removeChild(termsOfUse);
+            this.pane.appendChild(termsOfUse);
+            termsOfUse.className = "olLayerGoogleCopyright";
+            termsOfUse.style.right = "";
+            termsOfUse.style.bottom = "";
+
             var poweredBy = this.div.lastChild;
             this.div.removeChild(poweredBy);
             this.pane.appendChild(poweredBy);
@@ -23304,13 +24156,6 @@
             poweredBy.style.left = "";
             poweredBy.style.bottom = "";
 
-            var termsOfUse = this.div.lastChild;
-            this.div.removeChild(termsOfUse);
-            this.pane.appendChild(termsOfUse);
-            termsOfUse.className = "olLayerGoogleCopyright";
-            termsOfUse.style.right = "";
-            termsOfUse.style.bottom = "";
-
         } catch (e) {
             OpenLayers.Console.error(e);
         }
@@ -23362,14 +24207,26 @@
      * evt - {Event}
      */
     onMapResize: function() {
+        this.windowResized = true;
         if(this.visibility) {
-            this.mapObject.checkResize();  
-        } else {
-            this.windowResized = true;
+            this.checkResize();
         }
     },
-    
+
     /**
+     * APIMethod: checkResize
+     * Check if the map has been resized, and if the Google Maps object is ready for checkResize.
+     * If the mapObject.getSize() equals the layer div's offset size, or if mapObject.getCenter()
+     * is null, the mapObject.checkResize() will fail.
+     */
+    checkResize: function() {
+        if (this.windowResized && this.div.style.display != "none" && this.mapObject.getCenter()) {
+            this.mapObject.checkResize();
+            this.windowResized = false;
+        }
+    },
+
+    /**
      * Method: display
      * Hide or show the layer
      *
@@ -23378,13 +24235,25 @@
      */
     display: function(display) {
         OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
-        if(this.div.style.display == "block" && this.windowResized) {
-            this.mapObject.checkResize();
-            this.windowResized = false;
-        }
+        this.checkResize();
     },
 
     /**
+     * Method: moveTo
+     * Handle calls to move the layer.
+     *
+     * 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) {
+        OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
+        this.checkResize();
+    },
+
+    /**
      * APIMethod: getZoomForExtent
      * 
      * Parameters:
@@ -23727,6 +24596,7 @@
 
 /**
  * @requires OpenLayers/Layer/HTTPRequest.js
+ * @requires OpenLayers/Console.js
  */
 
 /**
@@ -24552,21 +25422,29 @@
      *                used as default style for this style object. This style
      *                applies if no rules are specified. Symbolizers defined in
      *                rules will extend this default style.
-     * options      - {Object} An optional object with properties to set on the
-     *                userStyle
+     * options - {Object} An optional object with properties to set on the
+     *     style.
+     *
+     * Valid options:
+     * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
+     *     style.
      * 
      * Return:
      * {<OpenLayers.Style>}
      */
     initialize: function(style, options) {
+
+        OpenLayers.Util.extend(this, options);
         this.rules = [];
+        if(options && options.rules) {
+            this.addRules(options.rules);
+        }
 
         // use the default style from OpenLayers.Feature.Vector if no style
         // was given in the constructor
-        this.setDefaultStyle(style || 
-                OpenLayers.Feature.Vector.style["default"]);
-        
-        OpenLayers.Util.extend(this, options);
+        this.setDefaultStyle(style ||
+                             OpenLayers.Feature.Vector.style["default"]);
+
     },
 
     /** 
@@ -24628,8 +25506,6 @@
         // don't display if there were rules but none applied
         if(rules.length > 0 && appliedRules == false) {
             style.display = "none";
-        } else {
-            style.display = "";
         }
         
         return style;
@@ -24701,7 +25577,7 @@
         var rules = this.rules;
         var symbolizer, value;
         for (var i=0, len=rules.length; i<len; i++) {
-            var symbolizer = rules[i].symbolizer;
+            symbolizer = rules[i].symbolizer;
             for (var key in symbolizer) {
                 value = symbolizer[key];
                 if (typeof value == "object") {
@@ -24995,7 +25871,7 @@
     },
 
     /**
-     * Method: defaultRightDblClick 
+     * Method: defaultDblRightClick 
      * 
      * Parameters:
      * evt - {Event} 
@@ -25207,6 +26083,44 @@
     },
     
     /**
+     * APIMethod: distanceTo
+     * Calculate the closest distance between two geometries (on the x-y plane).
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
+     *
+     * Valid options depend on the specific geometry type.
+     * 
+     * Returns:
+     * {Number | Object} The distance between this geometry and the target.
+     *     If details is true, the return will be an object with distance,
+     *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
+     *     the coordinates of the closest point on this geometry. The x1 and y1
+     *     properties represent the coordinates of the closest point on the
+     *     target geometry.
+     */
+    distanceTo: function(geometry, options) {
+    },
+    
+    /**
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
+     *
+     * Parameters:
+     * nodes - {Boolean} For lines, only return vertices that are
+     *     endpoints.  If false, for lines, only vertices that are not
+     *     endpoints will be returned.  If not provided, all vertices will
+     *     be returned.
+     *
+     * Returns:
+     * {Array} A list of all vertices in the geometry.
+     */
+    getVertices: function(nodes) {
+    },
+
+    /**
      * Method: atPoint
      * Note - This is only an approximation based on the bounds of the 
      * geometry.
@@ -25266,6 +26180,17 @@
     },
 
     /**
+     * APIMethod: getCentroid
+     * Calculate the centroid of this geometry. This method is defined in subclasses.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Point>} The centroid of the collection
+     */
+    getCentroid: function() {
+        return null;
+    },
+
+    /**
      * Method: toString
      * Returns the Well-Known Text representation of a geometry
      *
@@ -25281,6 +26206,36 @@
     CLASS_NAME: "OpenLayers.Geometry"
 });
 
+/**
+ * Function: OpenLayers.Geometry.fromWKT
+ * Generate a geometry given a Well-Known Text string.
+ *
+ * Parameters:
+ * wkt - {String} A string representing the geometry in Well-Known Text.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} A geometry of the appropriate class.
+ */
+OpenLayers.Geometry.fromWKT = function(wkt) {
+    var format = arguments.callee.format;
+    if(!format) {
+        format = new OpenLayers.Format.WKT();
+        arguments.callee.format = format;
+    }
+    var geom;
+    var result = format.read(wkt);
+    if(result instanceof OpenLayers.Feature.Vector) {
+        geom = result.geometry;
+    } else if(result instanceof Array) {
+        var len = result.length;
+        var components = new Array(len);
+        for(var i=0; i<len; ++i) {
+            components[i] = result[i].geometry;
+        }
+        geom = new OpenLayers.Geometry.Collection(components);
+    }
+    return geom;
+};
     
 /**
  * Method: OpenLayers.Geometry.segmentsIntersect
@@ -25297,11 +26252,23 @@
  * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
  *     and y2.  The start point is represented by x1 and y1.  The end point
  *     is represented by x2 and y2.  Start and end are ordered so that x1 < x2.
+ * options - {Object} Optional properties for calculating the intersection.
+ *
+ * Valid options:
  * point - {Boolean} Return the intersection point.  If false, the actual
  *     intersection point will not be calculated.  If true and the segments
  *     intersect, the intersection point will be returned.  If true and
  *     the segments do not intersect, false will be returned.  If true and
  *     the segments are coincident, true will be returned.
+ * tolerance - {Number} If a non-null value is provided, if the segments are
+ *     within the tolerance distance, this will be considered an intersection.
+ *     In addition, if the point option is true and the calculated intersection
+ *     is within the tolerance distance of an end point, the endpoint will be
+ *     returned instead of the calculated intersection.  Further, if the
+ *     intersection is within the tolerance of endpoints on both segments, or
+ *     if two segment endpoints are within the tolerance distance of eachother
+ *     (but no intersection is otherwise calculated), an endpoint on the
+ *     first segment provided will be returned.
  *
  * Returns:
  * {Boolean | <OpenLayers.Geometry.Point>}  The two segments intersect.
@@ -25310,7 +26277,9 @@
  *     are coincident, return will be true (and the instersection is equal
  *     to the shorter segment).
  */
-OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, point) {
+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
+    var point = options && options.point;
+    var tolerance = options && options.tolerance;
     var intersection = false;
     var x11_21 = seg1.x1 - seg2.x1;
     var y11_21 = seg1.y1 - seg2.y1;
@@ -25342,8 +26311,102 @@
             }
         }
     }
+    if(tolerance) {
+        var dist;
+        if(intersection) {
+            if(point) {
+                var segs = [seg1, seg2];
+                var seg, x, y;
+                // check segment endpoints for proximity to intersection
+                // set intersection to first endpoint within the tolerance
+                outer: for(var i=0; i<2; ++i) {
+                    seg = segs[i];
+                    for(var j=1; j<3; ++j) {
+                        x = seg["x" + j];
+                        y = seg["y" + j];
+                        dist = Math.sqrt(
+                            Math.pow(x - intersection.x, 2) +
+                            Math.pow(y - intersection.y, 2)
+                        );
+                        if(dist < tolerance) {
+                            intersection.x = x;
+                            intersection.y = y;
+                            break outer;
+                        }
+                    }
+                }
+                
+            }
+        } else {
+            // no calculated intersection, but segments could be within
+            // the tolerance of one another
+            var segs = [seg1, seg2];
+            var source, target, x, y, p, result;
+            // check segment endpoints for proximity to intersection
+            // set intersection to first endpoint within the tolerance
+            outer: for(var i=0; i<2; ++i) {
+                source = segs[i];
+                target = segs[(i+1)%2];
+                for(var j=1; j<3; ++j) {
+                    p = {x: source["x"+j], y: source["y"+j]};
+                    result = OpenLayers.Geometry.distanceToSegment(p, target);
+                    if(result.distance < tolerance) {
+                        if(point) {
+                            intersection = new OpenLayers.Geometry.Point(p.x, p.y);
+                        } else {
+                            intersection = true;
+                        }
+                        break outer;
+                    }
+                }
+            }
+        }
+    }
     return intersection;
 };
+
+/**
+ * Function: OpenLayers.Geometry.distanceToSegment
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties representing the
+ *     point coordinates.
+ * segment - {Object} An object with x1, y1, x2, and y2 properties
+ *     representing endpoint coordinates.
+ *
+ * Returns:
+ * {Object} An object with distance, x, and y properties.  The distance
+ *     will be the shortest distance between the input point and segment.
+ *     The x and y properties represent the coordinates along the segment
+ *     where the shortest distance meets the segment.
+ */
+OpenLayers.Geometry.distanceToSegment = function(point, segment) {
+    var x0 = point.x;
+    var y0 = point.y;
+    var x1 = segment.x1;
+    var y1 = segment.y1;
+    var x2 = segment.x2;
+    var y2 = segment.y2;
+    var dx = x2 - x1;
+    var dy = y2 - y1;
+    var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
+                (Math.pow(dx, 2) + Math.pow(dy, 2));
+    var x, y;
+    if(along <= 0.0) {
+        x = x1;
+        y = y1;
+    } else if(along >= 1.0) {
+        x = x2;
+        y = y2;
+    } else {
+        x = x1 + along * dx;
+        y = y1 + along * dy;
+    }
+    return {
+        distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
+        x: x, y: y
+    };
+};
 /* ======================================================================
     OpenLayers/Layer/MapGuide.js
    ====================================================================== */
@@ -25373,6 +26436,16 @@
      **/
     isBaseLayer: true,
     
+    /**
+     * APIProperty: useHttpTile
+     * {Boolean} use a tile cache exposed directly via a webserver rather than the 
+	   *    via mapguide server. This does require extra configuration on the Mapguide Server,
+	   *    and will only work when singleTile is false. The url for the layer must be set to the
+	   *    webserver path rather than the Mapguide mapagent.	  
+	   *    See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp 
+     **/
+    useHttpTile: false,
+    
     /** 
      * APIProperty: singleTile
      * {Boolean} use tile server or request single tile image. 
@@ -25383,12 +26456,26 @@
      * APIProperty: useOverlay
      * {Boolean} flag to indicate if the layer should be retrieved using
      * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.
-     * Using an overlay image is only available in MapGuide Enterprise 2010
-     * and MapGuide Open Source v2.0.3 or higher, but it allows selections
-     * to be drawn separately from the map and offers styling options
      **/
     useOverlay: false,
     
+    /** 
+     * APIProperty: useAsyncOverlay
+     * {Boolean} indicates if the MapGuide site supports the asynchronous 
+     * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010
+     * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG 
+     * is called asynchronously, allows selections to be drawn separately from 
+     * the map and offers styling options.
+     * 
+     * With older versions of MapGuide, set useAsyncOverlay=false.  Note that in
+     * this case a synchronous AJAX call is issued and the mapname and session
+     * parameters must be used to initialize the layer, not the mapdefinition
+     * parameter. Also note that this will issue a synchronous AJAX request 
+     * before the image request can be issued so the users browser may lock
+     * up if the MG Web tier does not respond in a timely fashion.
+     **/
+    useAsyncOverlay: true,
+    
     /**
      * Constant: TILE_PARAMS
      * {Object} Hashtable of default parameter key/value pairs for tiled layer
@@ -25419,10 +26506,23 @@
         format: 'PNG',
         locale: 'en',
         clip: '1',
-        version: '2.1.0'
+        version: '2.0.0'
     },
     
     /** 
+     * Constant: FOLDER_PARAMS
+     * {Object} Hashtable of parameter key/value pairs which describe 
+     * the folder structure for tiles as configured in the mapguide 
+     * serverconfig.ini section [TileServiceProperties]
+     */
+    FOLDER_PARAMS: {
+        tileColumnsPerFolder: 30,
+        tileRowsPerFolder: 30,
+        format: 'png',
+        querystring: null
+    },	
+
+    /** 
      * Property: defaultSize
      * {<OpenLayers.Size>} Tile size as produced by MapGuide server
      **/
@@ -25438,8 +26538,9 @@
      * For untiled base layers, specify either combination of 'mapName' and
      * 'session', or 'mapDefinition' and 'locale'.  
      *
-     * For untiled overlay layers (useOverlay=true), 
-     * mapName and session are required parameters for the Layer constructor.  
+     * For older versions of MapGuide and overlay layers, set useAsyncOverlay 
+     * to false and in this case mapName and session are required parameters 
+     * for the constructor.
      *
      * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion 
      * factor that are different than the defaults used in OpenLayers, 
@@ -25498,6 +26599,9 @@
                            this.params,
                            this.OVERLAY_PARAMS
                            );
+            if (!this.useAsyncOverlay) {
+              this.params.version = "1.0.0";
+            }
           } else {
             OpenLayers.Util.applyDefaults(
                            this.params,
@@ -25506,10 +26610,17 @@
           }         
         } else {
             //initialize for tiled layers
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.TILE_PARAMS
-                           );
+            if (this.useHttpTile) {
+                OpenLayers.Util.applyDefaults(
+                               this.params,
+                               this.FOLDER_PARAMS
+                               );
+            } else {
+                OpenLayers.Util.applyDefaults(
+                               this.params,
+                               this.TILE_PARAMS
+                               );
+            }
             this.setTileSize(this.defaultSize); 
         }
     },
@@ -25578,8 +26689,21 @@
             setviewcenterx: center.lon,
             setviewcentery: center.lat,
             setviewscale: this.map.getScale()
+          };
+          
+          if (this.useOverlay && !this.useAsyncOverlay) {
+            //first we need to call GETVISIBLEMAPEXTENT to set the extent
+            var getVisParams = {};
+            getVisParams = OpenLayers.Util.extend(getVisParams, params);
+            getVisParams.operation = "GETVISIBLEMAPEXTENT";
+            getVisParams.version = "1.0.0";
+            getVisParams.session = this.params.session;
+            getVisParams.mapName = this.params.mapName;
+            getVisParams.format = 'text/xml';
+            url = this.getFullRequestString( getVisParams );
+            
+            OpenLayers.Request.GET({url: url, async: false});
           }
-          
           //construct the full URL
           url = this.getFullRequestString( params );
         } else {
@@ -25591,15 +26715,24 @@
           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;
+          if (this.useHttpTile){
+	          url = this.getImageFilePath(
+                   {
+                       tilecol: colidx,
+                       tilerow: rowidx,
+                       scaleindex: this.resolutions.length - this.map.zoom - 1
+                    });
+		  
+          } else {
+            url = this.getFullRequestString(
+                   {
+                       tilecol: colidx,
+                       tilerow: rowidx,
+                       scaleindex: this.resolutions.length - this.map.zoom - 1
+                    });
+          }
+       }
+       return url;
     },
 
     /**
@@ -25666,6 +26799,68 @@
         return requestString;
     },
 
+     /** 
+     * Method: getImageFilePath
+     * special handler to request mapguide tiles from an http exposed tilecache 
+     *
+     * Parameters:
+     * altUrl - {String} Alternative base URL to use.
+     *
+     * Returns:
+     * {String} A string with the url for the tile image
+     */
+    getImageFilePath: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;        
+
+        var tileRowGroup = "";
+        var tileColGroup = "";
+        
+        if (newParams.tilerow < 0) {
+          tileRowGroup =  '-';
+        }
+          
+        if (newParams.tilerow == 0 ) {
+          tileRowGroup += '0';
+        } else {
+          tileRowGroup += Math.floor(Math.abs(newParams.tilerow/this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;
+        }
+          
+        if (newParams.tilecol < 0) {
+          tileColGroup =  '-';
+        }
+        
+        if (newParams.tilecol == 0) {
+          tileColGroup += '0';
+        } else {
+          tileColGroup += Math.floor(Math.abs(newParams.tilecol/this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;
+        }					
+        
+        var tilePath = '/S' + Math.floor(newParams.scaleindex)
+                + '/' + this.params.basemaplayergroupname
+                + '/R' + tileRowGroup
+                + '/C' + tileColGroup
+                + '/' + (newParams.tilerow % this.params.tileRowsPerFolder) 
+                + '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) 
+                + '.' + this.params.format;
+    
+        if (this.params.querystring) {
+               tilePath += "?" + this.params.querystring;
+        }
+        
+        requestString += tilePath;
+        return requestString;
+    },
+    
     /** 
      * Method: calculateGridLayout
      * Generate parameters for the grid layout. This  
@@ -25966,7 +27161,16 @@
      * but some services want it that way. Default false.
      */
     encodeBBOX: false,
- 
+    
+    /** 
+     * APIProperty: noMagic 
+     * {Boolean} If true, the image format will not be automagicaly switched 
+     *     from image/jpeg to image/png or image/gif when using 
+     *     TRANSPARENT=TRUE. Also isBaseLayer will not changed by the  
+     *     constructor. Default false. 
+     */ 
+    noMagic: false,  
+    
     /**
      * Constructor: OpenLayers.Layer.WMS
      * Create a new WMS layer object
@@ -25999,7 +27203,7 @@
 
 
         //layer is transparent        
-        if (this.params.TRANSPARENT && 
+        if (!this.noMagic && this.params.TRANSPARENT && 
             this.params.TRANSPARENT.toString().toLowerCase() == "true") {
             
             // unless explicitly set in options, make layer an overlay
@@ -26243,10 +27447,9 @@
      * {<OpenLayers.Rule>}
      */
     initialize: function(options) {
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
         this.symbolizer = {};
-
         OpenLayers.Util.extend(this, options);
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
     },
 
     /** 
@@ -26320,6 +27523,33 @@
         }
         return context;
     },
+    
+    /**
+     * APIMethod: clone
+     * Clones this rule.
+     * 
+     * Returns:
+     * {<OpenLayers.Rule>} Clone of this rule.
+     */
+    clone: function() {
+        var options = OpenLayers.Util.extend({}, this);
+        // clone symbolizer
+        options.symbolizer = {};
+        for(var key in this.symbolizer) {
+            value = this.symbolizer[key];
+            type = typeof value;
+            if(type === "object") {
+                options.symbolizer[key] = OpenLayers.Util.extend({}, value);
+            } else if(type === "string") {
+                options.symbolizer[key] = value;
+            }
+        }
+        // clone filter
+        options.filter = this.filter && this.filter.clone();
+        // clone context
+        options.context = this.context && OpenLayers.Util.extend({}, this.context);
+        return new OpenLayers.Rule(options);
+    },
         
     CLASS_NAME: "OpenLayers.Rule"
 });
@@ -26344,7 +27574,7 @@
     /**
      * Property: styles
      * Hash of {<OpenLayers.Style>}, keyed by names of well known
-     * rendering intents (e.g. "default", "temporary", "select").
+     * rendering intents (e.g. "default", "temporary", "select", "delete").
      */
     styles: null,
     
@@ -26375,7 +27605,9 @@
             "select": new OpenLayers.Style(
                 OpenLayers.Feature.Vector.style["select"]),
             "temporary": new OpenLayers.Style(
-                OpenLayers.Feature.Vector.style["temporary"])
+                OpenLayers.Feature.Vector.style["temporary"]),
+            "delete": new OpenLayers.Style(
+                OpenLayers.Feature.Vector.style["delete"])
         };
         
         // take whatever the user passed as style parameter and convert it
@@ -26385,6 +27617,7 @@
             this.styles["default"] = style;
             this.styles["select"] = style;
             this.styles["temporary"] = style;
+            this.styles["delete"] = style;
         } else if(typeof style == "object") {
             for(var key in style) {
                 if(style[key] instanceof OpenLayers.Style) {
@@ -26398,6 +27631,7 @@
                     this.styles["default"] = new OpenLayers.Style(style);
                     this.styles["select"] = new OpenLayers.Style(style);
                     this.styles["temporary"] = new OpenLayers.Style(style);
+                    this.styles["delete"] = new OpenLayers.Style(style);
                     break;
                 }
             }
@@ -26718,14 +27952,37 @@
         }
         return area;
     },
+    
+    /**
+     * APIMethod: getCentroid
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Point>} The centroid of the collection
+     */
+    getCentroid: function() {
+        return this.components.length && this.components[0].getCentroid();
+        /*
+        var centroid;
+        for (var i=0, len=this.components.length; i<len; i++) {
+            if (!centroid) {
+                centroid = this.components[i].getCentroid();
+            } else {
+                centroid.resize(this.components[i].getCentroid(), 0.5);
+            }
+        }
+        return centroid;
+        */
+    },
 
     /**
      * APIMethod: move
-     * Moves a collection in place
+     * Moves a geometry by the given displacement along positive x and y axes.
+     *     This modifies the position of the geometry and clears the cached
+     *     bounds.
      *
      * Parameters:
-     * x - {Float} The x-displacement (in map units)
-     * y - {Float} The y-displacement (in map units)
+     * x - {Float} Distance to move geometry in positive x direction. 
+     * y - {Float} Distance to move geometry in positive y direction.
      */
     move: function(x, y) {
         for(var i=0, len=this.components.length; i<len; i++) {
@@ -26760,23 +28017,74 @@
      *                 will have four times the area).
      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+     * 
+     * Returns:
+     * {OpenLayers.Geometry} - The current geometry. 
      */
     resize: function(scale, origin, ratio) {
         for(var i=0; i<this.components.length; ++i) {
             this.components[i].resize(scale, origin, ratio);
         }
+        return this;
     },
 
     /**
-     * APIMethod: equals
-     * Tests for equivalent geometries
+     * APIMethod: distanceTo
+     * Calculate the closest distance between two geometries (on the x-y plane).
      *
      * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
      *
+     * Valid options:
+     * details - {Boolean} Return details from the distance calculation.
+     *     Default is false.
+     * edge - {Boolean} Calculate the distance from this geometry to the
+     *     nearest edge of the target geometry.  Default is true.  If true,
+     *     calling distanceTo from a geometry that is wholly contained within
+     *     the target will result in a non-zero distance.  If false, whenever
+     *     geometries intersect, calling distanceTo will return 0.  If false,
+     *     details cannot be returned.
+     *
      * Returns:
-     * {Boolean} The coordinates are equivalent
+     * {Number | Object} The distance between this geometry and the target.
+     *     If details is true, the return will be an object with distance,
+     *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
+     *     the coordinates of the closest point on this geometry. The x1 and y1
+     *     properties represent the coordinates of the closest point on the
+     *     target geometry.
      */
+    distanceTo: function(geometry, options) {
+        var edge = !(options && options.edge === false);
+        var details = edge && options && options.details;
+        var result, best;
+        var min = Number.POSITIVE_INFINITY;
+        for(var i=0, len=this.components.length; i<len; ++i) {
+            result = this.components[i].distanceTo(geometry, options);
+            distance = details ? result.distance : result;
+            if(distance < min) {
+                min = distance;
+                best = result;
+                if(min == 0) {
+                    break;
+                }
+            }
+        }
+        return best;
+    },
+
+    /** 
+     * APIMethod: equals
+     * Determine whether another geometry is equivalent to this one.  Geometries
+     *     are considered equivalent if all components have the same coordinates.
+     * 
+     * Parameters:
+     * geom - {<OpenLayers.Geometry>} The geometry to test. 
+     *
+     * Returns:
+     * {Boolean} The supplied geometry is equivalent to this geometry.
+     */
     equals: function(geometry) {
         var equivalent = true;
         if(!geometry || !geometry.CLASS_NAME ||
@@ -26839,6 +28147,30 @@
         return intersect;
     },
 
+    /**
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
+     *
+     * Parameters:
+     * nodes - {Boolean} For lines, only return vertices that are
+     *     endpoints.  If false, for lines, only vertices that are not
+     *     endpoints will be returned.  If not provided, all vertices will
+     *     be returned.
+     *
+     * Returns:
+     * {Array} A list of all vertices in the geometry.
+     */
+    getVertices: function(nodes) {
+        var vertices = [];
+        for(var i=0, len=this.components.length; i<len; ++i) {
+            Array.prototype.push.apply(
+                vertices, this.components[i].getVertices(nodes)
+            );
+        }
+        return vertices;
+    },
+
+
     CLASS_NAME: "OpenLayers.Geometry.Collection"
 });
 /* ======================================================================
@@ -26918,35 +28250,69 @@
 
     /**
      * APIMethod: distanceTo
-     * 
+     * Calculate the closest distance between two geometries (on the x-y plane).
+     *
      * Parameters:
-     * point - {<OpenLayers.Geometry.Point>} 
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
+     *
+     * Valid options:
+     * details - {Boolean} Return details from the distance calculation.
+     *     Default is false.
+     * edge - {Boolean} Calculate the distance from this geometry to the
+     *     nearest edge of the target geometry.  Default is true.  If true,
+     *     calling distanceTo from a geometry that is wholly contained within
+     *     the target will result in a non-zero distance.  If false, whenever
+     *     geometries intersect, calling distanceTo will return 0.  If false,
+     *     details cannot be returned.
+     *
+     * Returns:
+     * {Number | Object} The distance between this geometry and the target.
+     *     If details is true, the return will be an object with distance,
+     *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
+     *     the coordinates of the closest point on this geometry. The x1 and y1
+     *     properties represent the coordinates of the closest point on the
+     *     target geometry.
      */
-    distanceTo: function(point) {
-        var distance = 0.0;
-        if ( (this.x != null) && (this.y != null) && 
-             (point != null) && (point.x != null) && (point.y != null) ) {
-             
-             var dx2 = Math.pow(this.x - point.x, 2);
-             var dy2 = Math.pow(this.y - point.y, 2);
-             distance = Math.sqrt( dx2 + dy2 );
+    distanceTo: function(geometry, options) {
+        var edge = !(options && options.edge === false);
+        var details = edge && options && options.details;
+        var distance, x0, y0, x1, y1, result;
+        if(geometry instanceof OpenLayers.Geometry.Point) {
+            x0 = this.x;
+            y0 = this.y;
+            x1 = geometry.x;
+            y1 = geometry.y;
+            distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
+            result = !details ?
+                distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
+        } else {
+            result = geometry.distanceTo(this, options);
+            if(details) {
+                // switch coord order since this geom is target
+                result = {
+                    x0: result.x1, y0: result.y1,
+                    x1: result.x0, y1: result.y0,
+                    distance: result.distance
+                };
+            }
         }
-        return distance;
+        return result;
     },
     
     /** 
-    * APIMethod: equals
-    * 
-    * Parameters:
-    * xy - {<OpenLayers.Geometry>} 
-    *
-    * Returns:
-    * {Boolean} Boolean value indicating whether the passed-in 
-    *          {<OpenLayers.Geometry>} object has the same  components as this
-    *          note that if ll passed in is null, returns false
-    *
-    */
-    equals:function(geom) {
+     * APIMethod: equals
+     * Determine whether another geometry is equivalent to this one.  Geometries
+     *     are considered equivalent if all components have the same coordinates.
+     * 
+     * Parameters:
+     * geom - {<OpenLayers.Geometry.Point>} The geometry to test. 
+     *
+     * Returns:
+     * {Boolean} The supplied geometry is equivalent to this geometry.
+     */
+    equals: function(geom) {
         var equals = false;
         if (geom != null) {
             equals = ((this.x == geom.x && this.y == geom.y) ||
@@ -26968,11 +28334,13 @@
     
     /**
      * APIMethod: move
-     * Moves a point in place
+     * Moves a geometry by the given displacement along positive x and y axes.
+     *     This modifies the position of the geometry and clears the cached
+     *     bounds.
      *
      * Parameters:
-     * x - {Float} 
-     * y - {Float} 
+     * x - {Float} Distance to move geometry in positive x direction. 
+     * y - {Float} Distance to move geometry in positive y direction.
      */
     move: function(x, y) {
         this.x = this.x + x;
@@ -26999,6 +28367,16 @@
     },
 
     /**
+     * APIMethod: getCentroid
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Point>} The centroid of the collection
+     */
+    getCentroid: function() {
+        return new OpenLayers.Geometry.Point(this.x, this.y);
+    },
+
+    /**
      * APIMethod: resize
      * Resize a point relative to some origin.  For points, this has the effect
      *     of scaling a vector (from the origin to the point).  This method is
@@ -27010,12 +28388,16 @@
      *                 distance between the point and origin.
      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+     * 
+     * Returns:
+     * {OpenLayers.Geometry} - The current geometry. 
      */
     resize: function(scale, origin, ratio) {
         ratio = (ratio == undefined) ? 1 : ratio;
         this.x = origin.x + (scale * ratio * (this.x - origin.x));
         this.y = origin.y + (scale * (this.y - origin.y));
         this.clearBounds();
+        return this;
     },
     
     /**
@@ -27058,6 +28440,23 @@
         return this;
     },
 
+    /**
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
+     *
+     * Parameters:
+     * nodes - {Boolean} For lines, only return vertices that are
+     *     endpoints.  If false, for lines, only vertices that are not
+     *     endpoints will be returned.  If not provided, all vertices will
+     *     be returned.
+     *
+     * Returns:
+     * {Array} A list of all vertices in the geometry.
+     */
+    getVertices: function(nodes) {
+        return [this];
+    },
+
     CLASS_NAME: "OpenLayers.Geometry.Point"
 });
 /* ======================================================================
@@ -27073,6 +28472,7 @@
  * @requires OpenLayers/Renderer.js
  * @requires OpenLayers/StyleMap.js
  * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Console.js
  */
 
 /**
@@ -27098,49 +28498,62 @@
      *     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.
+     * object - {Object} A reference to layer.events.object.
+     * element - {DOMElement} A reference to layer.events.element.
      *
      * Supported map event types (in addition to those from <OpenLayers.Layer>):
-     *  - *beforefeatureadded* Triggered before a feature is added.  Listeners
+     * beforefeatureadded - Triggered before a feature is added.  Listeners
      *      will receive an object with a *feature* property referencing the
      *      feature to be added.  To stop the feature from being added, a
      *      listener should return false.
-     *  - *beforefeaturesadded* Triggered before an array of features is added.
+     * beforefeaturesadded - Triggered before an array of features is added.
      *      Listeners will receive an object with a *features* property
      *      referencing the feature to be added. To stop the features from
      *      being added, a listener should return false.
-     *  - *featureadded* Triggered after a feature is added.  The event
+     * featureadded - Triggered after a feature is added.  The event
      *      object passed to listeners will have a *feature* property with a
      *      reference to the added feature.
-     *  - *featuresadded* Triggered after features are added.  The event
+     * featuresadded - Triggered after features are added.  The event
      *      object passed to listeners will have a *features* property with a
      *      reference to an array of added features.
-     *  - *beforefeatureremoved* Triggered before a feature is removed. Listeners
+     * beforefeatureremoved - Triggered before a feature is removed. Listeners
      *      will receive an object with a *feature* property referencing the
      *      feature to be removed.
-     *  - *featureremoved* Triggerd after a feature is removed. The event
+     * featureremoved - Triggerd after a feature is removed. The event
      *      object passed to listeners will have a *feature* property with a
      *      reference to the removed feature.
-     *  - *featuresremoved* Triggered after features are removed. The event
+     * featuresremoved - Triggered after features are removed. The event
      *      object passed to listeners will have a *features* property with a
      *      reference to an array of removed features.
-     *  - *featureselected* Triggered after a feature is selected.  Listeners
+     * featureselected - Triggered after a feature is selected.  Listeners
      *      will receive an object with a *feature* property referencing the
      *      selected feature.
-     *  - *featureunselected* Triggered after a feature is unselected.
+     * featureunselected - Triggered after a feature is unselected.
      *      Listeners will receive an object with a *feature* property
      *      referencing the unselected feature.
-     *  - *beforefeaturemodified* Triggered when a feature is selected to 
+     * beforefeaturemodified - Triggered when a feature is selected to 
      *      be modified.  Listeners will receive an object with a *feature* 
      *      property referencing the selected feature.
-     *  - *featuremodified* Triggered when a feature has been modified.
+     * featuremodified - Triggered when a feature has been modified.
      *      Listeners will receive an object with a *feature* property referencing 
      *      the modified feature.
-     *  - *afterfeaturemodified* Triggered when a feature is finished being modified.
+     * afterfeaturemodified - Triggered when a feature is finished being modified.
      *      Listeners will receive an object with a *feature* property referencing 
      *      the modified feature.
-     *  - *refresh* Triggered when something wants a strategy to ask the protocol
+     * vertexmodified - Triggered when a vertex within any feature geometry
+     *      has been modified.  Listeners will receive an object with a
+     *      *feature* property referencing the modified feature, a *vertex*
+     *      property referencing the vertex modified (always a point geometry),
+     *      and a *pixel* property referencing the pixel location of the
+     *      modification.
+     * sketchmodified - Triggered when a feature sketch bound for this layer
+     *      is modified.  Listeners will receive an object with a *vertex*
+     *      property referencing the modified vertex.
+     * sketchcomplete - Triggered when a feature sketch bound for this layer
+     *      is complete.  Listeners will receive an object with a *feature*
+     *      property referencing the sketch feature.  By returning false, a
+     *      listener can stop the sketch feature from being added to the layer.
+     * refresh - Triggered when something wants a strategy to ask the protocol
      *      for a new set of features.
      */
     EVENT_TYPES: ["beforefeatureadded", "beforefeaturesadded",
@@ -27148,6 +28561,7 @@
                   "beforefeatureremoved", "featureremoved", "featuresremoved",
                   "beforefeatureselected", "featureselected", "featureunselected", 
                   "beforefeaturemodified", "featuremodified", "afterfeaturemodified",
+                  "vertexmodified", "sketchmodified", "sketchcomplete",
                   "refresh"],
 
     /**
@@ -27169,7 +28583,7 @@
      * {Boolean} Whether the layer is a vector layer.
      */
     isVector: true,
-
+    
     /** 
      * APIProperty: features
      * {Array(<OpenLayers.Feature.Vector>)} 
@@ -27350,7 +28764,7 @@
      *     the refresh event.
      */
     refresh: function(obj) {
-        if(this.inRange && this.visibility) {
+        if(this.calculateInRange() && this.visibility) {
             this.events.triggerEvent("refresh", obj);
         }
     },
@@ -27361,7 +28775,7 @@
      * and assigns the first one whose "supported()" function returns true.
      */    
     assignRenderer: function()  {
-        for (var i=0, len=this.renderers.length; i<this.renderers.length; i++) {
+        for (var i=0, len=this.renderers.length; i<len; i++) {
             var rendererClass = OpenLayers.Renderer[this.renderers[i]];
             if (rendererClass && rendererClass.prototype.supported()) {
                 this.renderer = new rendererClass(this.div,
@@ -27496,6 +28910,23 @@
             }
         }    
     },
+    
+    /** 
+     * APIMethod: display
+     * Hide or show the Layer
+     * 
+     * Parameters:
+     * display - {Boolean}
+     */
+    display: function(display) {
+        OpenLayers.Layer.prototype.display.apply(this, arguments);
+        // we need to set the display style of the root in case it is attached
+        // to a foreign layer
+        var currentDisplay = this.div.style.display;
+        if(currentDisplay != this.renderer.root.style.display) {
+            this.renderer.root.style.display = currentDisplay;
+        }
+    },
 
     /**
      * APIMethod: addFeatures
@@ -27571,10 +29002,20 @@
 
     /**
      * APIMethod: removeFeatures
+     * Remove features from the layer.  This erases any drawn features and
+     *     removes them from the layer's control.  The beforefeatureremoved
+     *     and featureremoved events will be triggered for each feature.  The
+     *     featuresremoved event will be triggered after all features have
+     *     been removed.  To supress event triggering, use the silent option.
      * 
      * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} 
-     * options - {Object}
+     * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
+     *     removed.
+     * options - {Object} Optional properties for changing behavior of the
+     *     removal.
+     *
+     * Valid options:
+     * silent - {Boolean} Supress event triggering.  Default is false.
      */
     removeFeatures: function(features, options) {
         if(!features || features.length === 0) {
@@ -27667,6 +29108,13 @@
      * feature's style will be used.  If the feature doesn't have a style,
      * the layer's style will be used.
      * 
+     * This function is not designed to be used when adding features to 
+     * the layer (use addFeatures instead). It is meant to be used when
+     * the style of a feature has changed, or in some other way needs to 
+     * visually updated *after* it has already been added to a layer. You
+     * must add the feature to the layer for most layer-related events to 
+     * happen.
+     *
      * Parameters: 
      * feature - {<OpenLayers.Feature.Vector>} 
      * style - {Object} Symbolizer hash or {String} renderIntent
@@ -27677,10 +29125,11 @@
         if (!this.drawn) {
             return
         }
-        
         if (typeof style != "object") {
-            var renderIntent = typeof style == "string" ?
-                style : feature.renderIntent;
+            if(!style && feature.state === OpenLayers.State.DELETE) {
+                style = "delete";
+            }
+            var renderIntent = style || feature.renderIntent;
             style = feature.style || this.style;
             if (!style) {
                 style = this.styleMap.createSymbolizer(feature, renderIntent);
@@ -27901,9 +29350,11 @@
 /**
  * Class: OpenLayers.Handler.Point
  * Handler to draw a point on the map.  Point is displayed on mouse down,
- * moves on mouse move, and is finished on mouse up.  The handler triggers
- * callbacks for 'done' and 'cancel'.  Create a new instance with the
- * <OpenLayers.Handler.Point> constructor.
+ *     moves on mouse move, and is finished on mouse up.  The handler triggers
+ *     callbacks for 'done', 'cancel', and 'modify'.  The modify callback is
+ *     called with each change in the sketc and will receive the latest point
+ *     drawn.  Create a new instance with the <OpenLayers.Handler.Point>
+ *     constructor.
  * 
  * Inherits from:
  *  - <OpenLayers.Handler>
@@ -27974,19 +29425,23 @@
      *
      * Parameters:
      * control - {<OpenLayers.Control>} The control that owns this handler
-     * callbacks - {Object} An object with a 'done' property whose value is a
-     *             function to be called when the point drawing is finished.
-     *             The callback should expect to recieve a single argument,
-     *             the point geometry.  If the callbacks object contains a
-     *             'cancel' property, this function will be called when the
-     *             handler is deactivated while drawing.  The cancel should
-     *             expect to receive a geometry.
+     * callbacks - {Object} An object with a properties whose values are
+     *     functions.  Various callbacks described below.
      * options - {Object} An optional object with properties to be set on the
      *           handler
+     *
+     * Named callbacks:
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the point geometry.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * cancel - Called when the handler is deactivated while drawing.  The
+     *     cancel callback will receive a geometry.
      */
     initialize: function(control, callbacks, options) {
-        // TBD: deal with style
-        this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+        if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
+            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+        }
 
         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
     },
@@ -28122,6 +29577,21 @@
     },
     
     /**
+     * Method: modifyFeature
+     * Modify the existing geometry given a pixel location.
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
+     */
+    modifyFeature: function(pixel) {
+        var lonlat = this.map.getLonLatFromPixel(pixel);
+        this.point.geometry.x = lonlat.lon;
+        this.point.geometry.y = lonlat.lat;
+        this.callback("modify", [this.point.geometry, this.point]);
+        this.point.geometry.clearBounds();
+    },
+
+    /**
      * Method: drawFeature
      * Render features on the temporary layer.
      */
@@ -28184,10 +29654,7 @@
         }
         this.lastDown = evt.xy;
         this.drawing = true;
-        var lonlat = this.map.getLonLatFromPixel(evt.xy);
-        this.point.geometry.x = lonlat.lon;
-        this.point.geometry.y = lonlat.lat;
-        this.point.geometry.clearBounds();
+        this.modifyFeature(evt.xy);
         this.drawFeature();
         return false;
     },
@@ -28205,10 +29672,7 @@
      */
     mousemove: function (evt) {
         if(this.drawing) {
-            var lonlat = this.map.getLonLatFromPixel(evt.xy);
-            this.point.geometry.x = lonlat.lon;
-            this.point.geometry.y = lonlat.lat;
-            this.point.geometry.clearBounds();
+            this.modifyFeature(evt.xy);
             this.drawFeature();
         }
         return true;
@@ -28455,7 +29919,400 @@
         }
         return segments.sort(byX1);
     },
+    
+    /**
+     * Method: splitWithSegment
+     * Split this geometry with the given segment.
+     *
+     * Parameters:
+     * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
+     *     segment endpoint coordinates.
+     * options - {Object} Properties of this object will be used to determine
+     *     how the split is conducted.
+     *
+     * Valid options:
+     * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+     *     true.  If false, a vertex on the source segment must be within the
+     *     tolerance distance of the intersection to be considered a split.
+     * tolerance - {Number} If a non-null value is provided, intersections
+     *     within the tolerance distance of one of the source segment's
+     *     endpoints will be assumed to occur at the endpoint.
+     *
+     * Returns:
+     * {Object} An object with *lines* and *points* properties.  If the given
+     *     segment intersects this linestring, the lines array will reference
+     *     geometries that result from the split.  The points array will contain
+     *     all intersection points.  Intersection points are sorted along the
+     *     segment (in order from x1,y1 to x2,y2).
+     */
+    splitWithSegment: function(seg, options) {
+        var edge = !(options && options.edge === false);
+        var tolerance = options && options.tolerance;
+        var lines = [];
+        var verts = this.getVertices();
+        var points = [];
+        var intersections = [];
+        var split = false;
+        var vert1, vert2, point;
+        var node, vertex, target;
+        var interOptions = {point: true, tolerance: tolerance};
+        var result = null;
+        for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+            vert1 = verts[i];
+            points.push(vert1.clone());
+            vert2 = verts[i+1];
+            target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
+            point = OpenLayers.Geometry.segmentsIntersect(
+                seg, target, interOptions
+            );
+            if(point instanceof OpenLayers.Geometry.Point) {
+                if((point.x === seg.x1 && point.y === seg.y1) ||
+                   (point.x === seg.x2 && point.y === seg.y2) ||
+                   point.equals(vert1) || point.equals(vert2)) {
+                    vertex = true;
+                } else {
+                    vertex = false;
+                }
+                if(vertex || edge) {
+                    // push intersections different than the previous
+                    if(!point.equals(intersections[intersections.length-1])) {
+                        intersections.push(point.clone());
+                    }
+                    if(i === 0) {
+                        if(point.equals(vert1)) {
+                            continue;
+                        }
+                    }
+                    if(point.equals(vert2)) {
+                        continue;
+                    }
+                    split = true;
+                    if(!point.equals(vert1)) {
+                        points.push(point);
+                    }
+                    lines.push(new OpenLayers.Geometry.LineString(points));
+                    points = [point.clone()];
+                }
+            }
+        }
+        if(split) {
+            points.push(vert2.clone());
+            lines.push(new OpenLayers.Geometry.LineString(points));
+        }
+        if(intersections.length > 0) {
+            // sort intersections along segment
+            var xDir = seg.x1 < seg.x2 ? 1 : -1;
+            var yDir = seg.y1 < seg.y2 ? 1 : -1;
+            result = {
+                lines: lines,
+                points: intersections.sort(function(p1, p2) {
+                    return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
+                })
+            };
+        }
+        return result;
+    },
 
+    /**
+     * Method: split
+     * Use this geometry (the source) to attempt to split a target geometry.
+     * 
+     * Parameters:
+     * target - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Properties of this object will be used to determine
+     *     how the split is conducted.
+     *
+     * Valid options:
+     * mutual - {Boolean} Split the source geometry in addition to the target
+     *     geometry.  Default is false.
+     * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+     *     true.  If false, a vertex on the source must be within the tolerance
+     *     distance of the intersection to be considered a split.
+     * tolerance - {Number} If a non-null value is provided, intersections
+     *     within the tolerance distance of an existing vertex on the source
+     *     will be assumed to occur at the vertex.
+     * 
+     * Returns:
+     * {Array} A list of geometries (of this same type as the target) that
+     *     result from splitting the target with the source geometry.  The
+     *     source and target geometry will remain unmodified.  If no split
+     *     results, null will be returned.  If mutual is true and a split
+     *     results, return will be an array of two arrays - the first will be
+     *     all geometries that result from splitting the source geometry and
+     *     the second will be all geometries that result from splitting the
+     *     target geometry.
+     */
+    split: function(target, options) {
+        var results = null;
+        var mutual = options && options.mutual;
+        var sourceSplit, targetSplit, sourceParts, targetParts;
+        if(target instanceof OpenLayers.Geometry.LineString) {
+            var verts = this.getVertices();
+            var vert1, vert2, seg, splits, lines, point;
+            var points = [];
+            sourceParts = [];
+            for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+                vert1 = verts[i];
+                vert2 = verts[i+1];
+                seg = {
+                    x1: vert1.x, y1: vert1.y,
+                    x2: vert2.x, y2: vert2.y
+                };
+                targetParts = targetParts || [target];
+                if(mutual) {
+                    points.push(vert1.clone());
+                }
+                for(var j=0; j<targetParts.length; ++j) {
+                    splits = targetParts[j].splitWithSegment(seg, options);
+                    if(splits) {
+                        // splice in new features
+                        lines = splits.lines;
+                        if(lines.length > 0) {
+                            lines.unshift(j, 1);
+                            Array.prototype.splice.apply(targetParts, lines);
+                            j += lines.length - 2;
+                        }
+                        if(mutual) {
+                            for(var k=0, len=splits.points.length; k<len; ++k) {
+                                point = splits.points[k];
+                                if(!point.equals(vert1)) {
+                                    points.push(point);
+                                    sourceParts.push(new OpenLayers.Geometry.LineString(points));
+                                    if(point.equals(vert2)) {
+                                        points = [];
+                                    } else {
+                                        points = [point.clone()];
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if(mutual && sourceParts.length > 0 && points.length > 0) {
+                points.push(vert2.clone());
+                sourceParts.push(new OpenLayers.Geometry.LineString(points));
+            }
+        } else {
+            results = target.splitWith(this, options);
+        }
+        if(targetParts && targetParts.length > 1) {
+            targetSplit = true;
+        } else {
+            targetParts = [];
+        }
+        if(sourceParts && sourceParts.length > 1) {
+            sourceSplit = true;
+        } else {
+            sourceParts = [];
+        }
+        if(targetSplit || sourceSplit) {
+            if(mutual) {
+                results = [sourceParts, targetParts];
+            } else {
+                results = targetParts;
+            }
+        }
+        return results;
+    },
+
+    /**
+     * Method: splitWith
+     * Split this geometry (the target) with the given geometry (the source).
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} A geometry used to split this
+     *     geometry (the source).
+     * options - {Object} Properties of this object will be used to determine
+     *     how the split is conducted.
+     *
+     * Valid options:
+     * mutual - {Boolean} Split the source geometry in addition to the target
+     *     geometry.  Default is false.
+     * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+     *     true.  If false, a vertex on the source must be within the tolerance
+     *     distance of the intersection to be considered a split.
+     * tolerance - {Number} If a non-null value is provided, intersections
+     *     within the tolerance distance of an existing vertex on the source
+     *     will be assumed to occur at the vertex.
+     * 
+     * Returns:
+     * {Array} A list of geometries (of this same type as the target) that
+     *     result from splitting the target with the source geometry.  The
+     *     source and target geometry will remain unmodified.  If no split
+     *     results, null will be returned.  If mutual is true and a split
+     *     results, return will be an array of two arrays - the first will be
+     *     all geometries that result from splitting the source geometry and
+     *     the second will be all geometries that result from splitting the
+     *     target geometry.
+     */
+    splitWith: function(geometry, options) {
+        return geometry.split(this, options);
+
+    },
+
+    /**
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
+     *
+     * Parameters:
+     * nodes - {Boolean} For lines, only return vertices that are
+     *     endpoints.  If false, for lines, only vertices that are not
+     *     endpoints will be returned.  If not provided, all vertices will
+     *     be returned.
+     *
+     * Returns:
+     * {Array} A list of all vertices in the geometry.
+     */
+    getVertices: function(nodes) {
+        var vertices;
+        if(nodes === true) {
+            vertices = [
+                this.components[0],
+                this.components[this.components.length-1]
+            ];
+        } else if (nodes === false) {
+            vertices = this.components.slice(1, this.components.length-1);
+        } else {
+            vertices = this.components.slice();
+        }
+        return vertices;
+    },
+
+    /**
+     * APIMethod: distanceTo
+     * Calculate the closest distance between two geometries (on the x-y plane).
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
+     *
+     * Valid options:
+     * details - {Boolean} Return details from the distance calculation.
+     *     Default is false.
+     * edge - {Boolean} Calculate the distance from this geometry to the
+     *     nearest edge of the target geometry.  Default is true.  If true,
+     *     calling distanceTo from a geometry that is wholly contained within
+     *     the target will result in a non-zero distance.  If false, whenever
+     *     geometries intersect, calling distanceTo will return 0.  If false,
+     *     details cannot be returned.
+     *
+     * Returns:
+     * {Number | Object} The distance between this geometry and the target.
+     *     If details is true, the return will be an object with distance,
+     *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
+     *     the coordinates of the closest point on this geometry. The x1 and y1
+     *     properties represent the coordinates of the closest point on the
+     *     target geometry.
+     */
+    distanceTo: function(geometry, options) {
+        var edge = !(options && options.edge === false);
+        var details = edge && options && options.details;
+        var result, best = {};
+        var min = Number.POSITIVE_INFINITY;
+        if(geometry instanceof OpenLayers.Geometry.Point) {
+            var segs = this.getSortedSegments();
+            var x = geometry.x;
+            var y = geometry.y;
+            var seg;
+            for(var i=0, len=segs.length; i<len; ++i) {
+                seg = segs[i];
+                result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
+                if(result.distance < min) {
+                    min = result.distance;
+                    best = result;
+                    if(min === 0) {
+                        break;
+                    }
+                } else {
+                    // if distance increases and we cross y0 to the right of x0, no need to keep looking.
+                    if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
+                        break;
+                    }
+                }
+            }
+            if(details) {
+                best = {
+                    distance: best.distance,
+                    x0: best.x, y0: best.y,
+                    x1: x, y1: y
+                };
+            } else {
+                best = best.distance;
+            }
+        } else if(geometry instanceof OpenLayers.Geometry.LineString) { 
+            var segs0 = this.getSortedSegments();
+            var segs1 = geometry.getSortedSegments();
+            var seg0, seg1, intersection, x0, y0;
+            var len1 = segs1.length;
+            var interOptions = {point: true};
+            outer: for(var i=0, len=segs0.length; i<len; ++i) {
+                seg0 = segs0[i];
+                x0 = seg0.x1;
+                y0 = seg0.y1;
+                for(var j=0; j<len1; ++j) {
+                    seg1 = segs1[j];
+                    intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
+                    if(intersection) {
+                        min = 0;
+                        best = {
+                            distance: 0,
+                            x0: intersection.x, y0: intersection.y,
+                            x1: intersection.x, y1: intersection.y
+                        };
+                        break outer;
+                    } else {
+                        result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
+                        if(result.distance < min) {
+                            min = result.distance;
+                            best = {
+                                distance: min,
+                                x0: x0, y0: y0,
+                                x1: result.x, y1: result.y
+                            };
+                        }
+                    }
+                }
+            }
+            if(!details) {
+                best = best.distance;
+            }
+            if(min !== 0) {
+                // check the final vertex in this line's sorted segments
+                if(seg0) {
+                    result = geometry.distanceTo(
+                        new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
+                        options
+                    );
+                    var dist = details ? result.distance : result;
+                    if(dist < min) {
+                        if(details) {
+                            best = {
+                                distance: min,
+                                x0: result.x1, y0: result.y1,
+                                x1: result.x0, y1: result.y0
+                            };
+                        } else {
+                            best = dist;
+                        }
+                    }
+                }
+            }
+        } else {
+            best = geometry.distanceTo(this, options);
+            // swap since target comes from this line
+            if(details) {
+                best = {
+                    distance: best.distance,
+                    x0: best.x1, y0: best.y1,
+                    x1: best.x0, y1: best.y0
+                };
+            }
+        }
+        return best;
+    },
+
     CLASS_NAME: "OpenLayers.Geometry.LineString"
 });
 /* ======================================================================
@@ -28573,11 +30430,13 @@
     
     /**
      * APIMethod: move
-     * Moves a collection in place
+     * Moves a geometry by the given displacement along positive x and y axes.
+     *     This modifies the position of the geometry and clears the cached
+     *     bounds.
      *
      * Parameters:
-     * x - {Float} The x-displacement (in map units)
-     * y - {Float} The y-displacement (in map units)
+     * x - {Float} Distance to move geometry in positive x direction. 
+     * y - {Float} Distance to move geometry in positive y direction.
      */
     move: function(x, y) {
         for(var i = 0, len=this.components.length; i<len - 1; i++) {
@@ -28612,11 +30471,15 @@
      *                 will have four times the area).
      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+     * 
+     * Returns:
+     * {OpenLayers.Geometry} - The current geometry. 
      */
     resize: function(scale, origin, ratio) {
         for(var i=0, len=this.components.length; i<len - 1; ++i) {
             this.components[i].resize(scale, origin, ratio);
         }
+        return this;
     },
     
     /**
@@ -28642,6 +30505,29 @@
     },
 
     /**
+     * APIMethod: getCentroid
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Point>} The centroid of the collection
+     */
+    getCentroid: function() {
+        if ( this.components && (this.components.length > 2)) {
+            var sumX = 0.0;
+            var sumY = 0.0;
+            for (var i = 0; i < this.components.length - 1; i++) {
+                var b = this.components[i];
+                var c = this.components[i+1];
+                sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
+                sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
+            }
+            var area = -1 * this.getArea();
+            var x = sumX / (6 * area);
+            var y = sumY / (6 * area);
+        }
+        return new OpenLayers.Geometry.Point(x, y);
+    },
+
+    /**
      * APIMethod: getArea
      * Note - The area is positive if the ring is oriented CW, otherwise
      *         it will be negative.
@@ -28784,6 +30670,23 @@
         return intersect;
     },
 
+    /**
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
+     *
+     * Parameters:
+     * nodes - {Boolean} For lines, only return vertices that are
+     *     endpoints.  If false, for lines, only vertices that are not
+     *     endpoints will be returned.  If not provided, all vertices will
+     *     be returned.
+     *
+     * Returns:
+     * {Array} A list of all vertices in the geometry.
+     */
+    getVertices: function(nodes) {
+        return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
+    },
+
     CLASS_NAME: "OpenLayers.Geometry.LinearRing"
 });
 /* ======================================================================
@@ -28840,17 +30743,20 @@
      * Create a new path hander
      *
      * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object with a 'done' property whos value is a
-     *     function to be called when the path drawing is finished. The 
-     *     callback should expect to recieve a single argument, the line 
-     *     string geometry. If the callbacks object contains a 'point' 
-     *     property, this function will be sent each point as they are added.  
-     *     If the callbacks object contains a 'cancel' property, this function 
-     *     will be called when the handler is deactivated while drawing. The 
-     *     cancel should expect to receive a geometry.
+     * control - {<OpenLayers.Control>} The control that owns this handler
+     * callbacks - {Object} An object with a properties whose values are
+     *     functions.  Various callbacks described below.
      * options - {Object} An optional object with properties to be set on the
      *           handler
+     *
+     * Named callbacks:
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the linestring geometry.
+     * point - Called as each point is added.  Receives the new point geometry.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * cancel - Called when the handler is deactivated while drawing.  The
+     *     cancel callback will receive a geometry.
      */
     initialize: function(control, callbacks, options) {
         OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
@@ -28859,12 +30765,19 @@
     /**
      * Method: createFeature
      * Add temporary geometries
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
+     *     feature.
      */
-    createFeature: function() {
+    createFeature: function(pixel) {
+        var lonlat = this.control.map.getLonLatFromPixel(pixel);
+        this.point = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
+        );
         this.line = new OpenLayers.Feature.Vector(
-                                        new OpenLayers.Geometry.LineString());
-        this.point = new OpenLayers.Feature.Vector(
-                                        new OpenLayers.Geometry.Point());
+            new OpenLayers.Geometry.LineString([this.point.geometry])
+        );
         this.layer.addFeatures([this.line, this.point], {silent: true});
     },
         
@@ -28878,12 +30791,12 @@
     },
 
     /**
-     * Method: destroyPoint
+     * Method: removePoint
      * Destroy the temporary point.
      */
-    destroyPoint: function() {
+    removePoint: function() {
         if(this.point) {
-            this.layer.destroyFeatures([this.point]);
+            this.layer.removeFeatures([this.point]);
         }
     },
     
@@ -28891,11 +30804,22 @@
      * Method: addPoint
      * Add point to geometry.  Send the point index to override
      * the behavior of LinearRing that disregards adding duplicate points.
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.
      */
-    addPoint: function() {
-        this.line.geometry.addComponent(this.point.geometry.clone(),
-                                        this.line.geometry.components.length);
+    addPoint: function(pixel) {
+        this.layer.removeFeatures([this.point]);
+        var lonlat = this.control.map.getLonLatFromPixel(pixel);
+        this.point = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
+        );
+        this.line.geometry.addComponent(
+            this.point.geometry, this.line.geometry.components.length
+        );
         this.callback("point", [this.point.geometry, this.getGeometry()]);
+        this.callback("modify", [this.point.geometry, this.getSketch()]);
+        this.drawFeature();
     },
     
     /**
@@ -28913,14 +30837,20 @@
     /**
      * Method: modifyFeature
      * Modify the existing geometry given the new point
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
+     *     point.
      */
-    modifyFeature: function() {
-        var index = this.line.geometry.components.length - 1;
-        this.line.geometry.components[index].x = this.point.geometry.x;
-        this.line.geometry.components[index].y = this.point.geometry.y;
-        this.line.geometry.components[index].clearBounds();
+    modifyFeature: function(pixel) {
+        var lonlat = this.control.map.getLonLatFromPixel(pixel);
+        this.point.geometry.x = lonlat.lon;
+        this.point.geometry.y = lonlat.lat;
+        this.callback("modify", [this.point.geometry, this.getSketch()]);
+        this.point.geometry.clearBounds();
+        this.drawFeature();
     },
-    
+
     /**
      * Method: drawFeature
      * Render geometries on the temporary layer.
@@ -28931,6 +30861,17 @@
     },
 
     /**
+     * Method: getSketch
+     * Return the sketch feature.
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>}
+     */
+    getSketch: function() {
+        return this.line;
+    },
+
+    /**
      * Method: getGeometry
      * Return the sketch geometry.  If <multi> is true, this will return
      *     a multi-part geometry.
@@ -28966,18 +30907,12 @@
             if(this.persist) {
                 this.destroyFeature();
             }
-            this.createFeature();
+            this.createFeature(evt.xy);
+        } else if((this.lastUp == null) || !this.lastUp.equals(evt.xy)) {
+            this.addPoint(evt.xy);
         }
         this.mouseDown = true;
         this.lastDown = evt.xy;
-        var lonlat = this.control.map.getLonLatFromPixel(evt.xy);
-        this.point.geometry.x = lonlat.lon;
-        this.point.geometry.y = lonlat.lat;
-        this.point.geometry.clearBounds();
-        if((this.lastUp == null) || !this.lastUp.equals(evt.xy)) {
-            this.addPoint();
-        }
-        this.drawFeature();
         this.drawing = true;
         return false;
     },
@@ -28995,17 +30930,11 @@
      */
     mousemove: function (evt) {
         if(this.drawing) { 
-            var lonlat = this.map.getLonLatFromPixel(evt.xy);
-            this.point.geometry.x = lonlat.lon;
-            this.point.geometry.y = lonlat.lat;
-            this.point.geometry.clearBounds();
             if(this.mouseDown && this.freehandMode(evt)) {
-                this.addPoint();
+                this.addPoint(evt.xy);
             } else {
-                this.modifyFeature();
+                this.modifyFeature(evt.xy);
             }
-            this.drawFeature();
-            //this.callback("move", [evt.xy]);
         }
         return true;
     },
@@ -29025,13 +30954,11 @@
         this.mouseDown = false;
         if(this.drawing) {
             if(this.freehandMode(evt)) {
-                if(this.persist) {
-                    this.destroyPoint();
-                }
+                this.removePoint();
                 this.finalize();
             } else {
                 if(this.lastUp == null) {
-                   this.addPoint();
+                   this.addPoint(evt.xy);
                 }
                 this.lastUp = evt.xy;
             }
@@ -29055,9 +30982,7 @@
         if(!this.freehandMode(evt)) {
             var index = this.line.geometry.components.length - 1;
             this.line.geometry.removeComponent(this.line.geometry.components[index]);
-            if(this.persist) {
-                this.destroyPoint();
-            }
+            this.removePoint();
             this.finalize();
         }
         return false;
@@ -29227,6 +31152,47 @@
         return intersect;
     },
 
+    /**
+     * APIMethod: distanceTo
+     * Calculate the closest distance between two geometries (on the x-y plane).
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
+     *
+     * Valid options:
+     * details - {Boolean} Return details from the distance calculation.
+     *     Default is false.
+     * edge - {Boolean} Calculate the distance from this geometry to the
+     *     nearest edge of the target geometry.  Default is true.  If true,
+     *     calling distanceTo from a geometry that is wholly contained within
+     *     the target will result in a non-zero distance.  If false, whenever
+     *     geometries intersect, calling distanceTo will return 0.  If false,
+     *     details cannot be returned.
+     *
+     * Returns:
+     * {Number | Object} The distance between this geometry and the target.
+     *     If details is true, the return will be an object with distance,
+     *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
+     *     the coordinates of the closest point on this geometry. The x1 and y1
+     *     properties represent the coordinates of the closest point on the
+     *     target geometry.
+     */
+    distanceTo: function(geometry, options) {
+        var edge = !(options && options.edge === false);
+        var result;
+        // this is the case where we might not be looking for distance to edge
+        if(!edge && this.intersects(geometry)) {
+            result = 0;
+        } else {
+            result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
+                this, [geometry, options]
+            );
+        }
+        return result;
+    },
+
     CLASS_NAME: "OpenLayers.Geometry.Polygon"
 });
 
@@ -29293,18 +31259,20 @@
      * Create a Polygon Handler.
      *
      * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object with a 'done' property whos value is
-     *                          a function to be called when the path drawing is
-     *                          finished. The callback should expect to recieve a
-     *                          single argument, the polygon geometry.
-     *                          If the callbacks object contains a 'point'
-     *                          property, this function will be sent each point
-     *                          as they are added.  If the callbacks object contains
-     *                          a 'cancel' property, this function will be called when
-     *                          the handler is deactivated while drawing.  The cancel
-     *                          should expect to receive a geometry.
-     * options - {Object} 
+     * control - {<OpenLayers.Control>} The control that owns this handler
+     * callbacks - {Object} An object with a properties whose values are
+     *     functions.  Various callbacks described below.
+     * options - {Object} An optional object with properties to be set on the
+     *           handler
+     *
+     * Named callbacks:
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the polygon geometry.
+     * point - Called as each point is added.  Receives the new point geometry.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * cancel - Called when the handler is deactivated while drawing.  The
+     *     cancel callback will receive a geometry.
      */
     initialize: function(control, callbacks, options) {
         OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
@@ -29313,15 +31281,22 @@
     /**
      * Method: createFeature
      * Add temporary geometries
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
+     *     feature.
      */
-    createFeature: function() {
+    createFeature: function(pixel) {
+        var lonlat = this.control.map.getLonLatFromPixel(pixel);
+        this.point = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
+        );
+        this.line = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.LinearRing([this.point.geometry])
+        );
         this.polygon = new OpenLayers.Feature.Vector(
-                                        new OpenLayers.Geometry.Polygon());
-        this.line = new OpenLayers.Feature.Vector(
-                                        new OpenLayers.Geometry.LinearRing());
-        this.polygon.geometry.addComponent(this.line.geometry);
-        this.point = new OpenLayers.Feature.Vector(
-                                        new OpenLayers.Geometry.Point());
+            new OpenLayers.Geometry.Polygon([this.line.geometry])
+        );
         this.layer.addFeatures([this.polygon, this.point], {silent: true});
     },
 
@@ -29335,18 +31310,6 @@
     },
 
     /**
-     * Method: modifyFeature
-     * Modify the existing geometry given the new point
-     * 
-     */
-    modifyFeature: function() {
-        var index = this.line.geometry.components.length - 2;
-        this.line.geometry.components[index].x = this.point.geometry.x;
-        this.line.geometry.components[index].y = this.point.geometry.y;
-        this.line.geometry.components[index].clearBounds();
-    },
-
-    /**
      * Method: drawFeature
      * Render geometries on the temporary layer.
      */
@@ -29356,6 +31319,17 @@
     },
     
     /**
+     * Method: getSketch
+     * Return the sketch feature.
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>}
+     */
+    getSketch: function() {
+        return this.polygon;
+    },
+
+    /**
      * Method: getGeometry
      * Return the sketch geometry.  If <multi> is true, this will return
      *     a multi-part geometry.
@@ -29384,9 +31358,7 @@
             // remove the penultimate point
             var index = this.line.geometry.components.length - 2;
             this.line.geometry.removeComponent(this.line.geometry.components[index]);
-            if(this.persist) {
-                this.destroyPoint();
-            }
+            this.removePoint();
             this.finalize();
         }
         return false;

Modified: branches/fusion-2.0/lib/OpenLayers/theme/default/framedCloud.css
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/theme/default/framedCloud.css	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/OpenLayers/theme/default/framedCloud.css	2009-04-03 19:40:15 UTC (rev 1830)
@@ -1,5 +0,0 @@
-
-.olFramedCloudPopupContent {
-    padding: 5px;
-    overflow: auto;
-}
\ No newline at end of file

Copied: branches/fusion-2.0/lib/OpenLayers/theme/default/ie6-style.css (from rev 1828, trunk/lib/OpenLayers/theme/default/ie6-style.css)
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/theme/default/ie6-style.css	                        (rev 0)
+++ branches/fusion-2.0/lib/OpenLayers/theme/default/ie6-style.css	2009-04-03 19:40:15 UTC (rev 1830)
@@ -0,0 +1,7 @@
+.olControlZoomPanel div {
+    background-image: url(img/zoom-panel-NOALPHA.png);
+}
+.olControlPanPanel div {
+    background-image: url(img/pan-panel-NOALPHA.png);
+}
+

Copied: branches/fusion-2.0/lib/OpenLayers/theme/default/img/pan-panel-NOALPHA.png (from rev 1828, trunk/lib/OpenLayers/theme/default/img/pan-panel-NOALPHA.png)
===================================================================
(Binary files differ)

Copied: branches/fusion-2.0/lib/OpenLayers/theme/default/img/pan-panel.png (from rev 1828, trunk/lib/OpenLayers/theme/default/img/pan-panel.png)
===================================================================
(Binary files differ)

Copied: branches/fusion-2.0/lib/OpenLayers/theme/default/img/zoom-panel-NOALPHA.png (from rev 1828, trunk/lib/OpenLayers/theme/default/img/zoom-panel-NOALPHA.png)
===================================================================
(Binary files differ)

Copied: branches/fusion-2.0/lib/OpenLayers/theme/default/img/zoom-panel.png (from rev 1828, trunk/lib/OpenLayers/theme/default/img/zoom-panel.png)
===================================================================
(Binary files differ)

Modified: branches/fusion-2.0/lib/OpenLayers/theme/default/style.css
===================================================================
--- branches/fusion-2.0/lib/OpenLayers/theme/default/style.css	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/OpenLayers/theme/default/style.css	2009-04-03 19:40:15 UTC (rev 1830)
@@ -40,22 +40,15 @@
    font-size: xx-small;
 }
 .olControlScaleLineBottom {
-   background-color: #000;
-   border: 1px solid #fff;
-   margin: 1px;
-    font-size: 9px;
-    padding-left: 2px;
-    line-height: 12px;
-    color: #fff;
+   border: solid 2px black;
+   border-bottom: none;
+   margin-top:-2px;
+   text-align: center;
 }
 .olControlScaleLineTop {
-   background-color: #000;
-   border: 1px solid #fff;
-   margin: 1px;
-    font-size: 9px;
-    padding-left: 2px;
-    line-height: 12px;
-    color: #fff;
+   border: solid 2px black;
+   border-top: none;
+   text-align: center;
 }
 
 .olControlPermalink {
@@ -82,12 +75,11 @@
 }
 
 .olControlOverviewMapElement {
-/* modified for fusion
+/* modified for fusion    
     padding: 10px 18px 10px 10px;
     background-color: #00008B;
     -moz-border-radius: 1em 0 0 0;
-    */
-}
+    */}
 
 .olControlOverviewMapMinimizeButton {
     right: 0px;
@@ -250,15 +242,90 @@
     filter: alpha(opacity=50);
 }   
 
-/* 
- * Due to current limitations in the OpenLayers code, you can only
- * replace this image with another image which is 17px x 17px. 
- */   
+.olControlPanPanel {
+    top: 10px;
+    left: 5px;
+}  
+
+.olControlPanPanel div {
+    background-image: url(img/pan-panel.png);
+    height: 18px;
+    width: 18px;
+    cursor: pointer;
+    position: absolute;
+}
+
+.olControlPanPanel .olControlPanNorthItemInactive {
+    top: 0px;
+    left: 9px;
+    background-position: 0px 0px;
+}
+.olControlPanPanel .olControlPanSouthItemInactive {
+    top: 36px;
+    left: 9px;
+    background-position: 18px 0px;
+}
+.olControlPanPanel .olControlPanWestItemInactive {
+    position: absolute;
+    top: 18px;
+    left: 0px;
+    background-position: 0px 18px;
+}
+.olControlPanPanel .olControlPanEastItemInactive {
+    top: 18px;
+    left: 18px;
+    background-position: 18px 18px;
+}
+
+.olControlZoomPanel {
+    top: 71px;
+    left: 14px;
+} 
+
+.olControlZoomPanel div {
+    background-image: url(img/zoom-panel.png);
+    position: absolute;
+    height: 18px;
+    width: 18px;
+    cursor: pointer;
+}
+
+.olControlZoomPanel .olControlZoomInItemInactive {
+    top: 0px;
+    left: 0px;
+    background-position: 0px 0px;
+}
+
+.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
+    top: 18px;
+    left: 0px;
+    background-position: 0px -18px;
+}
+
+.olControlZoomPanel .olControlZoomOutItemInactive {
+    top: 36px;
+    left: 0px;
+    background-position: 0px 18px;
+}
+
 .olPopupCloseBox {
   background: url("img/close.gif") no-repeat;
   cursor: pointer;
 }
 
+.olFramedCloudPopupContent {
+    padding: 5px;
+    overflow: auto;
+}
+
 .olControlNoSelect {
  -moz-user-select: none;
 }
+
+/**
+ * Cursor styles
+ */
+
+.olCursorWait {
+    cursor: wait;
+}
\ No newline at end of file

Modified: branches/fusion-2.0/lib/Widget.js
===================================================================
--- branches/fusion-2.0/lib/Widget.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/Widget.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -85,7 +85,10 @@
     deactivate: function() { },
 
     setUiObject: function(uiObj) {
-        Fusion.Widget.uiInstances[this.type].push(uiObj);
+        Fusion.Widget.uiInstances[this.type].push(this);
+        if (this.widgetTag.tooltip) {
+          uiObj.setTooltip(this.widgetTag.tooltip);
+        }
         if (this.isExclusive) {
             this.getMap().buttonSet.add(uiObj);
         }
@@ -93,7 +96,9 @@
             this.activate();
         }
         if (uiObj.addEvents) {
-            if (Fusion.Widget.uiInstances[this.type][0].options.active) {
+            if (Fusion.Widget.uiInstances[this.type][0].uiObj &&
+                Fusion.Widget.uiInstances[this.type][0].uiObj.options.active &&
+                Fusion.Widget.uiInstances[this.type][0].shouldActivateWith(this)) {
                 uiObj.options.active = true;
                 if (uiObj.domA) {
                     uiObj.domA.addClass('jx' + uiObj.options.type + 'Active');
@@ -108,9 +113,11 @@
                     var instances = Fusion.Widget.uiInstances[this.type];
                     for (var i=0; i<instances.length; i++) {
                         var instance = instances[i];
-                        instance.options.active = false;
-                        if (instance.domA) {
-                            instance.domA.removeClass('jx' + instance.options.type + 'Active');
+                        if (instance.shouldActivateWith(this) && instance.uiObj) {
+                            instance.uiObj.options.active = false;
+                            if (instance.uiObj.domA) {
+                                instance.uiObj.domA.removeClass('jx' + instance.uiObj.options.type + 'Active');
+                            }                            
                         }
                     }
                     this.deactivate();
@@ -119,9 +126,11 @@
                     var instances = Fusion.Widget.uiInstances[this.type];
                     for (var i=0; i<instances.length; i++) {
                         var instance = instances[i];
-                        instance.options.active = true;
-                        if (instance.domA) {
-                            instance.domA.addClass('jx' + instance.options.type + 'Active');
+                        if (instance.shouldActivateWith(this) && instance.uiObj) {
+                            instance.uiObj.options.active = true;
+                            if (instance.uiObj.domA) {
+                                instance.uiObj.domA.addClass('jx' + instance.uiObj.options.type + 'Active');
+                            }                            
                         }
                     }
                     this.activate();
@@ -130,6 +139,12 @@
         }
         this.uiObj = uiObj; 
     },
+    
+    /**
+     */
+    shouldActivateWith: function(widget) {
+        return true;
+    },
 
     /**
      * set the map object that this widget is associated with

Modified: branches/fusion-2.0/lib/fusion.js
===================================================================
--- branches/fusion-2.0/lib/fusion.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/fusion.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -800,7 +800,7 @@
         getFusionURL: function() {return this.fusionURL;},
     
         getConfigurationItem: function(arch, key) { 
-            if (this.configuration[arch] && this.configuration[arch][key]) { 
+            if (this.configuration[arch] && (this.configuration[arch][key]!='undefined')) { 
                 return this.configuration[arch][key]; 
             } 
             return null; 

Modified: branches/fusion-2.0/lib/jxlib.uncompressed.js
===================================================================
--- branches/fusion-2.0/lib/jxlib.uncompressed.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/lib/jxlib.uncompressed.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -4288,7 +4288,6 @@
 	},
 
 	start: function(event){
-	    event.stop();
 		if (this.options.preventDefault) event.preventDefault();
 		this.fireEvent('beforeStart', this.element);
 		this.mouse.start = event.page;
@@ -5232,7 +5231,6 @@
 	},
 
 	clickedElement: function(event){
-	    event.stop();
 		var dir = this.range < 0 ? -1 : 1;
 		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
 		position = position.limit(-this.options.offset, this.full -this.options.offset);
@@ -5444,7 +5442,7 @@
  * reset.css - Copyright (c) 2006, Yahoo! Inc. All rights reserved.
  * Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt
  ******************************************************************************
- * Jx UI Library, 2.0-beta-6
+ * Jx UI Library, 2.0-rc-1
  * Copyright (c) 2006-2008, DM Solutions Group Inc. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -5465,7 +5463,7 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  *****************************************************************************/
-// $Id: common.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: common.js 303 2009-04-02 18:52:21Z pagameba $
 /**
  * Class: Jx
  * Jx is a global singleton object that contains the entire Jx library
@@ -5504,6 +5502,12 @@
     }    
 })();
 
+Class.Mutators.Family = function(self,name) {
+    self.$family = {'name': name};
+    return self;
+}
+
+
 /* Setup global namespace
  * If jxcore is loaded by jx.js, then the namespace and baseURL are
  * already established
@@ -5536,13 +5540,22 @@
                  * }
                  * (end)
                  */ 
-                 Jx.aPixel = document.createElement('img');
+                 Jx.aPixel = document.createElement('img', {alt:'',title:''});
                  Jx.aPixel.src = matches[1]+'/a_pixel.png';
                  Jx.baseURL = Jx.aPixel.src.substring(0,
                      Jx.aPixel.src.indexOf('a_pixel.png'));
                 
             }
         }
+	   /**
+        * Determine if we're running in Adobe AIR. If so, determine which sandbox we're in
+        */
+		var src = aScripts[0].src;
+        if (src.contains('app:')){
+		    Jx.isAir = true;
+        } else {
+		    Jx.isAir = false;
+	    }
     //});
 } 
 
@@ -6024,13 +6037,16 @@
             }
         } else if (this.options.contentURL) {
             this.contentIsLoaded = false;
-            new Request({
+            this.req = new Request({
                 url: this.options.contentURL, 
                 method:'get',
                 update: element,
                 onSuccess:(function(html) {
                     element.innerHTML = html;
                     this.contentIsLoaded = true;
+					if (Jx.isAir){
+						$clear(this.reqTimeout);
+					}
                     this.fireEvent('contentLoaded', this);
                 }).bind(this), 
                 onFailure: (function(){
@@ -6038,7 +6054,12 @@
                     this.fireEvent('contentLoadFailed', this);
                 }).bind(this),
                 headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'}
-            }).send();
+            });
+            this.req.send();
+            if (Jx.isAir) {
+                var timeout = $defined(this.options.timeout) ? this.options.timeout : 10000;
+                this.reqTimeout = this.checkRequest.delay(timeout, this);
+            }
         } else {
             this.contentIsLoaded = true;
         }
@@ -6071,7 +6092,39 @@
     }
 });
 
+
 /**
+ * It seems AIR never returns an XHR that "fails" by not finding the 
+ * appropriate file when run in the application sandbox and retrieving a local
+ * file. This affects Jx.ContentLoader in that a "failed" event is never fired. 
+ * 
+ * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
+ * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
+ * XHR and fire the failure event.
+ *
+ * This code only gets added if we're in AIR.
+ */
+if (Jx.isAir){
+	Jx.ContentLoader.implement({
+		/**
+		 * Method: checkRequest()
+		 * Is fired after a delay to check the request to make sure it's not
+		 * failing in AIR.
+		 */
+		checkRequest: function(){
+			if (this.req.xhr.readyState === 1) {
+				//we still haven't gotten the file. Cancel and fire the
+				//failure
+				$clear(this.reqTimeout);
+				this.req.cancel();
+				this.contentIsLoaded = true;
+				this.fireEvent('contentLoadFailed', this);
+			}
+		}
+	});
+}
+
+/**
  * Class: Jx.AutoPosition
  * Mix-in class that provides a method for positioning
  * elements relative to other elements.
@@ -6139,6 +6192,7 @@
         var ver = $splat(options.vertical || ['center center']);
         var offsets = $merge({top:0,right:0,bottom:0,left:0}, options.offsets || {});
         
+        var coords = relative.getCoordinates(); //top, left, width, height
         var page;
         var scroll;
         if (!$(element.parentNode) || element.parentNode ==  document.body) {
@@ -6148,7 +6202,18 @@
             page = $(element.parentNode).getContentBoxSize(); //width, height
             scroll = $(element.parentNode).getScroll();
         }
-        var coords = relative.getCoordinates(); //top, left, width, height
+        if (relative == document.body) {
+            // adjust coords for the scroll offsets to make the object
+            // appear in the right part of the page.
+            coords.left += scroll.x;
+            coords.top += scroll.y;            
+        } else if (element.parentNode == relative) {
+            // if the element is opening *inside* its relative, we want
+            // it to position correctly within it so top/left becomes
+            // the reference system.
+            coords.left = 0;
+            coords.top = 0;
+        }
         var size = element.getMarginBoxSize(); //width, height
         var left;
         var right;
@@ -6329,7 +6394,10 @@
      */
     makeChrome: function(element) {
         var c = new Element('div', {
-            'class':'jxChrome'      
+            'class':'jxChrome',
+            events: {
+                contextmenu: function(e) { e.stop(); }
+            }      
         });
         
         /* add to element so we can get the background image style */
@@ -6343,8 +6411,9 @@
         c.setStyle('padding', 0);
         
         /* get the chrome image from the background image of the element */
+		/* the app: protocol check is for adobe air support */
         var src = c.getStyle('backgroundImage');
-        if (!(src.contains('http://') || src.contains('https://') || src.contains('file://'))) {
+        if (!(src.contains('http://') || src.contains('https://') || src.contains('file://') || src.contains('app:/'))) {
             src = null;
         } else {
             src = src.slice(4,-1);
@@ -6366,7 +6435,9 @@
                     }).adopt(
                     new Element('img',{
                         'class':'png24',
-                        src:src
+                        src:src,
+                        alt: '',
+                        title: ''
                     }))
                 );
             }, this);
@@ -6446,8 +6517,12 @@
         $(this.addable || this.domObj).inject(reference,where);
         this.fireEvent('addTo',this);
         return this;
+    },
+    
+    toElement: function() {
+        return this.addable || this.domObj;
     }
-});// $Id: button.js 242 2008-12-10 15:19:21Z pagameba $
+});// $Id: button.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Button
  * Jx.Button creates a clickable element that can be added to a web page.
@@ -6540,6 +6615,7 @@
  */
  
 Jx.Button = new Class({
+    Family: 'Jx.Button',
     /**
      * Implements:
      * * Options
@@ -6671,7 +6747,9 @@
         if (this.options.image || !this.options.label) {
             var i = new Element('img', {
                 'class':'jx'+this.options.type+'Icon',
-                'src': Jx.aPixel.src
+                'src': Jx.aPixel.src,
+                alt: '',
+                title: ''
             });
             //if image is not a_pixel, set the background image of the image
             //otherwise let the default css take over.
@@ -6806,7 +6884,9 @@
             if (!this.domImg) {
                 var i = new Element('img', {
                     'class':'jx'+this.options.type+'Icon',
-                    'src': Jx.aPixel.src
+                    'src': Jx.aPixel.src,
+                    alt: '',
+                    title: ''
                 });
                 if (this.options.imageClass) {
                     i.addClass(this.options.imageClass);
@@ -6875,7 +6955,7 @@
     blur: function() {
         this.domA.blur();
     }
-});// $Id: button.flyout.js 247 2008-12-10 19:21:02Z fred.warnock $
+});// $Id: button.flyout.js 261 2009-03-31 18:36:52Z pagameba $
 /**
  * Class: Jx.Button.Flyout
  * Flyout buttons expose a panel when the user clicks the button.  The
@@ -6929,6 +7009,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Button.Flyout = new Class({
+    Family: 'Jx.Button.Flyout',
     /**
      * Extends:
      * <Jx.Button>
@@ -6962,11 +7043,6 @@
         if (!Jx.Button.Flyout.Stack) {
             Jx.Button.Flyout.Stack = [];
         }
-        var content = null;
-        if (options && options.content) {
-            content = options.content;
-            options.content = null;
-        }
         this.parent(options);
         this.domA.addClass('jx'+this.options.type+'Flyout');
         
@@ -6983,7 +7059,6 @@
         this.contentContainer.adopt(this.content);
         
         this.content.store('jxFlyout', this);
-        this.options.content = content;
         this.loadContent(this.content);
         this.keypressWatcher = this.keypressHandler.bindWithEvent(this);
         this.hideWatcher = this.clickHandler.bindWithEvent(this);
@@ -7101,7 +7176,7 @@
             Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
         }
     }
-});// $Id: button.multi.js 246 2008-12-10 19:09:41Z fred.warnock $
+});// $Id: button.multi.js 276 2009-04-01 14:32:54Z pagameba $
 /**
  * Class: Jx.Button.Multi
  * Multi buttons are used to contain multiple buttons in a drop down list
@@ -7154,6 +7229,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Button.Multi = new Class({
+    Family: 'Jx.Button.Multi',
     /**
      * Extends: 
      * <Jx.Button>
@@ -7223,11 +7299,13 @@
                 document.addEvent('mousedown', this.hideWatcher);
                 document.addEvent('keyup', this.keypressWatcher);
                 
+                /* why were we doing this? It was affecting issue 13
                 if (e.$extended) {
                     e.stop();                                        
                 } else if (e.event && e.event.$extended) {
                     e.event.stop();
                 }
+                */
                 this.fireEvent('show', this);
             }).bindWithEvent(this.menu),
             'mouseenter':(function(){
@@ -7275,7 +7353,11 @@
                 }
             }).bind(this)
         });
-        a.adopt(new Element('img', {src: Jx.aPixel.src}));
+        a.adopt(new Element('img', {
+            src: Jx.aPixel.src,
+            alt: '',
+            title: ''
+        }));
         this.domObj.adopt(a);
         this.discloser = a;
         if (this.options.items) {
@@ -7388,7 +7470,7 @@
         this.setActiveButton(button);
         button.clicked();
     }
-});// $Id: colorpalette.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: colorpalette.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.ColorPalette
  * A Jx.ColorPalette presents a user interface for selecting colors.  Currently,
@@ -7418,6 +7500,7 @@
  */
 
 Jx.ColorPalette = new Class({
+    Family: 'Jx.ColorPalette',
     /**
      * Implements:
      * * Options
@@ -7434,7 +7517,8 @@
         parent: null,
         color: '#000000',
         alpha: 1,
-        hexColors: ['00', '33', '66', '99', 'CC', 'FF']
+        hexColors: ['00', '33', '66', '99', 'CC', 'FF'],
+        alphaLabel: 'alpha (%)'
     },
     /**
      */
@@ -7491,7 +7575,7 @@
 
         top.adopt(this.colorInput);
 
-        this.alphaLabel = new Element('label', {'class':'jxAlphaLabel', 'html':'alpha (%)'});
+        this.alphaLabel = new Element('label', {'class':'jxAlphaLabel', 'html':this.options.alphaLabel});
         top.adopt(this.alphaLabel);
 
         this.alphaInput = new Element('input', {
@@ -7692,7 +7776,7 @@
     }
 });
 
-// $Id: button.color.js 241 2008-12-10 15:11:05Z pagameba $ 
+// $Id: button.color.js 258 2009-03-31 17:26:30Z pagameba $ 
 /**
  * Class: Jx.Button.Color
  * A <Jx.ColorPalette> wrapped up in a Jx.Button.  The button includes a
@@ -7720,6 +7804,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Button.Color = new Class({
+    Family: 'Jx.Button.Color',
     /**
      * Extends:
      * <Jx.Button.Flyout>
@@ -7866,7 +7951,7 @@
         this.selectedSwatch.setStyles(styles);
     }
 });
-// $Id: buttonset.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: buttonset.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.ButtonSet
  * A ButtonSet manages a set of <Jx.Button> instances by ensuring that only one
@@ -7895,6 +7980,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.ButtonSet = new Class({
+    Family: 'Jx.ButtonSet',
     /**
      * Implements:
      * * Events
@@ -7998,7 +8084,7 @@
 
 
 
-// $Id: grid.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: grid.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Grid
  * A tabular control that has fixed scrolling headers on the rows and columns
@@ -8030,6 +8116,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Grid = new Class({
+    Family: 'Jx.Grid',
     Implements: [Options, Events, Jx.Addable],
     domObj : null,
     model : null,
@@ -8923,6 +9010,7 @@
  * has selected a cell by clicking on the cell in the grid.
  */
 Jx.Grid.Model = new Class({
+    Family: 'Jx.Grid.Model',
     Implements: [Events, Options],
     options: {
         colHeaderHeight: 28,
@@ -8974,7 +9062,7 @@
     
     }
 });
-// $Id: layout.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: layout.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Layout
  * Jx.Layout is used to provide more flexible layout options for applications
@@ -9001,6 +9089,7 @@
  */
  
 Jx.Layout = new Class({
+    Family: 'Jx.Layout',
     /** 
      * Implements:
      * * Options
@@ -9375,7 +9464,7 @@
 
         this.fireEvent('sizeChange',this);
     }
-});// $Id: menu.js 244 2008-12-10 15:32:49Z pagameba $
+});// $Id: menu.js 304 2009-04-02 18:57:18Z pagameba $
 /**
  * Class: Jx.Menu
  * A main menu as opposed to a sub menu that lives inside the menu.
@@ -9394,6 +9483,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Menu = new Class({
+    Family: 'Jx.Menu',
     /**
      * Implements:
      * * Options
@@ -9522,6 +9612,9 @@
      */
     eventInMenu: function(e) {
         var target = $(e.target);
+        if (!target) {
+            return false;
+        }
         if (target.descendantOf(this.domObj) ||
             target.descendantOf(this.subDomObj)) {
             return true;
@@ -9552,7 +9645,11 @@
      */
     hide: function(e) {
         if (e) {
-            if (this.eventInMenu(e)) {
+            if (this.visibleItem && this.visibleItem.eventInMenu) {
+                if (this.visibleItem.eventInMenu(e)) {
+                    return;
+                }
+            } else if (this.eventInMenu(e)) {
                 return;
             }
         }
@@ -9579,6 +9676,7 @@
         var e = o.event;
         if (Jx.Menu.Menus[0]) {
             if (Jx.Menu.Menus[0] != this) {
+                Jx.Menu.Menus[0].button.blur();
                 Jx.Menu.Menus[0].hide(e);
             } else {
                 this.hide();
@@ -9589,7 +9687,7 @@
             return;
         }
         Jx.Menu.Menus[0] = this;
-        
+        this.button.focus();
         this.contentContainer.setStyle('visibility','hidden');
         this.contentContainer.setStyle('display','block');
         $(document.body).adopt(this.contentContainer);            
@@ -9612,7 +9710,9 @@
             this.button.domA.addClass('jx'+this.button.options.type+'Active');            
         }
         if (e) {
-            e.stop();
+            //why were we doing this? it is affecting the closing of
+            //other elements like flyouts (issue 13)
+            //e.stop();
         }
         /* fix bug in IE that closes the menu as it opens because of bubbling */
         document.addEvent('mousedown', this.hideWatcher);
@@ -9645,7 +9745,7 @@
     }
 });
 
-// $Id: menu.item.js 242 2008-12-10 15:19:21Z pagameba $
+// $Id: menu.item.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Menu.Item
  * A menu item is a single entry in a menu.  It is typically composed of
@@ -9668,6 +9768,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Menu.Item = new Class({
+    Family: 'Jx.Menu.Item',
     /**
      * Extends: 
      * <Jx.Button>
@@ -9692,12 +9793,10 @@
      * options - {Object} an object containing options for <Jx.Button::initialize>
      */
     initialize: function(options) {
-        this.parent($merge(
-            {
+        this.parent($merge({
                 image: Jx.aPixel.src
             },
-            options, 
-            {
+            options, {
                 container:'li',
                 type:'MenuItem',
                 toggleClass: (options.image ? null : this.options.toggleClass)
@@ -9760,7 +9859,7 @@
     }
 });
 
-// $Id: menu.separator.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: menu.separator.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Menu.Separator
  * A convenience class to create a visual separator in a menu.
@@ -9775,6 +9874,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Menu.Separator = new Class({
+    Family: 'Jx.Menu.Separator',
     /**
      * Property: domObj
      * {HTMLElement} the HTML element that the separator is contained
@@ -9815,7 +9915,7 @@
      * Show the menu item
      */
     show: $empty
-});// $Id: menu.submenu.js 244 2008-12-10 15:32:49Z pagameba $
+});// $Id: menu.submenu.js 305 2009-04-02 18:58:16Z pagameba $
 /**
  * Class: Jx.Menu.SubMenu
  * A sub menu contains menu items within a main menu or another
@@ -9834,6 +9934,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Menu.SubMenu = new Class({
+    Family: 'Jx.Menu.SubMenu',
     /**
      * Extends:
      * <Jx.Menu.Item>
@@ -9841,12 +9942,10 @@
     Extends: Jx.Menu.Item,
     /**
      * Implements:
-     * * Options
-     * * Events
      * * <Jx.AutoPosition>
      * * <Jx.Chrome>
      */
-    Implements: [Options, Events, Jx.AutoPosition, Jx.Chrome],
+    Implements: [Jx.AutoPosition, Jx.Chrome],
     /**
      * Property: subDomObj
      * {HTMLElement} the HTML container for the sub menu.
@@ -9931,6 +10030,11 @@
     },
     
     eventInMenu: function(e) {
+        if (this.visibleItem && 
+            this.visibleItem.eventInMenu && 
+            this.visibleItem.eventInMenu(e)) {
+            return true;
+        }
         return $(e.target).descendantOf(this.domObj) ||
                $(e.target).descendantOf(this.subDomObj) ||
                this.items.some(
@@ -10066,7 +10170,7 @@
             this.visibleItem.show();
         }
     }
-});// $Id: menu.context.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: menu.context.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Menu.Context
  * A <Jx.Menu> that has no button but can be opened at a specific 
@@ -10085,6 +10189,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Menu.Context = new Class({
+    Family: 'Jx.Menu.Context',
     /** Extends:
      * <Jx.Menu>
      */
@@ -10140,7 +10245,7 @@
 
         e.stop();
     }    
-});// $Id: panel.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: panel.js 269 2009-03-31 21:53:04Z pagameba $
 /**
  * Class: Jx.Panel
  * A panel is a fundamental container object that has a content
@@ -10164,6 +10269,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Panel = new Class({
+    Family: 'Jx.Panel',
     /**
      * Implements:
      * * <Jx.ContentLoader>
@@ -10184,11 +10290,19 @@
         position: 'absolute',
         height: null,
         collapse: true,
+        collapseTooltip: 'Collapse/Expand Panel',
+        collapseLabel: 'Collapse',
+        expandLabel: 'Expand',
+        maximizeTooltip: 'Maximize Panel',
+        maximizeLabel: 'Maximize',
         detach: false,
         close: false,
+        closeTooltip: 'Close Panel',
+        closeLabel: 'Close',
         closed: false,
         hideTitle: false,
-        type: 'Panel'
+        type: 'Panel',
+        toolbars: []
     },
     
     /** 
@@ -10228,16 +10342,9 @@
      *     container's width (using the relative option in <Jx.Layout>).
      */
     initialize : function(options){
+        this.setOptions(options);
         this.toolbars = options ? options.toolbars || [] : [];
         
-        /* ugly hack around $unlink in mootools */
-        var content = null;
-        if (options && options.content) {
-            content = options.content;
-            options.content = null;
-        }
-        this.setOptions(options);
-        
         if ($defined(this.options.height) && !$defined(options.position)) {
             this.options.position = 'relative';
         }
@@ -10249,7 +10356,9 @@
         
         var i = new Element('img', {
             'class': 'jx'+this.options.type+'Icon',
-            src: Jx.aPixel.src
+            src: Jx.aPixel.src,
+            alt: '',
+            title: ''
         });
         if (this.options.image) {
             i.setStyle('backgroundImage', 'url('+this.options.image+')');
@@ -10284,7 +10393,7 @@
         if (this.options.collapse) {
             var b = new Jx.Button({
                 image: Jx.aPixel.src,
-                tooltip: 'Collapse/Expand Panel',
+                tooltip: this.options.collapseTooltip,
                 onClick: function() {
                     that.toggleCollapse();
                 }
@@ -10293,14 +10402,15 @@
             this.toolbar.add(b);
             if (this.menu) {
                 var item = new Jx.Menu.Item({
-                    label: 'Collapse',
+                    label: this.options.collapseLabel,
                     onClick: function() { that.toggleCollapse(); }
                 });
-                this.addEvent('collapse', function() {
-                    if (that.options.closed) {
-                        item.setLabel('Expand');
-                    } else {
-                        item.setLabel('Collapse');
+                this.addEvents({
+                    collapse: function() {
+                        item.setLabel(this.options.expandLabel);
+                    },
+                    expand: function() {
+                        item.setLabel(this.options.collapseLabel);
                     }
                 });
                 this.menu.add(item);
@@ -10310,7 +10420,7 @@
         if (this.options.maximize) {
             var b = new Jx.Button({
                 image: Jx.aPixel.src,
-                tooltip: 'Maximize Panel',
+                tooltip: this.options.maximizeTooltip,
                 onClick: function() {
                     that.maximize();
                 }
@@ -10319,7 +10429,7 @@
             this.toolbar.add(b);
             if (this.menu) {
                 var item = new Jx.Menu.Item({
-                    label: 'Maximize',
+                    label: this.options.maximizeLabel,
                     onClick: function() { that.maximize(); }
                 });
                 this.menu.add(item);
@@ -10329,7 +10439,7 @@
         if (this.options.close) {
             var b = new Jx.Button({
                 image: Jx.aPixel.src,
-                tooltip: 'Close Panel',
+                tooltip: this.options.closeTooltip,
                 onClick: function() {
                     that.close();
                 }
@@ -10338,7 +10448,7 @@
             this.toolbar.add(b);
             if (this.menu) {
                 var item = new Jx.Menu.Item({
-                    label: 'Close',
+                    label: this.options.closeLabel,
                     onClick: function() {
                         that.close();
                     }
@@ -10371,17 +10481,18 @@
         });
         this.domObj.adopt(this.contentContainer);
         
-        for (var i=0; i<this.toolbars.length; i++) {
-            var tb = this.toolbars[i];
-            var position = tb.options.position;
-            var tbc = this.toolbarContainers[position];
-            if (!tbc) {
-                var tbc = new Element('div');
-                new Jx.Layout(tbc);
-                this.contentContainer.adopt(tbc);
-                this.toolbarContainers[position] = tbc;
-            }
-            tb.addTo(tbc);
+        if ($type(this.options.toolbars) == 'array') {
+            this.options.toolbars.each(function(tb){
+                var position = tb.options.position;
+                var tbc = this.toolbarContainers[position];
+                if (!tbc) {
+                    var tbc = new Element('div');
+                    new Jx.Layout(tbc);
+                    this.contentContainer.adopt(tbc);
+                    this.toolbarContainers[position] = tbc;
+                }
+                tb.addTo(tbc);
+            }, this);
         }
         
         this.content = new Element('div', {
@@ -10391,10 +10502,9 @@
         this.contentContainer.adopt(this.content);
         new Jx.Layout(this.contentContainer);
         new Jx.Layout(this.content);
-        /* continue ugly $unlink hack */
-        this.options.content = content;
+        
         this.loadContent(this.content);
-        
+
         this.toggleCollapse(this.options.closed);
         
         this.addEvent('addTo', function() {
@@ -10443,38 +10553,39 @@
                     this.toolbarContainers[position].style.height = '';                
                 }
             }, this);
-            for (var i=0; i<this.toolbars.length; i++) {
-                tb = this.toolbars[i];
-                position = tb.options.position;
-                tbc = this.toolbarContainers[position];
-                // IE 6 doesn't seem to want to measure the width of things
-                // correctly
-                if (Browser.Engine.trident4) {
-                    var oldParent = $(tbc.parentNode);
-                    tbc.style.visibility = 'hidden';
-                    $(document.body).adopt(tbc);                    
-                }
-                var size = tbc.getBorderBoxSize();
-                // put it back into its real parent now we are done measuring
-                if (Browser.Engine.trident4) {
-                    oldParent.adopt(tbc);
-                    tbc.style.visibility = '';
-                }
-                switch(position) {
-                    case 'top':
-                        top = size.height;
-                        break;
-                    case 'bottom':
-                        bottom = size.height;
-                        break;
-                    case 'left':
-                        left = size.width;
-                        break;
-                    case 'right':
-                        right = size.width;
-                        break;
-                }
-            
+            if ($type(this.options.toolbars) == 'array') {
+                this.options.toolbars.each(function(tb){
+                    position = tb.options.position;
+                    tbc = this.toolbarContainers[position];
+                    // IE 6 doesn't seem to want to measure the width of 
+                    // things correctly
+                    if (Browser.Engine.trident4) {
+                        var oldParent = $(tbc.parentNode);
+                        tbc.style.visibility = 'hidden';
+                        $(document.body).adopt(tbc);                    
+                    }
+                    var size = tbc.getBorderBoxSize();
+                    // put it back into its real parent now we are done 
+                    // measuring
+                    if (Browser.Engine.trident4) {
+                        oldParent.adopt(tbc);
+                        tbc.style.visibility = '';
+                    }
+                    switch(position) {
+                        case 'top':
+                            top = size.height;
+                            break;
+                        case 'bottom':
+                            bottom = size.height;
+                            break;
+                        case 'left':
+                            left = size.width;
+                            break;
+                        case 'right':
+                            right = size.width;
+                            break;
+                    }                    
+                },this);
             }
             tbc = this.toolbarContainers['top'];
             if (tbc) {
@@ -10564,12 +10675,12 @@
         if (url.indexOf('?') == -1) {
             url = url + '?';
         }
-        //var ts = (new Date()).getTime();
-        //url = url + 'ts='+ts;
-        var opts = { method: 'get',
-                     onComplete:this.panelContentLoaded.bind(this),
-                     requestHeaders: ['If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT']};
-        var a = new Request(url, opts).send();
+        var a = new Request({
+            url: url,
+            method: 'get',
+            onComplete:this.panelContentLoaded.bind(this),
+            requestHeaders: ['If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT']
+        }).send();
     },
     /**
      * Method: panelContentLoaded
@@ -10647,7 +10758,7 @@
         this.fireEvent('close', this);
     }
     
-});// $Id: dialog.js 232 2008-12-02 12:42:14Z pagameba $
+});// $Id: dialog.js 291 2009-04-01 18:31:32Z pagameba $
 /**
  * Class: Jx.Dialog
  * A Jx.Dialog implements a floating dialog.  Dialogs represent a useful way
@@ -10678,12 +10789,16 @@
  * change - triggered when the value of an input in the dialog is changed
  * resize - triggered when the dialog is resized
  *
+ * Extends:
+ * Jx.Dialog extends <Jx.Panel>, please go there for more details.
+ *
  * License: 
  * Copyright (c) 2008, DM Solutions Group Inc.
  * 
  * This file is licensed under an MIT style license
  */
 Jx.Dialog = new Class({
+    Family: 'Jx.Dialog',
     /**
      * Extends:
      * <Jx.Panel>
@@ -10715,6 +10830,7 @@
         id: '',
         parent: null,
         resize: false,
+        resizeTooltip: '',
         move: true,
         close: true,
         collapse: true
@@ -10759,13 +10875,6 @@
         this.isOpening = false;
         this.firstShow = true;
         
-        /* ugly hack around $unlink in mootools */
-        var content = null;
-        if (options && options.content) {
-            content = options.content;
-            options.content = null;
-        }
-        
         /* initialize the panel overriding the type and position */
         this.parent($merge(
             {parent:document.body}, // these are defaults that can be overridden
@@ -10773,13 +10882,9 @@
             {type:'Dialog', position: 'absolute'} // these override anything passed to the options
         ));
         
-        /* ugly hack continued */
-        this.options.content = content;
-        this.loadContent(this.content);
-
         this.options.parent = $(this.options.parent);
         
-        if (!window.opera && this.options.modal) {
+        if (this.options.modal) {
             this.blanket = new Element('div',{
                 'class':'jxDialogModal',
                 styles:{
@@ -10835,6 +10940,7 @@
         if (this.options.resize) {
             this.resizeHandle = new Element('div', {
                 'class':'jxDialogResize',
+                title: this.options.resizeTooltip,
                 styles: {
                     'display':this.options.closed?'none':'block'
                 }
@@ -10876,6 +10982,34 @@
     },
     
     /**
+     * Method: resize
+     * resize the dialog.  This can be called when the dialog is closed
+     * or open.
+     *
+     * Parameters:
+     * width - the new width
+     * height - the new height
+     * autoPosition - boolean, false by default, if resizing an open dialog
+     * setting this to true will reposition it according to its position
+     * rules.
+     */
+    resize: function(width, height, autoPosition) {
+        this.options.width = width;
+        this.options.height = height;
+        if (this.domObj.getStyle('display') != 'none') {
+            this.layoutContent();
+            this.domObj.resize(this.options);
+            this.fireEvent('resize');
+            this.resizeChrome(this.domObj);
+            if (autoPosition) {
+                this.position(this.domObj, this.options.parent, this.options);                
+            }
+        } else {
+            this.firstShow = false;
+        }
+    },
+    
+    /**
      * Method: sizeChanged
      * overload panel's sizeChanged method
      */
@@ -10932,17 +11066,6 @@
     },
     
     /**
-     * Method: setTitle
-     * set the text of the dialog title.
-     *
-     * Parameters: 
-     * title - {String} the new title
-     */
-    setTitle: function( title ) {
-        this.title.childNodes[0].innerHTML = title;
-    },
-
-    /**
      * Method: show
      * show the dialog, external code should use the <Jx.Dialog::open> method
      * to make the dialog visible.
@@ -11010,6 +11133,23 @@
         
     },
     /**
+     * Method: openURL
+     * open the dialog and load content from the provided url.  If you don't
+     * provide a URL then the dialog opens normally.
+     *
+     * Parameters:
+     * url - <String> the url to load when opening.
+     */
+    openURL: function(url) {
+        if (url) {
+            this.options.contentURL = url;
+            this.loadContent(this.content);
+        } else {
+            this.open();
+        }
+    },
+    
+    /**
      * Method: open
      * open the dialog.  This may be delayed depending on the 
      * asynchronous loading of dialog content.  The onOpen
@@ -11024,6 +11164,8 @@
             this.show();
             this.fireEvent('open', this);
             this.isOpening = false;
+        } else {
+            this.addEvent('contentLoaded', this.open.bind(this));
         }
     },
     /**
@@ -11035,18 +11177,6 @@
         this.isOpening = false;
         this.hide();
         this.fireEvent('close');
-    },
-    /**
-     * Method: onContentLoaded
-     * handle the dialog content being loaded.  This triggers
-     * processing of inputs and the onContentLoaded callback
-     * function (if necessary).  Also, if the dialog was previously
-     * requested to be opened, this will actually open it.
-     */
-    onContentLoaded : function() {
-        if (this.isOpening) {
-            this.open();
-        }
     }
 });
 
@@ -11066,7 +11196,7 @@
     });
     
 };
-// $Id: panelset.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: panelset.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.PanelSet
  *
@@ -11097,6 +11227,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.PanelSet = new Class({
+    Family: 'Jx.PanelSet',
     /**
      * Implements:
      * * Options
@@ -11107,7 +11238,8 @@
     
     options: {
         parent: null,
-        panels: []
+        panels: [],
+        barTooltip: 'drag this bar to resize'
     },
     
     /**
@@ -11165,7 +11297,7 @@
             prepareBar: (function(i) {
                 var bar = new Element('div', {
                     'class': 'jxPanelBar',
-                    'title': 'drag this bar to resize'
+                    'title': this.options.barTooltip
                 });
                 
                 var panel = this.panels[i];
@@ -11296,7 +11428,7 @@
         }
         panel.domObj.resize({top: top, height:panelSize, bottom: null});
     }
-});// $Id: button.combo.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: button.combo.js 295 2009-04-02 13:40:34Z pagameba $
 /**
  * Class: Jx.Button.Combo
  * A drop down list of selectable items.  Items can be either a string, an image or both.
@@ -11326,17 +11458,12 @@
  * This file is licensed under an MIT style license
  */
 Jx.Button.Combo = new Class({
+    Family: 'Jx.Button.Combo',
     /**
      * Extends:
      * <Jx.Button.Multi>
      */
     Extends: Jx.Button.Multi,
-    /**
-     * Implements:
-     * * <Jx.AutoPosition>
-     * * <Jx.Chrome>
-     */
-    Implements: [Jx.AutoPosition, Jx.Chrome],
     domObj : null,
     ul : null,
     /**
@@ -11359,12 +11486,23 @@
      * is editable.
      */
     initialize: function(options) {
-        this.parent();
+        this.parent(); //we don't want to pass options to parent
         this.setOptions(options);
         this.domA.removeClass('jxButtonMulti');
-        this.domA.addClass('jxButtonComboDefault');
-        
         if (this.options.editable) {
+            // remove the button's normal A tag and replace it with a span
+            // so the input ends up not being inside an A tag - this was
+            // causing all kinds of problems for selecting text inside it
+            // due to some user-select: none classes that were introduced
+            // to make buttons not selectable in the first place.
+            //
+            // Ultimately I think we want to fix this so that the discloser
+            // in Jx.Button.Multi is a separate beast and we can use it here
+            // without inheriting from multi buttons
+            var s = new Element('span', {'class':'jxButton'});
+            s.adopt(this.domA.firstChild);
+            this.domA = s.replaces(this.domA);
+            this.domA.addClass('jxButtonComboDefault');
             this.domA.addClass('jxButtonEditCombo');
             this.domInput = new Element('input',{
                 type:'text',
@@ -11380,12 +11518,12 @@
                 },
                 value: this.options.label
             });
+            this.domLabel.empty();
             this.domLabel.addClass('jxComboInput');
             this.domLabel.adopt(this.domInput);
         } else {
             this.discloser.dispose();
             this.domA.addClass('jxButtonCombo');
-            //this.setLabel(this.options.label);
             this.addEvent('click', (function(e){
                 this.discloser.fireEvent('click', e);
             }).bindWithEvent(this));
@@ -11530,7 +11668,7 @@
         }
         return value;
     }
-});// $Id: splitter.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: splitter.js 282 2009-04-01 17:23:37Z pagameba $
 /**
  * Class: Jx.Splitter
  * a Jx.Splitter creates two or more containers within a parent container
@@ -11552,6 +11690,7 @@
  */
  
 Jx.Splitter = new Class({
+    Family: 'Jx.Splitter',
     /**
      * Implements:
      * * Options
@@ -11587,7 +11726,10 @@
         containerOptions: [],
         barOptions: [],
         layout: 'horizontal',
-        snaps: []
+        snaps: [],
+        barTooltip: 'drag this bar to resize',
+        onStart: null,
+        onFinish: null
     },
     /**
      * Constructor: Jx.Splitter
@@ -11750,7 +11892,7 @@
     prepareBar: function() {
         var o = new Element('div', {
             'class': 'jxSplitBar'+this.options.layout.capitalize(),
-            'title': 'drag this bar to resize'
+            'title': this.options.barTitle
         });
         return o;
     },
@@ -11771,6 +11913,7 @@
             fn = this.dragVertical;
         }
         this.bars.each(function(bar){
+            var mask;
             new Drag(bar, {
                 //limit: limit,
                 modifiers: modifiers,
@@ -11778,11 +11921,25 @@
                     obj.addClass('jxSplitBarDrag');
                 },
                 onComplete : (function(obj) {
+                    mask.destroy();
                     obj.removeClass('jxSplitBarDrag');
                     if (obj.retrieve('splitterObj') != this) {
                         return;
                     }
                     fn.apply(this,[obj]);
+                }).bind(this),
+                onBeforeStart: function(obj) {
+                    mask = new Element('div',{'class':'jxSplitterMask'}).inject(obj, 'after');
+                },
+                onStart: (function() {
+                    if (this.options.onStart) {
+                        this.options.onStart();
+                    }
+                }).bind(this),
+                onFinish: (function() {
+                    if (this.options.onFinish) {
+                        this.options.onFinish();
+                    }
                 }).bind(this)
             });
         }, this);
@@ -12215,7 +12372,7 @@
              }
          }
     }
-});// $Id: splitter.snap.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: splitter.snap.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Splitter.Snap
  * A helper class to create an element that can snap a split panel open or
@@ -12231,6 +12388,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Splitter.Snap = new Class({
+    Family: 'Jx.Splitter.Snap',
     /**
      * Property: snap
      * {HTMLElement} the DOM element of the snap (the thing that gets
@@ -12335,7 +12493,7 @@
             }
         }
     }
-});// $Id: tabset.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: tabset.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.TabSet
  * A TabSet manages a set of <Jx.Button.Tab> content areas by ensuring that only one
@@ -12372,6 +12530,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.TabSet = new Class({
+    Family: 'Jx.TabSet',
     Implements: [Options,Events],
     /**
      * Property: tabs
@@ -12479,7 +12638,7 @@
 
 
 
-// $Id: tabbox.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: tabbox.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.TabBox
  * A convenience class to handle the common case of a single toolbar
@@ -12504,6 +12663,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.TabBox = new Class({
+    Family: 'Jx.TabBox',
     /**
      * Implements:
      * * Options
@@ -12536,7 +12696,8 @@
         this.setOptions(options);
         this.tabBar = new Jx.Toolbar({
             type: 'TabBar', 
-            position: this.options.position
+            position: this.options.position,
+            scroll: this.options.scroll
         });
         this.panel = new Jx.Panel({
             toolbars: [this.tabBar],
@@ -12609,7 +12770,7 @@
         this.tabSet.remove(tab);
     }
 });
-// $Id: button.tab.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: button.tab.js 261 2009-03-31 18:36:52Z pagameba $
 /**
  * Class: Jx.Button.Tab
  * A single tab in a tab set.  A tab has a label (displayed in the tab) and a
@@ -12650,6 +12811,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Button.Tab = new Class({
+    Family: 'Jx.Button.Tab',
     /**
      * Extends:
      * <Jx.Button>
@@ -12677,15 +12839,9 @@
      * valid options.
      */
     initialize : function( options) {
-        var content;
-        if (options && options.content) {
-            content = options.content;
-            options.content = null;
-        }
         this.parent($merge(options, {type:'Tab', toggle:true}));
         this.content = new Element('div', {'class':'tabContent'});
         new Jx.Layout(this.content, options);
-        this.options.content = content;
         this.loadContent(this.content);
         var that = this;
         this.addEvent('down', function(){that.content.addClass('tabContentActive');});
@@ -12701,7 +12857,11 @@
                     }).bind(this)
                 } 
             });
-            a.adopt(new Element('img', {src: Jx.aPixel.src}));
+            a.adopt(new Element('img', {
+                src: Jx.aPixel.src,
+                alt: '',
+                title: ''
+            }));
             this.domObj.adopt(a);
         }
     },
@@ -12715,7 +12875,7 @@
             this.setActive(true);            
         }
     }
-});// $Id: toolbar.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: toolbar.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Toolbar
  * A toolbar is a container object that contains other objects such as
@@ -12763,6 +12923,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Toolbar = new Class({
+    Family: 'Jx.Toolbar',
     Implements: [Options,Events],
     /**
      * Property: items
@@ -12944,7 +13105,7 @@
         this.fireEvent('show', item);
     }
 });
-// $Id: toolbar.item.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: toolbar.item.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Toolbar.Item
  * A helper class to provide a container for something to go into 
@@ -12956,6 +13117,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Toolbar.Item = new Class( {
+    Family: 'Jx.Toolbar.Item',
     Implements: [Options],
     options: {
         active: true
@@ -12990,7 +13152,7 @@
             }
         }
     }
-});// $Id: toolbar.separator.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: toolbar.separator.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Toolbar.Separator
  * A helper class that represents a visual separator in a <Jx.Toolbar>
@@ -13005,6 +13167,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Toolbar.Separator = new Class({
+    Family: 'Jx.Toolbar.Separator',
     /**
      * Property: domObj
      * {HTMLElement} The DOM element that goes in the <Jx.Toolbar>
@@ -13020,7 +13183,7 @@
         this.domObj.appendChild(this.domSpan);
     }
 });
-// $Id: toolbar.container.js 241 2008-12-10 15:11:05Z pagameba $
+// $Id: toolbar.container.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Toolbar.Container
  * A toolbar container contains toolbars.  A single toolbar container fills the
@@ -13042,6 +13205,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Toolbar.Container = new Class({
+    Family: 'Jx.Toolbar.Container',
     Implements: [Options,Events, Jx.Addable],
     /**
      * Property: domObj
@@ -13164,7 +13328,6 @@
     },
     
     measure: function() {
-        
         if ((!this.scrollLeftSize || !this.scrollLeftSize.x) && this.domObj.parentNode) {
             this.scrollLeftSize = this.scrollLeft.domObj.getSize();
             this.scrollRightSize = this.scrollRight.domObj.getSize();
@@ -13265,8 +13428,22 @@
     scrollIntoView: function(item) {
         var width = this.domObj.getSize().x;
         var coords = item.domObj.getCoordinates(this.scroller);
-        var l = this.scroller.getStyle('left').toInt();
-        
+		
+		//left may be set to auto or even a zero length string. 
+		//In the previous version, in air, this would evaluate to
+		//NaN which would cause the right hand scroller to show when 
+		//the component was first created.
+		
+		//So, get the left value first
+        var l = this.scroller.getStyle('left');
+		//then check to see if it's auto or a zero length string 
+		if (l === 'auto' || l.length <= 0) {
+			//If so, set to 0.
+			l = 0;
+		} else {
+			//otherwise, convert to int
+			l = l.toInt();
+		}
         var slSize = this.scrollLeftSize ? this.scrollLeftSize.x : 0;
         var srSize = this.scrollRightSize ? this.scrollRightSize.x : 0;
         
@@ -13295,13 +13472,12 @@
         } else {
             this.scrollRight.domObj.setStyle('visibility', '');                
         }
-        
         if (left != l) {
             this.scrollFx.start('left', left);
         }
     }
 });
-// $Id: treeitem.js 239 2008-12-10 14:53:58Z pagameba $
+// $Id: treeitem.js 301 2009-04-02 16:12:11Z pagameba $
 /**
  * Class: Jx.TreeItem 
  * An item in a tree.  An item is a leaf node that has no children.
@@ -13327,6 +13503,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.TreeItem = new Class ({
+    Family: 'Jx.TreeItem',
     Implements: [Options,Events],
     /**
      * Property: domObj
@@ -13374,7 +13551,12 @@
             this.domObj.id = this.options.id;
         }
       
-        this.domNode = new Element('img',{'class': 'jxTreeImage', src: Jx.aPixel.src});
+        this.domNode = new Element('img',{
+            'class': 'jxTreeImage', 
+            src: Jx.aPixel.src,
+            alt: '',
+            title: ''
+        });
         this.domObj.appendChild(this.domNode);
         
         this.domLabel = (this.options.draw) ? 
@@ -13389,7 +13571,12 @@
         }
     },
     draw: function() {
-        var domImg = new Element('img',{'class':'jxTreeIcon', src: Jx.aPixel.src});
+        var domImg = new Element('img',{
+            'class':'jxTreeIcon', 
+            src: Jx.aPixel.src,
+            alt: '',
+            title: ''
+        });
         if (this.options.image) {
             domImg.setStyle('backgroundImage', 'url('+this.options.image+')');
         }
@@ -13407,14 +13594,17 @@
         domA.addEvents({
             click: this.selected.bind(this),
             dblclick: this.selected.bind(this),
-            contextmenu: this.showMenu.bind(this),
             drag: function(e) {e.stop();},
-            mousedown: function(e) {
-                domA.addClass('jxTreeItemPressed');
-                hasFocus = true;
-                mouseDown = true;
-                domA.focus();
-            },
+            contextmenu: function(e) { e.stop(); },
+            mousedown: (function(e) {
+               domA.addClass('jxTreeItemPressed');
+               hasFocus = true;
+               mouseDown = true;
+               domA.focus();
+               if (e.rightClick && this.options.contextMenu) {
+                   this.options.contextMenu.show(e);
+               }
+            }).bind(this),
             mouseup: function(e) {
                 domA.removeClass('jxTreeItemPressed');
                 mouseDown = false;
@@ -13502,25 +13692,9 @@
      * e - {Event} the DOM event
      */
     selected : function(e) {
-        this.lastEvent = new Event(e);
         this.fireEvent('click', this);
     },
     /**
-     * Method: showMenu
-     * Called when the DOM element for the TreeItem is right-clicked.  The
-     * node is selected and the context menu displayed (if there is one).
-     *
-     * Parameters:
-     * e - {Event} the DOM event
-     */
-    showMenu: function(e) {
-        this.lastEvent = new Event(e);
-        if (this.contextMenu) {
-            this.contextMenu.show(this.lastEvent);
-        }
-        this.lastEvent.stop();
-    },
-    /**
      * Method: getName
      * Get the label associated with a TreeItem
      *
@@ -13544,7 +13718,7 @@
             this.domObj.addClass('jxDisabled');
         }
     }
-});// $Id: treefolder.js 240 2008-12-10 15:04:11Z pagameba $
+});// $Id: treefolder.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.TreeFolder
  * A Jx.TreeFolder is an item in a tree that can contain other items.  It is
@@ -13563,6 +13737,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.TreeFolder = new Class({
+    Family: 'Jx.TreeFolder',
     Extends: Jx.TreeItem,
     /**
      * Property: subDomObj
@@ -13607,7 +13782,6 @@
         this.nodes = [];
         this.subDomObj = new Element('ul', {'class':'jxTree'});
         this.domObj.appendChild(this.subDomObj);
-        this.subDomObj.className = 'jxTree';
         if (this.options.open) {
             this.expand();
         } else {
@@ -13878,7 +14052,7 @@
         }
         return null;
     }
-});// $Id: tree.js 241 2008-12-10 15:11:05Z pagameba $
+});// $Id: tree.js 258 2009-03-31 17:26:30Z pagameba $
 /**
  * Class: Jx.Tree
  * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
@@ -13895,6 +14069,7 @@
  * This file is licensed under an MIT style license
  */
 Jx.Tree = new Class({
+    Family: 'Jx.Tree',
     Implements: [Jx.Addable],
     Extends: Jx.TreeFolder,
     /**

Modified: branches/fusion-2.0/templates/mapserver/standard/ApplicationDefinition.xml
===================================================================
--- branches/fusion-2.0/templates/mapserver/standard/ApplicationDefinition.xml	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/templates/mapserver/standard/ApplicationDefinition.xml	2009-04-03 19:40:15 UTC (rev 1830)
@@ -14,7 +14,7 @@
         <Type>MapServer</Type>
         <SingleTile>true</SingleTile>
         <Extension>
-            <MapFile>/ms4w/apps/gmap/htdocs/gmap_groups.map</MapFile>
+            <MapFile>/home/pdeschamps/fgs/apps/gmap-orig/htdocs/gmap_groups.map</MapFile>
         </Extension>
       </Map>
     </MapGroup>
@@ -23,7 +23,7 @@
         <Type>MapServer</Type>
         <SingleTile>true</SingleTile>
         <Extension>
-            <MapFile>/ms4w/apps/gmap/htdocs/gmap75.map</MapFile>
+            <MapFile>/home/pdeschamps/fgs/apps/gmap-orig/htdocs/gmap75.map</MapFile>
         </Extension>
       </Map>
     </MapGroup>
@@ -49,7 +49,7 @@
         <Type>MapServer</Type>
         <SingleTile>true</SingleTile>
         <Extension>
-            <MapFile>/ms4w/apps/gmap/htdocs/gmapLonLat.map</MapFile>
+            <MapFile>/home/pdeschamps/fgs/apps/gmap-orig/htdocs/gmap75.map</MapFile>
             <Options>
               <isBaseLayer>false</isBaseLayer>
               <singleTile>true</singleTile>
@@ -443,6 +443,7 @@
       <Label>Refresh Map</Label>
       <Disabled/>
     </Widget>
+    
 
 <!-- CONTEXT MENU ZOOM FIXED (OUT) -->
 
@@ -1169,6 +1170,43 @@
       <Disabled/>
     </Widget>
 
+<!-- MAPTIPS -->
+    <Widget xsi:type="UiWidgetType">
+      <Name>Maptip</Name>
+      <Type>Maptip</Type>
+      <Extension xsi:type="CustomContentType">
+        <Delay>250</Delay>
+        <Tolerance>5</Tolerance>
+        <Maptip>
+            <Layer>popplace </Layer>
+            <Label>Populated Places</Label>
+            <TextField>NAME</TextField>
+            <CustomURL>http://localhost/populationInfo.php?cityname=[NAME]</CustomURL>
+        </Maptip>
+        <Maptip>
+            <Layer>park</Layer>
+            <TextField>NAME_E</TextField>   
+        </Maptip>
+        <Maptip>
+            <Layer>park</Layer>
+            <Label>Area KMSQ</Label>
+            <TextField>AREA_KMSQ</TextField>
+            <CustomURL>http://localhost/showParkArea.php?parkId=[PARK_ID]&amp;areakmsq=[AREA_KMSQ]</CustomURL>
+        </Maptip>
+        
+        <Maptip>
+            <Layer>Precipitation</Layer>
+            <Label>Station ID</Label>
+            <TextField>STATN_ID</TextField>
+        </Maptip>
+        <Maptip>
+            <Layer>park</Layer>
+            <Label>Year Established</Label>
+            <TextField>YEAR_EST</TextField>
+        </Maptip>
+      </Extension>
+    </Widget>
+
     </WidgetSet>
 
   <Extension/>

Modified: branches/fusion-2.0/templates/mapserver/standard/index.html
===================================================================
--- branches/fusion-2.0/templates/mapserver/standard/index.html	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/templates/mapserver/standard/index.html	2009-04-03 19:40:15 UTC (rev 1830)
@@ -97,7 +97,8 @@
     <div id="SelectionPanel"></div>
     <div id="OverviewMap"></div>
 </div>
-<div id="Map"><div id="Navigator"></div></div>
+<div id="Map"><div id="Navigator"></div>
+<div id="Maptip"></div></div>
 <div id="Statusbar"></div>
 <div id="TaskPane"></div>
 <div id="PoweredBy" class="statusBarItem">
@@ -107,7 +108,7 @@
 </div>
 
 </div>
-
+<div id="Maptip"></div>
 <div id="BusyIndicator" style="visibility: hidden;">
 </div>
 

Modified: branches/fusion-2.0/widgets/ColorPicker.js
===================================================================
--- branches/fusion-2.0/widgets/ColorPicker.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/ColorPicker.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -63,12 +63,13 @@
     
     setUiObject: function(uiObj) {
         Fusion.Widget.prototype.setUiObject.apply(this, [uiObj]);
-        this.uiObj.addEvent('colorChange', OpenLayers.Function.bind(this.colorChanged, this));
+        this.uiObj.setColor(this.color);
+        this.uiObj.addEvent('change', OpenLayers.Function.bind(this.colorChanged, this));
     },
     
     colorChanged: function(button) {
-        var a = parseInt(this.uiObj.alpha*255/100).toString(16);
-        var c = a + this.uiObj.color.substring(1);
+        var a = parseInt(button.options.alpha*255/100).toString(16);
+        var c = a + button.options.color.substring(1);
         if (this.colorInput) {
             this.colorInput.value = c;
         }

Modified: branches/fusion-2.0/widgets/LayerManager.js
===================================================================
--- branches/fusion-2.0/widgets/LayerManager.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/LayerManager.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -42,7 +42,7 @@
  *
  * LayerThemeIcon: (string, optional)
  *
- * The url to an image to use for layers that are currently themed.
+ * The url to an image toTopography use for layers that are currently themed.
  *
  * DisabledLayerIcon: (string, optional)
  *
@@ -102,7 +102,7 @@
       var map = this.getMap();
       for (var i=0; i<map.aMaps.length; ++i) {
         var mapBlock = document.createElement('li');
-        this.mapBlock.className = 'jxLmanMap';
+        mapBlock.className = 'jxLmanMap';
         mapBlock.id = 'mapBlock_'+i;
         
         //add a handle so the map blocks can be re-arranged
@@ -145,11 +145,13 @@
         blockItem.layer = processArray[i];
       }
       
+      /*
       var options = [];
       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);
+      */
     },
    
   createItemHtml: function(parent, layer) {
@@ -189,15 +191,15 @@
   },
   
   setGrabCursor: function(ev) {
-    this.setCursor(this.cursorNormal, Event.element(ev) );
+   // this.setCursor(this.cursorNormal, Event.element(ev) );
   },
   
   setDragCursor: function(ev) {
-    this.setCursor(this.cursorDrag, Event.element(ev) );
+   // this.setCursor(this.cursorDrag, Event.element(ev) );
   },
   
   setNormalCursor: function(ev) {
-    this.setCursor('auto', Event.element(ev) );
+    //this.setCursor('auto', Event.element(ev) );
   },
   
   setCursor : function(cursor, domObj) {

Modified: branches/fusion-2.0/widgets/Legend.js
===================================================================
--- branches/fusion-2.0/widgets/Legend.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/Legend.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -59,7 +59,7 @@
     /**
      * Constant: defaultRootFolderIcon
      * {String} The default image for the root folder
-     */   
+     */
     defaultRootFolderIcon: 'images/icons/legend-map.png',
     
     /**
@@ -74,7 +74,7 @@
      */
     defaultGroupInfoIcon: 'images/icons/tree_group_info.png',
     
-    initializeWidget: function(widgetTag) {           
+    initializeWidget: function(widgetTag) {
         // TODO: maybe it's a good idea to do a function like Fusion.Widget.BindRenderer.. for limit the code
         // duplication if we plan to apply this pattern to others widgets
         Fusion.addWidgetStyleSheet(widgetTag.location + 'Legend/Legend.css');
@@ -85,7 +85,7 @@
         if (json.LegendRenderer)
         {
             var renderer = eval(json.LegendRenderer[0]);
-            if (renderer && renderer.prototype.CLASS_NAME 
+            if (renderer && renderer.prototype.CLASS_NAME
                 && renderer.prototype.CLASS_NAME == "Fusion.Widget.Legend.LegendRenderer") {
                 this.renderer = new renderer(this, widgetTag);
             } else if (typeof renderer == "function") {
@@ -102,13 +102,13 @@
         }
 
         if (this.renderer.mapReloaded)
-            this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, 
+            this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED,
                                            OpenLayers.Function.bind(this.renderer.mapReloaded, this.renderer));
         if (this.renderer.mapLoading)
-            this.getMap().registerForEvent(Fusion.Event.MAP_LOADING, 
+            this.getMap().registerForEvent(Fusion.Event.MAP_LOADING,
                                            OpenLayers.Function.bind(this.renderer.mapLoading,this.renderer));
         if (this.renderer.mapLoaded)
-            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, 
+            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED,
                                            OpenLayers.Function.bind(this.renderer.mapLoaded, this.renderer));
     }
 });
@@ -187,15 +187,15 @@
 
 /* Class: Fusion.Widget.Legend.LegendRendererDefault
  * This class provide a default legend as a collapsable tree.
- * 
+ *
  */
 
-Fusion.Widget.Legend.LegendRendererDefault = OpenLayers.Class(Fusion.Widget.Legend.LegendRenderer,  
+Fusion.Widget.Legend.LegendRendererDefault = OpenLayers.Class(Fusion.Widget.Legend.LegendRenderer,
 {
     /**
      * Property: showRootFolder
      * {Boolean} This controls whether the tree will have a single root node that
-     * contains the name of the map as its label.  By default, the root node does 
+     * contains the name of the map as its label.  By default, the root node does
      * not appear.  Set to "true" or "1" to make the root node appear.
      */
     showRootFolder: false,
@@ -231,7 +231,7 @@
         this.imgLayerDWFIcon = json.LayerDWFIcon ? json.LayerDWFIcon[0] : this.oLegend.defaultLayerDWFIcon;
         this.imgLayerRasterIcon = json.LayerRasterIcon ? json.LayerRasterIcon[0] : this.oLegend.defaultLayerRasterIcon;
         this.imgLayerThemeIcon = json.LayerThemeIcon ? json.LayerThemeIcon[0] : this.oLegend.defaultLayerThemeIcon;
-        this.imgDisabledLayerIcon = json.DisabledLayerIcon ? json.DisabledLayerIcon[0] : this.oLegend.defaultDisabledLayerIcon;       
+        this.imgDisabledLayerIcon = json.DisabledLayerIcon ? json.DisabledLayerIcon[0] : this.oLegend.defaultDisabledLayerIcon;
         this.imgLayerInfoIcon = json.LayerInfoIcon ? json.LayerInfoIcon[0] : this.oLegend.defaultLayerInfoIcon;
         this.imgGroupInfoIcon = json.GroupInfoIcon ? json.GroupInfoIcon[0] : this.oLegend.defaultGroupInfoIcon;
        
@@ -251,10 +251,20 @@
             label: OpenLayers.i18n('defaultMapTitle'),
             open: true,
             draw: this.renderFolder,
+            contextMenu: this.getContextMenu(),
             'class':'fusionLegendFolder'
         };
         this.oRoot = new Jx.TreeFolder(opt);
-        this.oRoot.contextMenu = this.getContextMenu(this.oRoot);
+        this.oRoot.options.contextMenu.add(
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('collapse'),
+                onClick: OpenLayers.Function.bind(this.collapseBranch, this, this.oRoot)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('expand'),
+                onClick: OpenLayers.Function.bind(this.expandBranch, this, this.oRoot)
+            })
+        );
         
         this.oTree.append(this.oRoot);
         
@@ -267,47 +277,23 @@
         this.extentsChangedWatcher = this.update.bind(this);
     },
     
-    getContextMenu: function(folder) {
-        if (folder instanceof Jx.TreeFolder) {
-                        return new Jx.Menu.Context(this.name).add(
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('collapse'),
-                    onClick: OpenLayers.Function.bind(this.collapseBranch, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('expand'),
-                    onClick: OpenLayers.Function.bind(this.expandBranch, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('refresh'),
-                    onClick: OpenLayers.Function.bind(this.update, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('collapseAll'),
-                    onClick: OpenLayers.Function.bind(this.collapseAll, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('expandAll'),
-                    onClick: OpenLayers.Function.bind(this.expandAll, this, folder)
-                })
-            );
-        } else {
-            return new Jx.Menu.Context(this.name).add(
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('refresh'),
-                    onClick: OpenLayers.Function.bind(this.update, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('collapseAll'),
-                    onClick: OpenLayers.Function.bind(this.collapseAll, this, folder)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('expandAll'),
-                    onClick: OpenLayers.Function.bind(this.expandAll, this, folder)
-                })
-            );
-        }
+    getContextMenu: function() {
+        return new Jx.Menu.Context(this.name).add(
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('refresh'),
+                onClick: OpenLayers.Function.bind(this.update, this)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('collapseAll'),
+                onClick: OpenLayers.Function.bind(this.collapseAll, this)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('expandAll'),
+                onClick: OpenLayers.Function.bind(this.expandAll, this)
+            })
+        );
     },
+    
     expandAll: function(folder) {
         for (var i=0; i<this.oTree.nodes.length; i++) {
             var item = this.oTree.nodes[i];
@@ -425,12 +411,22 @@
                 label: group.legendLabel,
                 open: group.expandInLegend,
                 draw: this.renderFolder,
-                'class':'fusionLegendFolder'                
+                contextMenu: this.getContextMenu(),
+                'class':'fusionLegendFolder'
             };
             group.legend.treeItem = new Jx.TreeFolder(opt);
-            group.legend.treeItem.contextMenu = this.getContextMenu(group.legend.treeItem);
             group.legend.treeItem.domObj.store('data', group);
-            
+            group.legend.treeItem.options.contextMenu.add(
+                new Jx.Menu.Item({
+                    label: OpenLayers.i18n('collapse'),
+                    onClick: OpenLayers.Function.bind(this.collapseBranch, this, group.legend.treeItem)
+                }),
+                new Jx.Menu.Item({
+                    label: OpenLayers.i18n('expand'),
+                    onClick: OpenLayers.Function.bind(this.expandBranch, this, group.legend.treeItem)
+                })
+            );
+
             folder.append(group.legend.treeItem);
             group.legend.treeItem.checkBox.checked = group.visible?true:false;
             OpenLayers.Event.observe(group.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, group));
@@ -522,7 +518,7 @@
         }
         for (var i=0; i<group.layers.length; i++) {
             this.updateLayer(group.layers[i], fScale);
-        }   
+        }
     },
     updateLayer: function(layer, fScale) {
 
@@ -564,7 +560,7 @@
                     layer.legend.treeItem = this.createTreeItem(layer, style, fScale, this.bIncludeVisToggle);
                     OpenLayers.Event.observe(layer.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
                     
-                    layer.parentGroup.legend.treeItem.append(layer.legend.treeItem);                   
+                    layer.parentGroup.legend.treeItem.append(layer.legend.treeItem);
                 } else if (layer.legend.treeItem instanceof Jx.TreeFolder) {
                     this.clearTreeItem(layer);
                     layer.legend.treeItem = this.createTreeItem(layer, style, fScale, this.bIncludeVisToggle);
@@ -578,7 +574,7 @@
                     } else {
                         $(layer.legend.treeItem.domObj).addClass('jxDisabled');
                     }
-                }               
+                }
             }
             layer.legend.treeItem.checkBox.checked = layer.visible?true:false;
             if (layer.layerTypes[0] == 4 || range.styles.length > 0) {
@@ -616,11 +612,23 @@
             isOpen: layer.expandInLegend,
             draw: this.renderFolder,
             'class':'fusionLegendItemCheckbox',
+            contextMenu: this.getContextMenu(),
             // image overrides
             image: this.imgLayerThemeIcon
         };
         var folder = new Jx.TreeFolder(opt);
-        folder.contextMenu = this.getContextMenu(folder);
+        folder.options.contextMenu.add(
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('collapse'),
+                onClick: OpenLayers.Function.bind(this.collapseBranch, this, folder)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('expand'),
+                onClick: OpenLayers.Function.bind(this.expandBranch, this, folder)
+            })
+        );
+        
+        
         var layerInfo = layer.oMap.getLayerInfoUrl(layer.layerName);
         if (layerInfo) {
             var a = document.createElement('a');
@@ -661,9 +669,9 @@
         } else {
             opt.image = layer.oMap.getLegendImageURL(scale, layer, style);
         }
+        opt.contextMenu = this.getContextMenu(); 
 
         var item = new Jx.TreeItem(opt);
-        item.contextMenu = this.getContextMenu(item); 
         if (bCheckBox) {
             //item.domObj.insertBefore(layer.legend.checkBox, item.domObj.childNodes[1]);
             /* only need to add layer info if it has a check box too */
@@ -726,7 +734,7 @@
             events: {
                 click: this.selected.bindWithEvent(this),
                 dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.showMenu.bindWithEvent(this)
+                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
             }
         });
         domA.appendChild(this.domImg);
@@ -756,7 +764,7 @@
             events: {
                 click: this.selected.bindWithEvent(this),
                 dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.showMenu.bindWithEvent(this)
+                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
             }
         });
         
@@ -796,7 +804,7 @@
             events: {
                 click: this.selected.bindWithEvent(this),
                 dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.showMenu.bindWithEvent(this)
+                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
             }
         });
         

Modified: branches/fusion-2.0/widgets/Maptip.js
===================================================================
--- branches/fusion-2.0/widgets/Maptip.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/Maptip.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -59,9 +59,14 @@
     offset: new OpenLayers.Pixel(2,20),
     szTip: '',
     szHref:'',
+    szLabel:'',
+    aTextFields: null,
+    mapTipFired: false,
+    bStartMapTips:false,
     
     initializeWidget: function(widgetTag) {
-        var json = widgetTag.extension;
+        //var json = widgetTag.extension;
+        var json = widgetTag.widgetSet.getWidgetByName(this.name).extension;
         
         this.sTarget = json.Target ? json.Target[0] : "MaptipWindow";
         if (json.WinFeatures) {
@@ -70,15 +75,20 @@
         this.delay = json.Delay ? parseInt(json.Delay[0]) : 350;
         this.nTolerance = json.Tolerance ? parseInt(json.Tolerance[0]) : 2;
 
-        this.customURL =  json.CustomURL;
-        this.textField = json.TextField;
+        this.aCustomURL =   [];
+        this.aTextFields = []; 
         this.aLayers = [];
-        if (json.Layer) {
-            for (var i=0; i<json.Layer.length; i++) {
-                this.aLayers.push(json.Layer[i]);
+        this.aLabels = [];
+        
+        if (json.Maptip) {
+            for (var i=0; i<json.Maptip.length; i++) {
+                this.aLayers.push(json.Maptip[i].Layer);
+                this.aTextFields.push(json.Maptip[i].TextField);
+                this.aLabels.push(json.Maptip[i].Label);
+                this.aCustomURL.push(json.Maptip[i].CustomURL);
             }
         }
-        
+
         //prepare the container div for the maptips
         Fusion.addWidgetStyleSheet(widgetTag.location + 'Maptip/Maptip.css');
         if (this.domObj) {
@@ -111,6 +121,7 @@
         this.eventListener = false;
         this.getMap().registerForEvent(Fusion.Event.MAP_MAPTIP_REQ_FINISHED,OpenLayers.Function.bind(this._display,this));
         this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, this.busyChanged.bind(this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, this.startMapTips.bind(this));
     },
     
     mouseOut: function(e) {
@@ -123,32 +134,37 @@
             }
         }
     },
+
+    startMapTips: function(){
+        this.bStartMapTips = true;
+    },
     
     mouseMove: function(e) {
-        if(!this.eventListener){
-            
-            this.eventListener = true;
-        }
-      //console.log('map tip mouseMove');
-        if (this.bOverTip || this.mouseIsDown) {
-            return;
-        }
-        
-        var map = this.getMap();
-        this.mapSize = map.getSize();
-        this.mapOffset = map._oDomObj.offsets;
+        if( this.bStartMapTips == true){
+            if(!this.eventListener){
+                this.eventListener = true;
+            }
+        //console.log('map tip mouseMove');
+            if (this.bOverTip || this.mouseIsDown) {
+                return;
+            }
 
-        var p = map.getEventPosition(e);
-        this.oCurrentPosition = p;
-        this.oMapTipPosition = p;
+            var map = this.getMap();
+            this.mapSize = map.getSize();
+            this.mapOffset = map._oDomObj.offsets;
 
-        if (this.oCurrentPosition) {
-            window.clearTimeout(this.nTimer);
-            this.nTimer = null;
-        }
-        
-        this.nTimer = window.setTimeout(OpenLayers.Function.bind(this.showMaptip, this), this.delay);
-        //Event.stop(e);
+            var p = map.getEventPosition(e);
+            this.oCurrentPosition = p;
+            this.oMapTipPosition = p;
+
+            if(typeof( this.nTimer) == "number") {
+                window.clearTimeout(this.nTimer);
+                this.nTimer = null;
+            }
+
+            this.nTimer = window.setTimeout(OpenLayers.Function.bind(this.showMaptip, this), this.delay);
+            //Event.stop(e);
+        } // bStartMapTips 
     },
     
     mouseDown: function() {
@@ -161,13 +177,11 @@
     },
     
     showMaptip: function() {
-        //console.log("MAPTIP: showMaptip");
         this.getMap().getMapTip(this);
-       
+        this.mapTipFired = true;
     },
     
     _display: function(eventID,oMapTip) {
-        //console.log("MAPTIP: _display");
         if (typeof(oMapTip) == "undefined" || oMapTip.t == '') {
             return;
         }
@@ -178,33 +192,56 @@
             this.domObj.appendChild(contentDiv);
             var empty = true;
             this.bIsVisible = true;
-            var t = oMapTip.t;
-            this.szTip = t;
-            this.szHref = h;
-            if (t) {
-                contentDiv.innerHTML = t.replace(/\n/g, "<br>");
-                empty = false;
+            
+            var tip = oMapTip.t;
+            var hyperlink =oMapTip.h;
+            var label =oMapTip.l;
+            
+            this.szTip = tip;
+            this.szHref = hyperlink;
+            this.label = label;
+            
+            if(typeof(tip) == "object"){
+                for(var i=0;i<tip.length;i++){
+                    var t = tip[i];
+                    var h = hyperlink[i];
+                    var l = label[i];
+                    var oLinkDom = this.addMapTipDomObj(h,t);
+                    var mapTipContent = document.createElement('DIV');
+                    mapTipContent.className = "mapTipContentDIV";
+                    // has a label with the maptip
+                    if(l != ""){
+                        mapTipContent.innerHTML = l+" : ";
+                        contentDiv.appendChild(mapTipContent);
+                        mapTipContent.appendChild(oLinkDom);
+                        empty = false;
+                    }
+                    else
+                    {
+                        contentDiv.appendChild(mapTipContent);
+                        contentDiv.appendChild(oLinkDom);
+                        empty = false;
+                    }
+                }
             }
-            var h =oMapTip.h;
-            if (h) {
-                var a, linkURL;
-                var linkDiv = document.createElement('div');
-                if (h.indexOf('href=') > 0) {   //MGOS allows complete anchor tags as the hyperlink
-                linkDiv.innerHTML = h;
-                a = linkDiv.firstChild;
-                linkURL = a.href;
-                } else {
-                a = document.createElement('a');
-                a.innerHTML = h;
-                linkURL = h;
-                linkDiv.appendChild(a);
+            else
+            {
+                if (tip) {
+                    var mapTipContent = document.createElement('DIV');
+                    mapTipContent.innerHTML = tip.replace(/\n/g, "<br>");
+                    contentDiv.appendChild(mapTipContent);
+                    empty = false;
                 }
-                a.href = 'javascript:void(0)';
-                var openLink = OpenLayers.Function.bind(this.openLink, this, linkURL);
-                a.onclick = OpenLayers.Function.bindAsEventListener(openLink, this);
-                contentDiv.appendChild(linkDiv);
-                empty = false;
+
+                if (hyperlink) {
+                    var mapTipContent = document.createElement('DIV');
+                    mapTipContent.innerHTML =  hyperlink.replace(/\n/g, "<br>");
+                    contentDiv.appendChild(mapTipContent);
+                    empty = false;
+                }
             }
+
+            
             if (!empty) {
                 var size = $(this.domObj).getBorderBoxSize();
                 this.oMapTipPosition = this.oMapTipPosition.add(this.mapOffset[0], this.mapOffset[1]);
@@ -234,7 +271,38 @@
                 this.hideMaptip();
             }
         }
+        this.mapTipFired = false;
     },
+
+    addMapTipDomObj: function(url,szText){
+        if(url == "undefined" || url == typeof("undefined")  || url ==""){
+                var linkSpan = document.createElement('SPAN');
+                linkSpan.className = "mapTipData";
+                linkSpan.innerHTML = szText;
+                return linkSpan;
+            }
+            else
+            {
+            var a, linkURL;
+            var linkSpan = document.createElement('SPAN');
+            linkSpan.className = "mapTipData"
+            if (url.indexOf('href=') > 0) {   //MGOS allows complete anchor tags as the hyperlink
+            linkSpan.innerHTML = url;
+            a = linkDiv.firstChild;
+            linkURL = a.href;
+            } else {
+            a = document.createElement('a');
+            a.className = "mapTipLink";
+            a.innerHTML = szText;
+            linkURL = url;
+            linkSpan.appendChild(a);
+            }
+            a.href = 'javascript:void(0)';
+            var openLink = OpenLayers.Function.bind(this.openLink, this, linkURL);
+            a.onclick = OpenLayers.Function.bindAsEventListener(openLink, this);
+            return linkSpan;
+            }
+    },
     
     hideMaptip: function() {
       //console.log('hideMaptip');

Modified: branches/fusion-2.0/widgets/PanQuery.js
===================================================================
--- branches/fusion-2.0/widgets/PanQuery.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/PanQuery.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -102,7 +102,7 @@
                 options.extendSelection = true;
             }
 
-            this.getMap().aMaps[0].query(options);
+            this.getMap().query(options);
         }
         Event.stop(e);
     },    

Modified: branches/fusion-2.0/widgets/Select.js
===================================================================
--- branches/fusion-2.0/widgets/Select.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/Select.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -83,6 +83,11 @@
 
     },
 
+    shouldActivateWith: function(widget) {
+        return (widget instanceof Fusion.Widget.Select &&
+                widget.bActiveOnly == this.bActiveOnly);
+        
+    },
     enable: function() {
         if (this.bActiveOnly) {
             var layer = this.getMap().getActiveLayer();

Modified: branches/fusion-2.0/widgets/SelectRadius.js
===================================================================
--- branches/fusion-2.0/widgets/SelectRadius.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/SelectRadius.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -31,7 +31,7 @@
  * **********************************************************************/
 Fusion.Event.RADIUS_WIDGET_ACTIVATED = Fusion.Event.lastEventId++;
 
-Fusion.Widget.SelectRadius = OpenLayers.Class(Fusion.Widget, Fusion.Tool.Canvas, {
+Fusion.Widget.SelectRadius = OpenLayers.Class(Fusion.Widget, {
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',

Modified: branches/fusion-2.0/widgets/SelectWithin/SelectWithin.php
===================================================================
--- branches/fusion-2.0/widgets/SelectWithin/SelectWithin.php	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/SelectWithin/SelectWithin.php	2009-04-03 19:40:15 UTC (rev 1830)
@@ -123,7 +123,7 @@
       }
     }
     
-    header('Content-type: text/x-json');
+    header('Content-type: application/json');
     header('X-JSON: true');
     echo var2json($result);
   } catch(MgException $e) {

Modified: branches/fusion-2.0/widgets/Zoom.js
===================================================================
--- branches/fusion-2.0/widgets/Zoom.js	2009-04-03 19:06:48 UTC (rev 1829)
+++ branches/fusion-2.0/widgets/Zoom.js	2009-04-03 19:40:15 UTC (rev 1830)
@@ -58,7 +58,14 @@
         mapWidget.handlers.push(this.handler);
         mapWidget.handlers.push(this.shiftHandler);
     },
-
+    
+    shouldActivateWith: function(widget) {
+        return (widget instanceof Fusion.Widget.Zoom &&
+                widget.zoomIn == this.zoomIn &&
+                widget.factor == this.factor &&
+                widget.tolerance == this.tolerance);
+    },
+    
    /**
      * activate the widget (listen to mouse events and change cursor)
      * This function should be defined for all functions that register



More information about the fusion-commits mailing list