[fusion-commits] r2323 - in branches/fusion-2.2: . lib lib/OpenLayers

svn_fusion at osgeo.org svn_fusion at osgeo.org
Thu Jan 20 14:57:00 EST 2011


Author: madair
Date: 2011-01-20 11:56:47 -0800 (Thu, 20 Jan 2011)
New Revision: 2323

Modified:
   branches/fusion-2.2/fusion.cfg
   branches/fusion-2.2/lib/OpenLayers/OpenLayers.js
   branches/fusion-2.2/lib/proj4js-combined.js
   branches/fusion-2.2/lib/proj4js-compressed.js
Log:
re #430: update to OL and Proj4js libraries applied to the 2.2 branch

Modified: branches/fusion-2.2/fusion.cfg
===================================================================
--- branches/fusion-2.2/fusion.cfg	2011-01-20 19:46:05 UTC (rev 2322)
+++ branches/fusion-2.2/fusion.cfg	2011-01-20 19:56:47 UTC (rev 2323)
@@ -2,17 +2,6 @@
 # for Fusion
 
 [first]
-OpenLayers/SingleFile.js
-OpenLayers.js
-OpenLayers/Util.js
-OpenLayers/Console.js
-OpenLayers/BaseTypes.js
-OpenLayers/BaseTypes/Class.js
-OpenLayers/BaseTypes/Size.js
-OpenLayers/BaseTypes/Bounds.js
-OpenLayers/BaseTypes/Element.js
-OpenLayers/BaseTypes/LonLat.js
-OpenLayers/BaseTypes/Pixel.js
 
 [last]
 
@@ -43,8 +32,19 @@
 OpenLayers/Control/DrawFeature.js
 OpenLayers/Control/Measure.js
 OpenLayers/Control/ScaleLine.js
+OpenLayers/Control/MousePosition.js
+OpenLayers/Filter.js
+OpenLayers/Filter/FeatureId.js
+OpenLayers/Filter/Spatial.js
+OpenLayers/Filter/Logical.js
+OpenLayers/Filter/Comparison.js
 OpenLayers/Format/GML/v2.js
 OpenLayers/Format/GML/v3.js
+OpenLayers/Format/WFSCapabilities.js
+OpenLayers/Format/WFSCapabilities/v1.js
+OpenLayers/Format/WFSCapabilities/v1_0_0.js
+OpenLayers/Format/WFSCapabilities/v1_1_0.js
+OpenLayers/Format/WFSDescribeFeatureType.js
 OpenLayers/Handler/Box.js
 OpenLayers/Handler/Click.js
 OpenLayers/Handler/Drag.js
@@ -57,7 +57,17 @@
 OpenLayers/Handler/RegularPolygon.js
 OpenLayers/Projection.js
 OpenLayers/Protocol/HTTP.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/Strategy/Fixed.js
+OpenLayers/Strategy/BBOX.js
+OpenLayers/Strategy/Filter.js
+OpenLayers/Strategy/Save.js
+OpenLayers/Strategy/Paging.js
+OpenLayers/Strategy/Refresh.js
+OpenLayers/Strategy/Cluster.js
 OpenLayers/Feature.js
 OpenLayers/Marker.js
 OpenLayers/Icon.js

Modified: branches/fusion-2.2/lib/OpenLayers/OpenLayers.js
===================================================================
--- branches/fusion-2.2/lib/OpenLayers/OpenLayers.js	2011-01-20 19:46:05 UTC (rev 2322)
+++ branches/fusion-2.2/lib/OpenLayers/OpenLayers.js	2011-01-20 19:56:47 UTC (rev 2323)
@@ -2,7 +2,7 @@
 
   OpenLayers.js -- OpenLayers Map Viewer Library
 
-  Copyright 2005-2010 OpenLayers Contributors, released under the Clear BSD
+  Copyright 2005-2011 OpenLayers Contributors, released under the Clear BSD
   license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt
   for the full text of the license.
 
@@ -90,430 +90,201 @@
  * issues. Applications that use the code below will continue to work seamlessly
  * when that happens.
  */
-/* ======================================================================
+
+/**
+ * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
+ * Copyright (c) 2006, Yahoo! Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use of this software in source and binary forms, with or
+ * without modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * 
+ * * 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.
+ * 
+ * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission of Yahoo! Inc.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *//* ======================================================================
     OpenLayers/SingleFile.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 var OpenLayers = {
-    singleFile: true
-};
-
-
-/* ======================================================================
-    OpenLayers.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/* 
- * @requires OpenLayers/BaseTypes.js
- * @requires OpenLayers/Lang/en.js
- * @requires OpenLayers/Console.js
- */ 
-
-(function() {
     /**
-     * Before creating the OpenLayers namespace, check to see if
-     * OpenLayers.singleFile is true.  This occurs if the
-     * OpenLayers/SingleFile.js script is included before this one - as is the
-     * case with single file builds.
+     * Constant: VERSION_NUMBER
      */
-    var singleFile = (typeof OpenLayers == "object" && OpenLayers.singleFile);
-    
+    VERSION_NUMBER: "$Revision$",
+
     /**
-     * Cache for the script location returned from
-     * OpenLayers._getScriptLocation
+     * Constant: singleFile
+     * TODO: remove this in 3.0 when we stop supporting build profiles that
+     * include OpenLayers.js
      */
-    var scriptLocation;
-    
+    singleFile: true,
+
     /**
-     * Namespace: OpenLayers
-     * The OpenLayers object provides a namespace for all things OpenLayers
+     * Method: _getScriptLocation
+     * Return the path to this script. This is also implemented in
+     * OpenLayers.js
+     *
+     * Returns:
+     * {String} Path to this script
      */
-    window.OpenLayers = {
-        
-        /**
-         * Property: _scriptName
-         * {String} Relative path of this script.
-         */
-        _scriptName: (!singleFile) ? "lib/OpenLayers.js" : "OpenLayers.js",
-
-        /**
-         * Function: _getScriptLocation
-         * Return the path to this script.
-         *
-         * Returns:
-         * {String} Path to this script
-         */
-        _getScriptLocation: function () {
-            if (scriptLocation != undefined) {
-                return scriptLocation;
-            }
-            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 match = src.match(isOL);
-                    if(match) {
-                        scriptLocation = match[1];
-                        break;
-                    }
+    _getScriptLocation: (function() {
+        var r = new RegExp("(^|(.*?\\/))(OpenLayers\.js)(\\?|$)"),
+            s = document.getElementsByTagName('script'),
+            src, m, l = "";
+        for(var i=0, len=s.length; i<len; i++) {
+            src = s[i].getAttribute('src');
+            if(src) {
+                var m = src.match(r);
+                if(m) {
+                    l = m[1];
+                    break;
                 }
             }
-            return scriptLocation;
         }
-    };
-    /**
-     * OpenLayers.singleFile is a flag indicating this file is being included
-     * in a Single File Library build of the OpenLayers Library.
-     * 
-     * When we are *not* part of a SFL build we dynamically include the
-     * OpenLayers library code.
-     * 
-     * When we *are* part of a SFL build we do not dynamically include the 
-     * OpenLayers library code as it will be appended at the end of this file.
-      */
-    if(!singleFile) {
-        var jsfiles = new Array(
-            "OpenLayers/Util.js",
-            "OpenLayers/BaseTypes.js",
-            "OpenLayers/BaseTypes/Class.js",
-            "OpenLayers/BaseTypes/Bounds.js",
-            "OpenLayers/BaseTypes/Element.js",
-            "OpenLayers/BaseTypes/LonLat.js",
-            "OpenLayers/BaseTypes/Pixel.js",
-            "OpenLayers/BaseTypes/Size.js",
-            "OpenLayers/Console.js",
-            "OpenLayers/Tween.js",
-            "Rico/Corner.js",
-            "Rico/Color.js",
-            "OpenLayers/Ajax.js",
-            "OpenLayers/Events.js",
-            "OpenLayers/Request.js",
-            "OpenLayers/Request/XMLHttpRequest.js",
-            "OpenLayers/Projection.js",
-            "OpenLayers/Map.js",
-            "OpenLayers/Layer.js",
-            "OpenLayers/Icon.js",
-            "OpenLayers/Marker.js",
-            "OpenLayers/Marker/Box.js",
-            "OpenLayers/Popup.js",
-            "OpenLayers/Tile.js",
-            "OpenLayers/Tile/Image.js",
-            "OpenLayers/Tile/Image/IFrame.js",
-            "OpenLayers/Tile/WFS.js",
-            "OpenLayers/Layer/Image.js",
-            "OpenLayers/Layer/SphericalMercator.js",
-            "OpenLayers/Layer/EventPane.js",
-            "OpenLayers/Layer/FixedZoomLevels.js",
-            "OpenLayers/Layer/Google.js",
-            "OpenLayers/Layer/Google/v3.js",
-            "OpenLayers/Layer/VirtualEarth.js",
-            "OpenLayers/Layer/Yahoo.js",
-            "OpenLayers/Layer/HTTPRequest.js",
-            "OpenLayers/Layer/Grid.js",
-            "OpenLayers/Layer/MapGuide.js",
-            "OpenLayers/Layer/MapServer.js",
-            "OpenLayers/Layer/MapServer/Untiled.js",
-            "OpenLayers/Layer/KaMap.js",
-            "OpenLayers/Layer/KaMapCache.js",
-            "OpenLayers/Layer/MultiMap.js",
-            "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/WMS/Post.js",
-            "OpenLayers/Layer/WMTS.js",
-            "OpenLayers/Layer/ArcIMS.js",
-            "OpenLayers/Layer/GeoRSS.js",
-            "OpenLayers/Layer/Boxes.js",
-            "OpenLayers/Layer/XYZ.js",
-            "OpenLayers/Layer/TMS.js",
-            "OpenLayers/Layer/TileCache.js",
-            "OpenLayers/Layer/Zoomify.js",
-            "OpenLayers/Popup/Anchored.js",
-            "OpenLayers/Popup/AnchoredBubble.js",
-            "OpenLayers/Popup/Framed.js",
-            "OpenLayers/Popup/FramedCloud.js",
-            "OpenLayers/Feature.js",
-            "OpenLayers/Feature/Vector.js",
-            "OpenLayers/Feature/WFS.js",
-            "OpenLayers/Handler.js",
-            "OpenLayers/Handler/Click.js",
-            "OpenLayers/Handler/Hover.js",
-            "OpenLayers/Handler/Point.js",
-            "OpenLayers/Handler/Path.js",
-            "OpenLayers/Handler/Polygon.js",
-            "OpenLayers/Handler/Feature.js",
-            "OpenLayers/Handler/Drag.js",
-            "OpenLayers/Handler/RegularPolygon.js",
-            "OpenLayers/Handler/Box.js",
-            "OpenLayers/Handler/MouseWheel.js",
-            "OpenLayers/Handler/Keyboard.js",
-            "OpenLayers/Control.js",
-            "OpenLayers/Control/Attribution.js",
-            "OpenLayers/Control/Button.js",
-            "OpenLayers/Control/ZoomBox.js",
-            "OpenLayers/Control/ZoomToMaxExtent.js",
-            "OpenLayers/Control/DragPan.js",
-            "OpenLayers/Control/Navigation.js",
-            "OpenLayers/Control/MouseDefaults.js",
-            "OpenLayers/Control/MousePosition.js",
-            "OpenLayers/Control/OverviewMap.js",
-            "OpenLayers/Control/KeyboardDefaults.js",
-            "OpenLayers/Control/PanZoom.js",
-            "OpenLayers/Control/PanZoomBar.js",
-            "OpenLayers/Control/ArgParser.js",
-            "OpenLayers/Control/Permalink.js",
-            "OpenLayers/Control/Scale.js",
-            "OpenLayers/Control/ScaleLine.js",
-            "OpenLayers/Control/Snapping.js",
-            "OpenLayers/Control/Split.js",
-            "OpenLayers/Control/LayerSwitcher.js",
-            "OpenLayers/Control/DrawFeature.js",
-            "OpenLayers/Control/DragFeature.js",
-            "OpenLayers/Control/ModifyFeature.js",
-            "OpenLayers/Control/Panel.js",
-            "OpenLayers/Control/SelectFeature.js",
-            "OpenLayers/Control/NavigationHistory.js",
-            "OpenLayers/Control/Measure.js",
-            "OpenLayers/Control/WMSGetFeatureInfo.js",
-            "OpenLayers/Control/WMTSGetFeatureInfo.js",
-            "OpenLayers/Control/Graticule.js",
-            "OpenLayers/Control/TransformFeature.js",
-            "OpenLayers/Control/SLDSelect.js",
-            "OpenLayers/Geometry.js",
-            "OpenLayers/Geometry/Rectangle.js",
-            "OpenLayers/Geometry/Collection.js",
-            "OpenLayers/Geometry/Point.js",
-            "OpenLayers/Geometry/MultiPoint.js",
-            "OpenLayers/Geometry/Curve.js",
-            "OpenLayers/Geometry/LineString.js",
-            "OpenLayers/Geometry/LinearRing.js",        
-            "OpenLayers/Geometry/Polygon.js",
-            "OpenLayers/Geometry/MultiLineString.js",
-            "OpenLayers/Geometry/MultiPolygon.js",
-            "OpenLayers/Geometry/Surface.js",
-            "OpenLayers/Renderer.js",
-            "OpenLayers/Renderer/Elements.js",
-            "OpenLayers/Renderer/SVG.js",
-            "OpenLayers/Renderer/Canvas.js",
-            "OpenLayers/Renderer/VML.js",
-            "OpenLayers/Layer/Vector.js",
-            "OpenLayers/Layer/Vector/RootContainer.js",
-            "OpenLayers/Strategy.js",
-            "OpenLayers/Strategy/Filter.js",
-            "OpenLayers/Strategy/Fixed.js",
-            "OpenLayers/Strategy/Cluster.js",
-            "OpenLayers/Strategy/Paging.js",
-            "OpenLayers/Strategy/BBOX.js",
-            "OpenLayers/Strategy/Save.js",
-            "OpenLayers/Strategy/Refresh.js",
-            "OpenLayers/Filter.js",
-            "OpenLayers/Filter/FeatureId.js",
-            "OpenLayers/Filter/Logical.js",
-            "OpenLayers/Filter/Comparison.js",
-            "OpenLayers/Filter/Spatial.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/Protocol/SOS.js",
-            "OpenLayers/Protocol/SOS/v1_0_0.js",
-            "OpenLayers/Layer/PointTrack.js",
-            "OpenLayers/Layer/GML.js",
-            "OpenLayers/Style.js",
-            "OpenLayers/Style2.js",
-            "OpenLayers/StyleMap.js",
-            "OpenLayers/Rule.js",
-            "OpenLayers/Format.js",
-            "OpenLayers/Format/XML.js",
-            "OpenLayers/Format/Context.js",
-            "OpenLayers/Format/ArcXML.js",
-            "OpenLayers/Format/ArcXML/Features.js",
-            "OpenLayers/Format/GML.js",
-            "OpenLayers/Format/GML/Base.js",
-            "OpenLayers/Format/GML/v2.js",
-            "OpenLayers/Format/GML/v3.js",
-            "OpenLayers/Format/Atom.js",
-            "OpenLayers/Format/KML.js",
-            "OpenLayers/Format/GeoRSS.js",
-            "OpenLayers/Format/WFS.js",
-            "OpenLayers/Format/WFSCapabilities.js",
-            "OpenLayers/Format/WFSCapabilities/v1.js",
-            "OpenLayers/Format/WFSCapabilities/v1_0_0.js",
-            "OpenLayers/Format/WFSCapabilities/v1_1_0.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/OWSCommon/v1.js",
-            "OpenLayers/Format/OWSCommon/v1_0_0.js",
-            "OpenLayers/Format/OWSCommon/v1_1_0.js",
-            "OpenLayers/Format/CSWGetDomain.js",
-            "OpenLayers/Format/CSWGetDomain/v2_0_2.js",
-            "OpenLayers/Format/CSWGetRecords.js",
-            "OpenLayers/Format/CSWGetRecords/v2_0_2.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",
-            "OpenLayers/Format/WMC.js",
-            "OpenLayers/Format/WMC/v1.js",
-            "OpenLayers/Format/WMC/v1_0_0.js",
-            "OpenLayers/Format/WMC/v1_1_0.js",
-            "OpenLayers/Format/WMSCapabilities.js",
-            "OpenLayers/Format/WMSCapabilities/v1.js",
-            "OpenLayers/Format/WMSCapabilities/v1_1.js",
-            "OpenLayers/Format/WMSCapabilities/v1_1_0.js",
-            "OpenLayers/Format/WMSCapabilities/v1_1_1.js",
-            "OpenLayers/Format/WMSCapabilities/v1_3.js",
-            "OpenLayers/Format/WMSCapabilities/v1_3_0.js",
-            "OpenLayers/Format/WMSGetFeatureInfo.js",
-            "OpenLayers/Format/SOSCapabilities.js",
-            "OpenLayers/Format/SOSCapabilities/v1_0_0.js",
-            "OpenLayers/Format/SOSGetObservation.js",
-            "OpenLayers/Format/SOSGetFeatureOfInterest.js",
-            "OpenLayers/Format/OWSContext.js",
-            "OpenLayers/Format/OWSContext/v0_3_1.js",
-            "OpenLayers/Format/WMTSCapabilities.js",
-            "OpenLayers/Format/WMTSCapabilities/v1_0_0.js",
-            "OpenLayers/Layer/WFS.js",
-            "OpenLayers/Control/GetFeature.js",
-            "OpenLayers/Control/MouseToolbar.js",
-            "OpenLayers/Control/NavToolbar.js",
-            "OpenLayers/Control/PanPanel.js",
-            "OpenLayers/Control/Pan.js",
-            "OpenLayers/Control/ZoomIn.js",
-            "OpenLayers/Control/ZoomOut.js",
-            "OpenLayers/Control/ZoomPanel.js",
-            "OpenLayers/Control/EditingToolbar.js",
-            "OpenLayers/Symbolizer.js",
-            "OpenLayers/Symbolizer/Point.js",
-            "OpenLayers/Symbolizer/Line.js",
-            "OpenLayers/Symbolizer/Polygon.js",
-            "OpenLayers/Symbolizer/Text.js",
-            "OpenLayers/Symbolizer/Raster.js",
-            "OpenLayers/Lang.js",
-            "OpenLayers/Lang/en.js"
-        ); // etc.
-
-        var agent = navigator.userAgent;
-        var docWrite = (agent.match("MSIE") || agent.match("Safari"));
-        if(docWrite) {
-            var allScriptTags = new Array(jsfiles.length);
-        }
-        var host = OpenLayers._getScriptLocation() + "lib/";    
-        for (var i=0, len=jsfiles.length; i<len; i++) {
-            if (docWrite) {
-                allScriptTags[i] = "<script src='" + host + jsfiles[i] +
-                                   "'></script>"; 
-            } else {
-                var s = document.createElement("script");
-                s.src = host + jsfiles[i];
-                var h = document.getElementsByTagName("head").length ? 
-                           document.getElementsByTagName("head")[0] : 
-                           document.body;
-                h.appendChild(s);
-            }
-        }
-        if (docWrite) {
-            document.write(allScriptTags.join(""));
-        }
-    }
-})();
-
-/**
- * Constant: VERSION_NUMBER
- */
-OpenLayers.VERSION_NUMBER="OpenLayers 2.10 -- $Revision$";
+        return (function() { return l; });
+    })()
+};
 /* ======================================================================
-    OpenLayers/Util.js
+    OpenLayers/BaseTypes/Class.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). 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
+ * @requires OpenLayers/SingleFile.js
  */
 
 /**
- * Namespace: Util
+ * Constructor: OpenLayers.Class
+ * Base class used to construct all other classes. Includes support for 
+ *     multiple inheritance. 
+ *     
+ * This constructor is new in OpenLayers 2.5.  At OpenLayers 3.0, the old 
+ *     syntax for creating classes and dealing with inheritance 
+ *     will be removed.
+ * 
+ * To create a new OpenLayers-style class, use the following syntax:
+ * > var MyClass = OpenLayers.Class(prototype);
+ *
+ * To create a new OpenLayers-style class with multiple inheritance, use the
+ *     following syntax:
+ * > var MyClass = OpenLayers.Class(Class1, Class2, prototype);
+ * Note that instanceof reflection will only reveil Class1 as superclass.
+ * Class2 ff are mixins.
+ *
  */
-OpenLayers.Util = {};
+OpenLayers.Class = function() {
+    var len = arguments.length;
+    var P = arguments[0];
+    var F = arguments[len-1];
 
-/** 
- * Function: getElement
- * This is the old $() from prototype
+    var C = typeof F.initialize == "function" ?
+        F.initialize :
+        function(){ P.apply(this, arguments); };
+
+    if (len > 1) {
+        var newArgs = [C, P].concat(
+                Array.prototype.slice.call(arguments).slice(1, len-1), F);
+        OpenLayers.inherit.apply(null, newArgs);
+    } else {
+        C.prototype = F;
+    }
+    return C;
+};
+
+/**
+ * Property: isPrototype
+ * *Deprecated*.  This is no longer needed and will be removed at 3.0.
  */
-OpenLayers.Util.getElement = function() {
-    var elements = [];
+OpenLayers.Class.isPrototype = function () {};
 
-    for (var i=0, len=arguments.length; i<len; i++) {
-        var element = arguments[i];
-        if (typeof element == 'string') {
-            element = document.getElementById(element);
+/**
+ * APIFunction: OpenLayers.create
+ * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
+ *     <OpenLayers.Class> constructor instead.
+ *
+ * Returns:
+ * An OpenLayers class
+ */
+OpenLayers.Class.create = function() {
+    return function() {
+        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
+            this.initialize.apply(this, arguments);
         }
-        if (arguments.length == 1) {
-            return element;
-        }
-        elements.push(element);
-    }
-    return elements;
+    };
 };
 
 /**
- * Function: isElement
- * A cross-browser implementation of "e instanceof Element".
+ * APIFunction: inherit
+ * *Deprecated*.  Old method to inherit from one or more OpenLayers style
+ *     classes.  Use the <OpenLayers.Class> constructor instead.
  *
  * Parameters:
- * o - {Object} The object to test.
+ * class - One or more classes can be provided as arguments
  *
  * Returns:
- * {Boolean}
+ * An object prototype
  */
-OpenLayers.Util.isElement = function(o) {
-    return !!(o && o.nodeType === 1);
+OpenLayers.Class.inherit = function (P) {
+    var C = function() {
+       P.call(this);
+    };
+    var newArgs = [C].concat(Array.prototype.slice.call(arguments));
+    OpenLayers.inherit.apply(null, newArgs);
+    return C.prototype;
 };
 
-/** 
- * Maintain existing definition of $.
+/**
+ * Function: OpenLayers.inherit
+ *
+ * Parameters:
+ * C - {Object} the class that inherits
+ * P - {Object} the superclass to inherit from
+ *
+ * In addition to the mandatory C and P parameters, an arbitrary number of
+ * objects can be passed, which will extend C.
  */
-if(typeof window.$  === "undefined") {
-    //window.$ = OpenLayers.Util.getElement;
-}
+OpenLayers.inherit = function(C, P) {
+   var F = function() {};
+   F.prototype = P.prototype;
+   C.prototype = new F;
+   var i, l, o;
+   for(i=2, l=arguments.length; i<l; i++) {
+       o = arguments[i];
+       if(typeof o === "function") {
+           o = o.prototype;
+       }
+       OpenLayers.Util.extend(C.prototype, o);
+   }
+};
 
 /**
  * APIFunction: extend
@@ -528,12 +299,13 @@
  * Returns:
  * {Object} The destination object.
  */
+OpenLayers.Util = OpenLayers.Util || {};
 OpenLayers.Util.extend = function(destination, source) {
     destination = destination || {};
-    if(source) {
-        for(var property in source) {
+    if (source) {
+        for (var property in source) {
             var value = source[property];
-            if(value !== undefined) {
+            if (value !== undefined) {
                 destination[property] = value;
             }
         }
@@ -553,1720 +325,27 @@
         var sourceIsEvt = typeof window.Event == "function"
                           && source instanceof window.Event;
 
-        if(!sourceIsEvt
-           && source.hasOwnProperty && source.hasOwnProperty('toString')) {
+        if (!sourceIsEvt
+           && source.hasOwnProperty && source.hasOwnProperty("toString")) {
             destination.toString = source.toString;
         }
     }
     return destination;
 };
-
-
-/** 
- * Function: removeItem
- * Remove an object from an array. Iterates through the array
- *     to find the item, then removes it.
- *
- * Parameters:
- * array - {Array}
- * item - {Object}
- * 
- * Return
- * {Array} A reference to the array
- */
-OpenLayers.Util.removeItem = function(array, item) {
-    for(var i = array.length - 1; i >= 0; i--) {
-        if(array[i] == item) {
-            array.splice(i,1);
-            //break;more than once??
-        }
-    }
-    return array;
-};
-
-/**
- * Function: clearArray
- * *Deprecated*. This function will disappear in 3.0.
- * Please use "array.length = 0" instead.
- * 
- * Parameters:
- * array - {Array}
- */
-OpenLayers.Util.clearArray = function(array) {
-    OpenLayers.Console.warn(
-        OpenLayers.i18n(
-            "methodDeprecated", {'newMethod': 'array = []'}
-        )
-    );
-    array.length = 0;
-};
-
-/** 
- * Function: indexOf
- * Seems to exist already in FF, but not in MOZ.
- * 
- * Parameters:
- * array - {Array}
- * obj - {Object}
- * 
- * Returns:
- * {Integer} The index at, which the first object was found in the array.
- *           If not found, returns -1.
- */
-OpenLayers.Util.indexOf = function(array, obj) {
-    // use the build-in function if available.
-    if (typeof array.indexOf == "function") {
-        return array.indexOf(obj);
-    } else {
-        for (var i = 0, len = array.length; i < len; i++) {
-            if (array[i] == obj) {
-                return i;
-            }
-        }
-        return -1;   
-    }
-};
-
-
-
-/**
- * Function: modifyDOMElement
- * 
- * Modifies many properties of a DOM element all at once.  Passing in 
- * null to an individual parameter will avoid setting the attribute.
- *
- * Parameters:
- * id - {String} The element id attribute to set.
- * px - {<OpenLayers.Pixel>} The left and top style position.
- * sz - {<OpenLayers.Size>}  The width and height style attributes.
- * position - {String}       The position attribute.  eg: absolute, 
- *                           relative, etc.
- * border - {String}         The style.border attribute.  eg:
- *                           solid black 2px
- * overflow - {String}       The style.overview attribute.  
- * opacity - {Float}         Fractional value (0.0 - 1.0)
- */
-OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
-                                            border, overflow, opacity) {
-
-    if (id) {
-        element.id = id;
-    }
-    if (px) {
-        element.style.left = px.x + "px";
-        element.style.top = px.y + "px";
-    }
-    if (sz) {
-        element.style.width = sz.w + "px";
-        element.style.height = sz.h + "px";
-    }
-    if (position) {
-        element.style.position = position;
-    }
-    if (border) {
-        element.style.border = border;
-    }
-    if (overflow) {
-        element.style.overflow = overflow;
-    }
-    if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
-        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
-        element.style.opacity = opacity;
-    } else if (parseFloat(opacity) == 1.0) {
-        element.style.filter = '';
-        element.style.opacity = '';
-    }
-};
-
-/** 
- * Function: createDiv
- * Creates a new div and optionally set some standard attributes.
- * Null may be passed to each parameter if you do not wish to
- * set a particular attribute.
- * Note - zIndex is NOT set on the resulting div.
- * 
- * Parameters:
- * id - {String} An identifier for this element.  If no id is
- *               passed an identifier will be created 
- *               automatically.
- * px - {<OpenLayers.Pixel>} The element left and top position. 
- * sz - {<OpenLayers.Size>} The element width and height.
- * imgURL - {String} A url pointing to an image to use as a 
- *                   background image.
- * position - {String} The style.position value. eg: absolute,
- *                     relative etc.
- * border - {String} The the style.border value. 
- *                   eg: 2px solid black
- * overflow - {String} The style.overflow value. Eg. hidden
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * 
- * Returns: 
- * {DOMElement} A DOM Div created with the specified attributes.
- */
-OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
-                                     border, overflow, opacity) {
-
-    var dom = document.createElement('div');
-
-    if (imgURL) {
-        dom.style.backgroundImage = 'url(' + imgURL + ')';
-    }
-
-    //set generic properties
-    if (!id) {
-        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
-    }
-    if (!position) {
-        position = "absolute";
-    }
-    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
-                                     border, overflow, opacity);
-
-    return dom;
-};
-
-/**
- * Function: createImage
- * Creates an img element with specific attribute values.
- *  
- * Parameters:
- * id - {String} The id field for the img.  If none assigned one will be
- *               automatically generated.
- * px - {<OpenLayers.Pixel>} The left and top positions.
- * sz - {<OpenLayers.Size>} The style.width and style.height values.
- * imgURL - {String} The url to use as the image source.
- * position - {String} The style.position value.
- * border - {String} The border to place around the image.
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * delayDisplay - {Boolean} If true waits until the image has been
- *                          loaded.
- * 
- * Returns:
- * {DOMElement} A DOM Image created with the specified attributes.
- */
-OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
-                                       opacity, delayDisplay) {
-
-    var image = document.createElement("img");
-
-    //set generic properties
-    if (!id) {
-        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
-    }
-    if (!position) {
-        position = "relative";
-    }
-    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
-                                     border, null, opacity);
-
-    if(delayDisplay) {
-        image.style.display = "none";
-        OpenLayers.Event.observe(image, "load", 
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, image));
-        OpenLayers.Event.observe(image, "error", 
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, image));
-        
-    }
-    
-    //set special properties
-    image.style.alt = id;
-    image.galleryImg = "no";
-    if (imgURL) {
-        image.src = imgURL;
-    }
-
-
-        
-    return image;
-};
-
-/**
- * Function: setOpacity
- * *Deprecated*.  This function has been deprecated. Instead, please use 
- *     <OpenLayers.Util.modifyDOMElement> 
- *     or 
- *     <OpenLayers.Util.modifyAlphaImageDiv>
- * 
- * Set the opacity of a DOM Element
- *     Note that for this function to work in IE, elements must "have layout"
- *     according to:
- *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
- *
- * Parameters:
- * element - {DOMElement} Set the opacity on this DOM element
- * opacity - {Float} Opacity value (0.0 - 1.0)
- */
-OpenLayers.Util.setOpacity = function(element, opacity) {
-    OpenLayers.Util.modifyDOMElement(element, null, null, null,
-                                     null, null, null, opacity);
-};
-
-/**
- * Function: onImageLoad
- * Bound to image load events.  For all images created with <createImage> or
- *     <createAlphaImageDiv>, this function will be bound to the load event.
- */
-OpenLayers.Util.onImageLoad = function() {
-    // The complex check here is to solve issues described in #480.
-    // Every time a map view changes, it increments the 'viewRequestID' 
-    // property. As the requests for the images for the new map view are sent
-    // out, they are tagged with this unique viewRequestID. 
-    // 
-    // If an image has no viewRequestID property set, we display it regardless, 
-    // but if it does have a viewRequestID property, we check that it matches 
-    // the viewRequestID set on the map.
-    // 
-    // If the viewRequestID on the map has changed, that means that the user
-    // has changed the map view since this specific request was sent out, and
-    // therefore this tile does not need to be displayed (so we do not execute
-    // this code that turns its display on).
-    //
-    if (!this.viewRequestID ||
-        (this.map && this.viewRequestID == this.map.viewRequestID)) { 
-        this.style.display = "";  
-    }
-    OpenLayers.Element.removeClass(this, "olImageLoadError");
-};
-
-/**
- * Property: IMAGE_RELOAD_ATTEMPTS
- * {Integer} How many times should we try to reload an image before giving up?
- *           Default is 0
- */
-OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
-
-/**
- * Function: onImageLoadError 
- */
-OpenLayers.Util.onImageLoadError = function() {
-    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
-    if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
-        var urls = this.urls;
-        if (urls && urls instanceof Array && urls.length > 1){
-            var src = this.src.toString();
-            var current_url, k;
-            for (k = 0; current_url = urls[k]; k++){
-                if(src.indexOf(current_url) != -1){
-                    break;
-                }
-            }
-            var guess = Math.floor(urls.length * Math.random());
-            var new_url = urls[guess];
-            k = 0;
-            while(new_url == current_url && k++ < 4){
-                guess = Math.floor(urls.length * Math.random());
-                new_url = urls[guess];
-            }
-            this.src = src.replace(current_url, new_url);
-        } else {
-            this.src = this.src;
-        }
-    } else {
-        OpenLayers.Element.addClass(this, "olImageLoadError");
-    }
-    this.style.display = "";
-};
-
-/**
- * Property: alphaHackNeeded
- * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
- */
-OpenLayers.Util.alphaHackNeeded = null;
-
-/**
- * Function: alphaHack
- * Checks whether it's necessary (and possible) to use the png alpha
- * hack which allows alpha transparency for png images under Internet
- * Explorer.
- * 
- * Returns:
- * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
- */
-OpenLayers.Util.alphaHack = function() {
-    if (OpenLayers.Util.alphaHackNeeded == null) {
-        var arVersion = navigator.appVersion.split("MSIE");
-        var version = parseFloat(arVersion[1]);
-        var filter = false;
-    
-        // IEs4Lin dies when trying to access document.body.filters, because 
-        // the property is there, but requires a DLL that can't be provided. This
-        // means that we need to wrap this in a try/catch so that this can
-        // continue.
-    
-        try { 
-            filter = !!(document.body.filters);
-        } catch (e) {}    
-    
-        OpenLayers.Util.alphaHackNeeded = (filter && 
-                                           (version >= 5.5) && (version < 7));
-    }
-    return OpenLayers.Util.alphaHackNeeded;
-};
-
-/** 
- * Function: modifyAlphaImageDiv
- * 
- * div - {DOMElement} Div containing Alpha-adjusted Image
- * id - {String}
- * px - {<OpenLayers.Pixel>}
- * sz - {<OpenLayers.Size>}
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
- * opacity - {Float} Fractional value (0.0 - 1.0)
- */ 
-OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
-                                               position, border, sizing, 
-                                               opacity) {
-
-    OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
-                                     null, null, opacity);
-
-    var img = div.childNodes[0];
-
-    if (imgURL) {
-        img.src = imgURL;
-    }
-    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
-                                     "relative", border);
-    
-    if (OpenLayers.Util.alphaHack()) {
-        if(div.style.display != "none") {
-            div.style.display = "inline-block";
-        }
-        if (sizing == null) {
-            sizing = "scale";
-        }
-        
-        div.style.filter = "progid:DXImageTransform.Microsoft" +
-                           ".AlphaImageLoader(src='" + img.src + "', " +
-                           "sizingMethod='" + sizing + "')";
-        if (parseFloat(div.style.opacity) >= 0.0 && 
-            parseFloat(div.style.opacity) < 1.0) {
-            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
-        }
-
-        img.style.filter = "alpha(opacity=0)";
-    }
-};
-
-/** 
- * Function: createAlphaImageDiv
- * 
- * id - {String}
- * px - {<OpenLayers.Pixel>}
- * sz - {<OpenLayers.Size>}
- * imgURL - {String}
- * position - {String}
- * border - {String}
- * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
- * opacity - {Float} Fractional value (0.0 - 1.0)
- * delayDisplay - {Boolean} If true waits until the image has been
- *                          loaded.
- * 
- * Returns:
- * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
- *              needed for transparency in IE, it is added.
- */ 
-OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
-                                               position, border, sizing, 
-                                               opacity, delayDisplay) {
-    
-    var div = OpenLayers.Util.createDiv();
-    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
-                                          null, false);
-    div.appendChild(img);
-
-    if (delayDisplay) {
-        img.style.display = "none";
-        OpenLayers.Event.observe(img, "load",
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, div));
-        OpenLayers.Event.observe(img, "error",
-            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, div));
-    }
-
-    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
-                                        border, sizing, opacity);
-    
-    return div;
-};
-
-
-/** 
- * Function: upperCaseObject
- * Creates a new hashtable and copies over all the keys from the 
- *     passed-in object, but storing them under an uppercased
- *     version of the key at which they were stored.
- * 
- * Parameters: 
- * object - {Object}
- * 
- * Returns: 
- * {Object} A new Object with all the same keys but uppercased
- */
-OpenLayers.Util.upperCaseObject = function (object) {
-    var uObject = {};
-    for (var key in object) {
-        uObject[key.toUpperCase()] = object[key];
-    }
-    return uObject;
-};
-
-/** 
- * Function: applyDefaults
- * Takes an object and copies any properties that don't exist from
- *     another properties, by analogy with OpenLayers.Util.extend() from
- *     Prototype.js.
- * 
- * Parameters:
- * to - {Object} The destination object.
- * from - {Object} The source object.  Any properties of this object that
- *     are undefined in the to object will be set on the to object.
- *
- * Returns:
- * {Object} A reference to the to object.  Note that the to argument is modified
- *     in place and returned by this function.
- */
-OpenLayers.Util.applyDefaults = function (to, from) {
-    to = to || {};
-    /*
-     * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
-     * prototype object" when calling hawOwnProperty if the source object is an
-     * instance of window.Event.
-     */
-    var fromIsEvt = typeof window.Event == "function"
-                    && from instanceof window.Event;
-
-    for (var key in from) {
-        if (to[key] === undefined ||
-            (!fromIsEvt && from.hasOwnProperty
-             && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
-            to[key] = from[key];
-        }
-    }
-    /**
-     * IE doesn't include the toString property when iterating over an object's
-     * properties with the for(property in object) syntax.  Explicitly check if
-     * the source has its own toString property.
-     */
-    if(!fromIsEvt && from && from.hasOwnProperty
-       && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
-        to.toString = from.toString;
-    }
-    
-    return to;
-};
-
-/**
- * Function: getParameterString
- * 
- * Parameters:
- * params - {Object}
- * 
- * Returns:
- * {String} A concatenation of the properties of an object in 
- *          http parameter notation. 
- *          (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
- *          If a parameter is actually a list, that parameter will then
- *          be set to a comma-seperated list of values (foo,bar) instead
- *          of being URL escaped (foo%3Abar). 
- */
-OpenLayers.Util.getParameterString = function(params) {
-    var paramsArray = [];
-    
-    for (var key in params) {
-      var value = params[key];
-      if ((value != null) && (typeof value != 'function')) {
-        var encodedValue;
-        if (typeof value == 'object' && value.constructor == Array) {
-          /* value is an array; encode items and separate with "," */
-          var encodedItemArray = [];
-          var item;
-          for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
-            item = value[itemIndex];
-            encodedItemArray.push(encodeURIComponent(
-                (item === null || item === undefined) ? "" : item)
-            );
-          }
-          encodedValue = encodedItemArray.join(",");
-        }
-        else {
-          /* value is a string; simply encode */
-          encodedValue = encodeURIComponent(value);
-        }
-        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
-      }
-    }
-    
-    return paramsArray.join("&");
-};
-
-/**
- * Function: urlAppend
- * Appends a parameter string to a url. This function includes the logic for
- * using the appropriate character (none, & or ?) to append to the url before
- * appending the param string.
- * 
- * Parameters:
- * url - {String} The url to append to
- * paramStr - {String} The param string to append
- * 
- * Returns:
- * {String} The new url
- */
-OpenLayers.Util.urlAppend = function(url, paramStr) {
-    var newUrl = url;
-    if(paramStr) {
-        var parts = (url + " ").split(/[?&]/);
-        newUrl += (parts.pop() === " " ?
-            paramStr :
-            parts.length ? "&" + paramStr : "?" + paramStr);
-    }
-    return newUrl;
-};
-
-/**
- * Property: ImgPath
- * {String} Default is ''.
- */
-OpenLayers.ImgPath = '';
-
-/** 
- * Function: getImagesLocation
- * 
- * Returns:
- * {String} The fully formatted image location string
- */
-OpenLayers.Util.getImagesLocation = function() {
-    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
-};
-
-
-/** 
- * Function: Try
- * Execute functions until one of them doesn't throw an error. 
- *     Capitalized because "try" is a reserved word in JavaScript.
- *     Taken directly from OpenLayers.Util.Try()
- * 
- * Parameters:
- * [*] - {Function} Any number of parameters may be passed to Try()
- *    It will attempt to execute each of them until one of them 
- *    successfully executes. 
- *    If none executes successfully, returns null.
- * 
- * Returns:
- * {*} The value returned by the first successfully executed function.
- */
-OpenLayers.Util.Try = function() {
-    var returnValue = null;
-
-    for (var i=0, len=arguments.length; i<len; i++) {
-      var lambda = arguments[i];
-      try {
-        returnValue = lambda();
-        break;
-      } catch (e) {}
-    }
-
-    return returnValue;
-};
-
-
-/** 
- * Function: getNodes
- * 
- * These could/should be made namespace aware?
- * 
- * Parameters:
- * p - {}
- * tagName - {String}
- * 
- * Returns:
- * {Array}
- */
-OpenLayers.Util.getNodes=function(p, tagName) {
-    var nodes = OpenLayers.Util.Try(
-        function () {
-            return OpenLayers.Util._getNodes(p.documentElement.childNodes,
-                                            tagName);
-        },
-        function () {
-            return OpenLayers.Util._getNodes(p.childNodes, tagName);
-        }
-    );
-    return nodes;
-};
-
-/**
- * Function: _getNodes
- * 
- * Parameters:
- * nodes - {Array}
- * tagName - {String}
- * 
- * Returns:
- * {Array}
- */
-OpenLayers.Util._getNodes=function(nodes, tagName) {
-    var retArray = [];
-    for (var i=0, len=nodes.length; i<len; i++) {
-        if (nodes[i].nodeName==tagName) {
-            retArray.push(nodes[i]);
-        }
-    }
-
-    return retArray;
-};
-
-
-
-/**
- * Function: getTagText
- * 
- * Parameters:
- * parent - {}
- * item - {String}
- * index - {Integer}
- * 
- * Returns:
- * {String}
- */
-OpenLayers.Util.getTagText = function (parent, item, index) {
-    var result = OpenLayers.Util.getNodes(parent, item);
-    if (result && (result.length > 0))
-    {
-        if (!index) {
-            index=0;
-        }
-        if (result[index].childNodes.length > 1) {
-            return result.childNodes[1].nodeValue; 
-        }
-        else if (result[index].childNodes.length == 1) {
-            return result[index].firstChild.nodeValue; 
-        }
-    } else { 
-        return ""; 
-    }
-};
-
-/**
- * Function: getXmlNodeValue
- * 
- * Parameters:
- * node - {XMLNode}
- * 
- * Returns:
- * {String} The text value of the given node, without breaking in firefox or IE
- */
-OpenLayers.Util.getXmlNodeValue = function(node) {
-    var val = null;
-    OpenLayers.Util.Try( 
-        function() {
-            val = node.text;
-            if (!val) {
-                val = node.textContent;
-            }
-            if (!val) {
-                val = node.firstChild.nodeValue;
-            }
-        }, 
-        function() {
-            val = node.textContent;
-        }); 
-    return val;
-};
-
-/** 
- * Function: mouseLeft
- * 
- * Parameters:
- * evt - {Event}
- * div - {HTMLDivElement}
- * 
- * Returns:
- * {Boolean}
- */
-OpenLayers.Util.mouseLeft = function (evt, div) {
-    // start with the element to which the mouse has moved
-    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
-    // walk up the DOM tree.
-    while (target != div && target != null) {
-        target = target.parentNode;
-    }
-    // if the target we stop at isn't the div, then we've left the div.
-    return (target != div);
-};
-
-/**
- * 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:
- * x - {Float}
- * 
- * Returns:
- * {Float}
- */
-OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
-
-/**
- * Function: deg
- *
- * Parameters:
- * x - {Float}
- *
- * Returns:
- * {Float}
- */
-OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
-
-/**
- * Property: VincentyConstants
- * {Object} Constants for Vincenty functions.
- */
-OpenLayers.Util.VincentyConstants = {
-    a: 6378137,
-    b: 6356752.3142,
-    f: 1/298.257223563
-};
-
-/**
- * APIFunction: distVincenty
- * Given two objects representing points with geographic coordinates, this
- *     calculates the distance between those points on the surface of an
- *     ellipsoid.
- *
- * Parameters:
- * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
- * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
- *
- * Returns:
- * {Float} The distance (in km) between the two input points as measured on an
- *     ellipsoid.  Note that the input point objects must be in geographic
- *     coordinates (decimal degrees) and the return distance is in kilometers.
- */
-OpenLayers.Util.distVincenty = function(p1, p2) {
-    var ct = OpenLayers.Util.VincentyConstants;
-    var a = ct.a, b = ct.b, f = ct.f;
-
-    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
-    var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
-    var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
-    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
-    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
-    var lambda = L, lambdaP = 2*Math.PI;
-    var iterLimit = 20;
-    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
-        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
-        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
-        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
-        if (sinSigma==0) {
-            return 0;  // co-incident points
-        }
-        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
-        var sigma = Math.atan2(sinSigma, cosSigma);
-        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
-        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
-        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
-        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
-        lambdaP = lambda;
-        lambda = L + (1-C) * f * Math.sin(alpha) *
-        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
-    }
-    if (iterLimit==0) {
-        return NaN;  // formula failed to converge
-    }
-    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
-    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
-    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
-    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
-        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
-    var s = b*A*(sigma-deltaSigma);
-    var d = s.toFixed(3)/1000; // round to 1mm precision
-    return d;
-};
-
-/**
- * APIFunction: destinationVincenty
- * Calculate destination point given start point lat/long (numeric degrees),
- * bearing (numeric degrees) & distance (in m).
- * Adapted from Chris Veness work, see
- * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
- *
- * Parameters:
- * lonlat  - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
- *     properties) The start point.
- * brng     - {Float} The bearing (degrees).
- * distance - {Float} The ground distance (meters).
- *
- * Returns:
- * {<OpenLayers.LonLat>} The destination point.
- */
-OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
-    var u = OpenLayers.Util;
-    var ct = u.VincentyConstants;
-    var a = ct.a, b = ct.b, f = ct.f;
-
-    var lon1 = lonlat.lon;
-    var lat1 = lonlat.lat;
-
-    var s = dist;
-    var alpha1 = u.rad(brng);
-    var sinAlpha1 = Math.sin(alpha1);
-    var cosAlpha1 = Math.cos(alpha1);
-
-    var tanU1 = (1-f) * Math.tan(u.rad(lat1));
-    var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
-    var sigma1 = Math.atan2(tanU1, cosAlpha1);
-    var sinAlpha = cosU1 * sinAlpha1;
-    var cosSqAlpha = 1 - sinAlpha*sinAlpha;
-    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
-    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
-    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
-
-    var sigma = s / (b*A), sigmaP = 2*Math.PI;
-    while (Math.abs(sigma-sigmaP) > 1e-12) {
-        var cos2SigmaM = Math.cos(2*sigma1 + sigma);
-        var sinSigma = Math.sin(sigma);
-        var cosSigma = Math.cos(sigma);
-        var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
-            B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
-        sigmaP = sigma;
-        sigma = s / (b*A) + deltaSigma;
-    }
-
-    var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
-    var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
-        (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
-    var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
-    var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
-    var L = lambda - (1-C) * f * sinAlpha *
-        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
-
-    var revAz = Math.atan2(sinAlpha, -tmp);  // final bearing
-
-    return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
-};
-
-/**
- * Function: getParameters
- * Parse the parameters from a URL or from the current page itself into a 
- *     JavaScript Object. Note that parameter values with commas are separated
- *     out into an Array.
- * 
- * Parameters:
- * url - {String} Optional url used to extract the query string.
- *                If null, query string is taken from page location.
- * 
- * Returns:
- * {Object} An object of key/value pairs from the query string.
- */
-OpenLayers.Util.getParameters = function(url) {
-    // if no url specified, take it from the location bar
-    url = url || window.location.href;
-
-    //parse out parameters portion of url string
-    var paramsString = "";
-    if (OpenLayers.String.contains(url, '?')) {
-        var start = url.indexOf('?') + 1;
-        var end = OpenLayers.String.contains(url, "#") ?
-                    url.indexOf('#') : url.length;
-        paramsString = url.substring(start, end);
-    }
-
-    var parameters = {};
-    var pairs = paramsString.split(/[&;]/);
-    for(var i=0, len=pairs.length; i<len; ++i) {
-        var keyValue = pairs[i].split('=');
-        if (keyValue[0]) {
-            var key = decodeURIComponent(keyValue[0]);
-            var value = keyValue[1] || ''; //empty string if no value
-
-            //decode individual values (being liberal by replacing "+" with " ")
-            value = decodeURIComponent(value.replace(/\+/g, " ")).split(",");
-
-            //if there's only one value, do not return as array                    
-            if (value.length == 1) {
-                value = value[0];
-            }                
-            
-            parameters[key] = value;
-         }
-     }
-    return parameters;
-};
-
-/**
- * Function: getArgs
- * *Deprecated*.  Will be removed in 3.0.  Please use instead
- *     <OpenLayers.Util.getParameters>
- * 
- * Parameters:
- * url - {String} Optional url used to extract the query string.
- *                If null, query string is taken from page location.
- * 
- * Returns:
- * {Object} An object of key/value pairs from the query string.
- */
-OpenLayers.Util.getArgs = function(url) {
-    OpenLayers.Console.warn(
-        OpenLayers.i18n(
-            "methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
-        )
-    );
-    return OpenLayers.Util.getParameters(url);
-};
-
-/**
- * Property: lastSeqID
- * {Integer} The ever-incrementing count variable.
- *           Used for generating unique ids.
- */
-OpenLayers.Util.lastSeqID = 0;
-
-/**
- * Function: createUniqueID
- * Create a unique identifier for this session.  Each time this function
- *     is called, a counter is incremented.  The return will be the optional
- *     prefix (defaults to "id_") appended with the counter value.
- * 
- * Parameters:
- * prefix {String} Optionsal string to prefix unique id. Default is "id_".
- * 
- * Returns:
- * {String} A unique id string, built on the passed in prefix.
- */
-OpenLayers.Util.createUniqueID = function(prefix) {
-    if (prefix == null) {
-        prefix = "id_";
-    }
-    OpenLayers.Util.lastSeqID += 1; 
-    return prefix + OpenLayers.Util.lastSeqID;        
-};
-
-/**
- * Constant: INCHES_PER_UNIT
- * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
- * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
- * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
- * and PROJ.4 (http://trac.osgeo.org/proj/)
- * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
- * The hardcoded table of PROJ.4 units are in pj_units.c.
- */
-OpenLayers.INCHES_PER_UNIT = { 
-    'inches': 1.0,
-    'ft': 12.0,
-    'mi': 63360.0,
-    'm': 39.3701,
-    'km': 39370.1,
-    'dd': 4374754,
-    'yd': 36
-};
-OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
-OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
-OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
-
-// Units from CS-Map
-OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
-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.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
- * {Integer} 72 (A sensible default)
- */
-OpenLayers.DOTS_PER_INCH = 72;
-
-/**
- * Function: normalizeScale
- * 
- * Parameters:
- * scale - {float}
- * 
- * Returns:
- * {Float} A normalized scale value, in 1 / X format. 
- *         This means that if a value less than one ( already 1/x) is passed
- *         in, it just returns scale directly. Otherwise, it returns 
- *         1 / scale
- */
-OpenLayers.Util.normalizeScale = function (scale) {
-    var normScale = (scale > 1.0) ? (1.0 / scale) 
-                                  : scale;
-    return normScale;
-};
-
-/**
- * Function: getResolutionFromScale
- * 
- * Parameters:
- * scale - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- *                  Default is degrees
- * 
- * Returns:
- * {Float} The corresponding resolution given passed-in scale and unit 
- *     parameters.  If the given scale is falsey, the returned resolution will
- *     be undefined.
- */
-OpenLayers.Util.getResolutionFromScale = function (scale, units) {
-    var resolution;
-    if (scale) {
-        if (units == null) {
-            units = "degrees";
-        }
-        var normScale = OpenLayers.Util.normalizeScale(scale);
-        resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
-                                        * OpenLayers.DOTS_PER_INCH);        
-    }
-    return resolution;
-};
-
-/**
- * Function: getScaleFromResolution
- * 
- * Parameters:
- * resolution - {Float}
- * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
- *                  Default is degrees
- * 
- * Returns:
- * {Float} The corresponding scale given passed-in resolution and unit 
- *         parameters.
- */
-OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
-
-    if (units == null) {
-        units = "degrees";
-    }
-
-    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
-                    OpenLayers.DOTS_PER_INCH;
-    return scale;
-};
-
-/**
- * Function: safeStopPropagation
- * *Deprecated*. This function has been deprecated. Please use directly 
- *     <OpenLayers.Event.stop> passing 'true' as the 2nd 
- *     argument (preventDefault)
- * 
- * Safely stop the propagation of an event *without* preventing
- *   the default browser action from occurring.
- * 
- * Parameter:
- * evt - {Event}
- */
-OpenLayers.Util.safeStopPropagation = function(evt) {
-    OpenLayers.Event.stop(evt, true);
-};
-
-/**
- * Function: pagePositon
- * Calculates the position of an element on the page. 
- *
- * Parameters:
- * forElement - {DOMElement}
- * 
- * Returns:
- * {Array} two item array, L value then T value.
- */
-OpenLayers.Util.pagePosition = function(forElement) {
-    var valueT = 0, valueL = 0;
-
-    var element = forElement;
-    var child = forElement;
-    while(element) {
-
-        if(element == document.body) {
-            if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
-                break;
-            }
-        }
-        
-        valueT += element.offsetTop  || 0;
-        valueL += element.offsetLeft || 0;
-
-        child = element;
-        try {
-            // wrapping this in a try/catch because IE chokes on the offsetParent
-            element = element.offsetParent;
-        } catch(e) {
-            OpenLayers.Console.error(OpenLayers.i18n(
-                                  "pagePositionFailed",{'elemId':element.id}));
-            break;
-        }
-    }
-
-    element = forElement;
-    while(element) {
-        valueT -= element.scrollTop  || 0;
-        valueL -= element.scrollLeft || 0;
-        element = element.parentNode;
-    }
-    
-    return [valueL, valueT];
-};
-
-
-/** 
- * Function: isEquivalentUrl
- * Test two URLs for equivalence. 
- * 
- * Setting 'ignoreCase' allows for case-independent comparison.
- * 
- * Comparison is based on: 
- *  - Protocol
- *  - Host (evaluated without the port)
- *  - Port (set 'ignorePort80' to ignore "80" values)
- *  - Hash ( set 'ignoreHash' to disable)
- *  - Pathname (for relative <-> absolute comparison) 
- *  - Arguments (so they can be out of order)
- *  
- * Parameters:
- * url1 - {String}
- * url2 - {String}
- * options - {Object} Allows for customization of comparison:
- *                    'ignoreCase' - Default is True
- *                    'ignorePort80' - Default is True
- *                    'ignoreHash' - Default is True
- *
- * Returns:
- * {Boolean} Whether or not the two URLs are equivalent
- */
-OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
-    options = options || {};
-
-    OpenLayers.Util.applyDefaults(options, {
-        ignoreCase: true,
-        ignorePort80: true,
-        ignoreHash: true
-    });
-
-    var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
-    var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
-
-    //compare all keys except for "args" (treated below)
-    for(var key in urlObj1) {
-        if(key !== "args") {
-            if(urlObj1[key] != urlObj2[key]) {
-                return false;
-            }
-        }
-    }
-
-    // compare search args - irrespective of order
-    for(var key in urlObj1.args) {
-        if(urlObj1.args[key] != urlObj2.args[key]) {
-            return false;
-        }
-        delete urlObj2.args[key];
-    }
-    // urlObj2 shouldn't have any args left
-    for(var key in urlObj2.args) {
-        return false;
-    }
-    
-    return true;
-};
-
-/**
- * Function: createUrlObject
- * 
- * Parameters:
- * url - {String}
- * options - {Object} A hash of options.  Can be one of:
- *            ignoreCase: lowercase url,
- *            ignorePort80: don't include explicit port if port is 80,
- *            ignoreHash: Don't include part of url after the hash (#).
- * 
- * Returns:
- * {Object} An object with separate url, a, port, host, and args parsed out 
- *          and ready for comparison
- */
-OpenLayers.Util.createUrlObject = function(url, options) {
-    options = options || {};
-
-    // deal with relative urls first
-    if(!(/^\w+:\/\//).test(url)) {
-        var loc = window.location;
-        var port = loc.port ? ":" + loc.port : "";
-        var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
-        if(url.indexOf("/") === 0) {
-            // full pathname
-            url = fullUrl + url;
-        } else {
-            // relative to current path
-            var parts = loc.pathname.split("/");
-            parts.pop();
-            url = fullUrl + parts.join("/") + "/" + url;
-        }
-    }
-  
-    if (options.ignoreCase) {
-        url = url.toLowerCase(); 
-    }
-
-    var a = document.createElement('a');
-    a.href = url;
-    
-    var urlObject = {};
-    
-    //host (without port)
-    urlObject.host = a.host.split(":").shift();
-
-    //protocol
-    urlObject.protocol = a.protocol;  
-
-    //port (get uniform browser behavior with port 80 here)
-    if(options.ignorePort80) {
-        urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
-    } else {
-        urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
-    }
-
-    //hash
-    urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;  
-    
-    //args
-    var queryString = a.search;
-    if (!queryString) {
-        var qMark = url.indexOf("?");
-        queryString = (qMark != -1) ? url.substr(qMark) : "";
-    }
-    urlObject.args = OpenLayers.Util.getParameters(queryString);
-
-    //pathname (uniform browser behavior with leading "/")
-    urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
-    
-    return urlObject; 
-};
- 
-/**
- * Function: removeTail
- * Takes a url and removes everything after the ? and #
- * 
- * Parameters:
- * url - {String} The url to process
- * 
- * Returns:
- * {String} The string with all queryString and Hash removed
- */
-OpenLayers.Util.removeTail = function(url) {
-    var head = null;
-    
-    var qMark = url.indexOf("?");
-    var hashMark = url.indexOf("#");
-
-    if (qMark == -1) {
-        head = (hashMark != -1) ? url.substr(0,hashMark) : url;
-    } else {
-        head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark)) 
-                                  : url.substr(0, qMark);
-    }
-    return head;
-};
-
-
-/**
- * Function: getBrowserName
- * 
- * Returns:
- * {String} A string which specifies which is the current 
- *          browser in which we are running. 
- * 
- *          Currently-supported browser detection and codes:
- *           * 'opera' -- Opera
- *           * 'msie'  -- Internet Explorer
- *           * 'safari' -- Safari
- *           * 'firefox' -- FireFox
- *           * 'mozilla' -- Mozilla
- * 
- *          If we are unable to property identify the browser, we 
- *           return an empty string.
- */
-OpenLayers.Util.getBrowserName = function() {
-    var browserName = "";
-    
-    var ua = navigator.userAgent.toLowerCase();
-    if ( ua.indexOf( "opera" ) != -1 ) {
-        browserName = "opera";
-    } else if ( ua.indexOf( "msie" ) != -1 ) {
-        browserName = "msie";
-    } else if ( ua.indexOf( "safari" ) != -1 ) {
-        browserName = "safari";
-    } else if ( ua.indexOf( "mozilla" ) != -1 ) {
-        if ( ua.indexOf( "firefox" ) != -1 ) {
-            browserName = "firefox";
-        } else {
-            browserName = "mozilla";
-        }
-    }
-    
-    return browserName;
-};
-
-
-
-    
-/**
- * Method: getRenderedDimensions
- * Renders the contentHTML offscreen to determine actual dimensions for
- *     popup sizing. As we need layout to determine dimensions the content
- *     is rendered -9999px to the left and absolute to ensure the 
- *     scrollbars do not flicker
- *     
- * Parameters:
- * contentHTML
- * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is 
- *     specified, we fix that dimension of the div to be measured. This is 
- *     useful in the case where we have a limit in one dimension and must 
- *     therefore meaure the flow in the other dimension.
- * 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}
- */
-OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
-    
-    var w, h;
-    
-    // create temp container div with restricted size
-    var container = document.createElement("div");
-    container.style.visibility = "hidden";
-        
-    var containerElement = (options && options.containerElement) 
-    	? options.containerElement : document.body;
-
-    //fix a dimension, if specified.
-    if (size) {
-        if (size.w) {
-            w = size.w;
-            container.style.width = w + "px";
-        } else if (size.h) {
-            h = size.h;
-            container.style.height = h + "px";
-        }
-    }
-
-    //add css classes, if specified
-    if (options && options.displayClass) {
-        container.className = options.displayClass;
-    }
-    
-    // create temp content div and assign content
-    var content = document.createElement("div");
-    content.innerHTML = contentHTML;
-    
-    // we need overflow visible when calculating the size
-    content.style.overflow = "visible";
-    if (content.childNodes) {
-        for (var i=0, l=content.childNodes.length; i<l; i++) {
-            if (!content.childNodes[i].style) continue;
-            content.childNodes[i].style.overflow = "visible";
-        }
-    }
-    
-    // add content to restricted container 
-    container.appendChild(content);
-    
-    // append container to body for rendering
-    containerElement.appendChild(container);
-    
-    // Opera and IE7 can't handle a node with position:aboslute if it inherits
-    // position:absolute from a parent.
-    var parentHasPositionAbsolute = false;
-    var parent = container.parentNode;
-    while (parent && parent.tagName.toLowerCase()!="body") {
-        var parentPosition = OpenLayers.Element.getStyle(parent, "position");
-        if(parentPosition == "absolute") {
-            parentHasPositionAbsolute = true;
-            break;
-        } else if (parentPosition && parentPosition != "static") {
-            break;
-        }
-        parent = parent.parentNode;
-    }
-
-    if(!parentHasPositionAbsolute) {
-        container.style.position = "absolute";
-    }
-    
-    // calculate scroll width of content and add corners and shadow width
-    if (!w) {
-        w = parseInt(content.scrollWidth);
-    
-        // update container width to allow height to adjust
-        container.style.width = w + "px";
-    }        
-    // capture height and add shadow and corner image widths
-    if (!h) {
-        h = parseInt(content.scrollHeight);
-    }
-
-    // remove elements
-    container.removeChild(content);
-    containerElement.removeChild(container);
-    
-    return new OpenLayers.Size(w, h);
-};
-
-/**
- * APIFunction: getScrollbarWidth
- * This function has been modified by the OpenLayers from the original version,
- *     written by Matthew Eernisse and released under the Apache 2 
- *     license here:
- * 
- *     http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
- * 
- *     It has been modified simply to cache its value, since it is physically 
- *     impossible that this code could ever run in more than one browser at 
- *     once. 
- * 
- * Returns:
- * {Integer}
- */
-OpenLayers.Util.getScrollbarWidth = function() {
-    
-    var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
-    
-    if (scrollbarWidth == null) {
-        var scr = null;
-        var inn = null;
-        var wNoScroll = 0;
-        var wScroll = 0;
-    
-        // Outer scrolling div
-        scr = document.createElement('div');
-        scr.style.position = 'absolute';
-        scr.style.top = '-1000px';
-        scr.style.left = '-1000px';
-        scr.style.width = '100px';
-        scr.style.height = '50px';
-        // Start with no scrollbar
-        scr.style.overflow = 'hidden';
-    
-        // Inner content div
-        inn = document.createElement('div');
-        inn.style.width = '100%';
-        inn.style.height = '200px';
-    
-        // Put the inner div in the scrolling div
-        scr.appendChild(inn);
-        // Append the scrolling div to the doc
-        document.body.appendChild(scr);
-    
-        // Width of the inner div sans scrollbar
-        wNoScroll = inn.offsetWidth;
-    
-        // Add the scrollbar
-        scr.style.overflow = 'scroll';
-        // Width of the inner div width scrollbar
-        wScroll = inn.offsetWidth;
-    
-        // Remove the scrolling div from the doc
-        document.body.removeChild(document.body.lastChild);
-    
-        // Pixel width of the scroller
-        OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
-        scrollbarWidth = OpenLayers.Util._scrollbarWidth;
-    }
-
-    return scrollbarWidth;
-};
-
-/**
- * APIFunction: getFormattedLonLat
- * This function will return latitude or longitude value formatted as 
- *
- * Parameters:
- * coordinate - {Float} the coordinate value to be formatted
- * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
- *          to be formatted (default = lat)
- * dmsOption - {String} specify the precision of the output can be one of:
- *           'dms' show degrees minutes and seconds
- *           'dm' show only degrees and minutes
- *           'd' show only degrees
- * 
- * Returns:
- * {String} the coordinate value formatted as a string
- */
-OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
-    if (!dmsOption) {
-        dmsOption = 'dms';    //default to show degree, minutes, seconds
-    }
-    var abscoordinate = Math.abs(coordinate)
-    var coordinatedegrees = Math.floor(abscoordinate);
-
-    var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
-    var tempcoordinateminutes = coordinateminutes;
-    coordinateminutes = Math.floor(coordinateminutes);
-    var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
-    coordinateseconds =  Math.round(coordinateseconds*10);
-    coordinateseconds /= 10;
-
-    if( coordinatedegrees < 10 ) {
-        coordinatedegrees = "0" + coordinatedegrees;
-    }
-    var str = coordinatedegrees + "\u00B0";
-
-    if (dmsOption.indexOf('dm') >= 0) {
-        if( coordinateminutes < 10 ) {
-            coordinateminutes = "0" + coordinateminutes;
-        }
-        str += coordinateminutes + "'";
-  
-        if (dmsOption.indexOf('dms') >= 0) {
-            if( coordinateseconds < 10 ) {
-                coordinateseconds = "0" + coordinateseconds;
-            }
-            str += coordinateseconds + '"';
-        }
-    }
-    
-    if (axis == "lon") {
-        str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
-    } else {
-        str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
-    }
-    return str;
-};
-
 /* ======================================================================
     OpenLayers/Console.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
  * Namespace: OpenLayers.Console
  * The OpenLayers.Console namespace is used for debugging and error logging.
  * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
@@ -2508,22 +587,154 @@
     }
 })();
 /* ======================================================================
+    OpenLayers/Lang.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes.js
+ * @requires OpenLayers/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();
+        }
+        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.BROWSER_NAME == "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 && 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/BaseTypes.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
- * @requires OpenLayers/BaseTypes/Class.js
- * @requires OpenLayers/BaseTypes/LonLat.js
- * @requires OpenLayers/BaseTypes/Size.js
- * @requires OpenLayers/BaseTypes/Pixel.js
- * @requires OpenLayers/BaseTypes/Bounds.js
- * @requires OpenLayers/BaseTypes/Element.js
- * @requires OpenLayers/Lang/en.js
+ * @requires OpenLayers/Lang.js
  * @requires OpenLayers/Console.js
  */
  
@@ -3106,7 +1317,7 @@
         if ("toISOString" in Date.prototype) {
             return function(date) {
                 return date.toISOString();
-            }
+            };
         } else {
             function pad(num, len) {
                 var str = num + "";
@@ -3132,7 +1343,7 @@
                         pad(date.getUTCMilliseconds(), 3) + "Z";
                 }
                 return str;
-            }
+            };
         }
 
     })(),
@@ -3162,7 +1373,6 @@
             date = new Date(elapsed);
         } else {
             var match = str.match(/^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))?$/);
-            var date;
             if (match && (match[1] || match[7])) { // must have at least year or time
                 var year = parseInt(match[1], 10) || 0;
                 var month = (parseInt(match[2], 10) - 1) || 0;
@@ -3180,7 +1390,7 @@
                     // check offset
                     if (type !== "Z") {
                         var hoursOffset = parseInt(type, 10);
-                        var minutesOffset = parseInt(match[8]) || 0;
+                        var minutesOffset = parseInt(match[8], 10) || 0;
                         var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
                         date = new Date(date.getTime() + offset);
                     }
@@ -3194,225 +1404,18 @@
 
 };
 /* ======================================================================
-    OpenLayers/BaseTypes/Class.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * Constructor: OpenLayers.Class
- * Base class used to construct all other classes. Includes support for 
- *     multiple inheritance. 
- *     
- * This constructor is new in OpenLayers 2.5.  At OpenLayers 3.0, the old 
- *     syntax for creating classes and dealing with inheritance 
- *     will be removed.
- * 
- * To create a new OpenLayers-style class, use the following syntax:
- * > var MyClass = OpenLayers.Class(prototype);
- *
- * To create a new OpenLayers-style class with multiple inheritance, use the
- *     following syntax:
- * > var MyClass = OpenLayers.Class(Class1, Class2, prototype);
- * Note that instanceof reflection will only reveil Class1 as superclass.
- * Class2 ff are mixins.
- *
- */
-OpenLayers.Class = function() {
-    var Class = function() {
-        /**
-         * This following condition can be removed at 3.0 - this is only for
-         * backwards compatibility while the Class.inherit method is still
-         * in use.  So at 3.0, the following three lines would be replaced with
-         * simply:
-         * this.initialize.apply(this, arguments);
-         */
-        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
-            this.initialize.apply(this, arguments);
-        }
-    };
-    var extended = {};
-    var parent, initialize, Type;
-    for(var i=0, len=arguments.length; i<len; ++i) {
-        Type = arguments[i];
-        if(typeof Type == "function") {
-            // make the class passed as the first argument the superclass
-            if(i == 0 && len > 1) {
-                initialize = Type.prototype.initialize;
-                // replace the initialize method with an empty function,
-                // because we do not want to create a real instance here
-                Type.prototype.initialize = function() {};
-                // the line below makes sure that the new class has a
-                // superclass
-                extended = new Type();
-                // restore the original initialize method
-                if(initialize === undefined) {
-                    delete Type.prototype.initialize;
-                } else {
-                    Type.prototype.initialize = initialize;
-                }
-            }
-            // get the prototype of the superclass
-            parent = Type.prototype;
-        } else {
-            // in this case we're extending with the prototype
-            parent = Type;
-        }
-        OpenLayers.Util.extend(extended, parent);
-    }
-    Class.prototype = extended;
-    return Class;
-};
-
-/**
- * Property: isPrototype
- * *Deprecated*.  This is no longer needed and will be removed at 3.0.
- */
-OpenLayers.Class.isPrototype = function () {};
-
-/**
- * APIFunction: OpenLayers.create
- * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
- *     <OpenLayers.Class> constructor instead.
- *
- * Returns:
- * An OpenLayers class
- */
-OpenLayers.Class.create = function() {
-    return function() {
-        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
-            this.initialize.apply(this, arguments);
-        }
-    };
-};
-
-
-/**
- * APIFunction: inherit
- * *Deprecated*.  Old method to inherit from one or more OpenLayers style
- *     classes.  Use the <OpenLayers.Class> constructor instead.
- *
- * Parameters:
- * class - One or more classes can be provided as arguments
- *
- * Returns:
- * An object prototype
- */
-OpenLayers.Class.inherit = function () {
-    var superClass = arguments[0];
-    var proto = new superClass(OpenLayers.Class.isPrototype);
-    for (var i=1, len=arguments.length; i<len; i++) {
-        if (typeof arguments[i] == "function") {
-            var mixin = arguments[i];
-            arguments[i] = new mixin(OpenLayers.Class.isPrototype);
-        }
-        OpenLayers.Util.extend(proto, arguments[i]);
-    }
-    return proto;
-};
-/* ======================================================================
-    OpenLayers/BaseTypes/Size.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * Class: OpenLayers.Size
- * Instances of this class represent a width/height pair
- */
-OpenLayers.Size = OpenLayers.Class({
-
-    /**
-     * APIProperty: w
-     * {Number} width
-     */
-    w: 0.0,
-    
-    /**
-     * APIProperty: h
-     * {Number} height
-     */
-    h: 0.0,
-
-
-    /**
-     * Constructor: OpenLayers.Size
-     * Create an instance of OpenLayers.Size
-     *
-     * Parameters:
-     * w - {Number} width
-     * h - {Number} height
-     */
-    initialize: function(w, h) {
-        this.w = parseFloat(w);
-        this.h = parseFloat(h);
-    },
-
-    /**
-     * Method: toString
-     * Return the string representation of a size object
-     *
-     * Returns:
-     * {String} The string representation of OpenLayers.Size object. 
-     * (ex. <i>"w=55,h=66"</i>)
-     */
-    toString:function() {
-        return ("w=" + this.w + ",h=" + this.h);
-    },
-
-    /**
-     * APIMethod: clone
-     * Create a clone of this size object
-     *
-     * Returns:
-     * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
-     * values
-     */
-    clone:function() {
-        return new OpenLayers.Size(this.w, this.h);
-    },
-
-    /**
-     *
-     * APIMethod: equals
-     * Determine where this size is equal to another
-     *
-     * Parameters:
-     * sz - {<OpenLayers.Size>}
-     *
-     * Returns: 
-     * {Boolean} The passed in size has the same h and w properties as this one.
-     * Note that if sz passed in is null, returns false.
-     *
-     */
-    equals:function(sz) {
-        var equals = false;
-        if (sz != null) {
-            equals = ((this.w == sz.w && this.h == sz.h) ||
-                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
-        }
-        return equals;
-    },
-
-    CLASS_NAME: "OpenLayers.Size"
-});
-/* ======================================================================
     OpenLayers/BaseTypes/Bounds.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -4021,14 +2024,15 @@
  * 
  * Parameters: 
  * str - {String}Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
+ * reverseAxisOrder - {Boolean} Does the string use reverse axis order?
  * 
  * Returns:
  * {<OpenLayers.Bounds>} New bounds object built from the 
  *                       passed-in String.
  */
-OpenLayers.Bounds.fromString = function(str) {
+OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
     var bounds = str.split(",");
-    return OpenLayers.Bounds.fromArray(bounds);
+    return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
 };
 
 /** 
@@ -4038,12 +2042,18 @@
  * 
  * Parameters:
  * bbox - {Array(Float)} Array of bounds values (ex. <i>[5,42,10,45]</i>)
+ * reverseAxisOrder - {Boolean} Does the array use reverse axis order?
  *
  * Returns:
  * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
  */
-OpenLayers.Bounds.fromArray = function(bbox) {
-    return new OpenLayers.Bounds(parseFloat(bbox[0]),
+OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
+    return reverseAxisOrder === true ?
+           new OpenLayers.Bounds(parseFloat(bbox[1]),
+                                 parseFloat(bbox[0]),
+                                 parseFloat(bbox[3]),
+                                 parseFloat(bbox[2])) :
+           new OpenLayers.Bounds(parseFloat(bbox[0]),
                                  parseFloat(bbox[1]),
                                  parseFloat(bbox[2]),
                                  parseFloat(bbox[3]));
@@ -4091,12 +2101,17 @@
     OpenLayers/BaseTypes/Element.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/BaseTypes.js
+ */
+
+/**
  * Namespace: OpenLayers.Element
  */
 OpenLayers.Element = {
@@ -4346,13 +2361,15 @@
     OpenLayers/BaseTypes/LonLat.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -4540,13 +2557,15 @@
     OpenLayers/BaseTypes/Pixel.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -4666,5859 +2685,1985 @@
     CLASS_NAME: "OpenLayers.Pixel"
 });
 /* ======================================================================
-    OpenLayers/Icon.js
+    OpenLayers/BaseTypes/Size.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
- * Class: OpenLayers.Icon
- * 
- * The icon represents a graphical icon on the screen.  Typically used in
- * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
- *
- * An icon has a url, size and position.  It also contains an offset which 
- * allows the center point to be represented correctly.  This can be
- * provided either as a fixed offset or a function provided to calculate
- * the desired offset. 
- * 
+ * @requires OpenLayers/BaseTypes/Class.js
  */
-OpenLayers.Icon = OpenLayers.Class({
-    
-    /** 
-     * Property: url 
-     * {String}  image url
-     */
-    url: null,
-    
-    /** 
-     * Property: size 
-     * {<OpenLayers.Size>} 
-     */
-    size: null,
 
-    /** 
-     * Property: offset 
-     * {<OpenLayers.Pixel>} distance in pixels to offset the image when being rendered
-     */
-    offset: null,    
-    
-    /** 
-     * Property: calculateOffset 
-     * {<OpenLayers.Pixel>} Function to calculate the offset (based on the size) 
-     */
-    calculateOffset: null,    
-    
-    /** 
-     * Property: imageDiv 
-     * {DOMElement} 
-     */
-    imageDiv: null,
+/**
+ * Class: OpenLayers.Size
+ * Instances of this class represent a width/height pair
+ */
+OpenLayers.Size = OpenLayers.Class({
 
-    /** 
-     * Property: px 
-     * {<OpenLayers.Pixel>} 
+    /**
+     * APIProperty: w
+     * {Number} width
      */
-    px: null,
+    w: 0.0,
     
-    /** 
-     * Constructor: OpenLayers.Icon
-     * Creates an icon, which is an image tag in a div.  
-     *
-     * url - {String} 
-     * size - {<OpenLayers.Size>} 
-     * offset - {<OpenLayers.Pixel>}
-     * calculateOffset - {Function} 
+    /**
+     * APIProperty: h
+     * {Number} height
      */
-    initialize: function(url, size, offset, calculateOffset) {
-        this.url = url;
-        this.size = (size) ? size : new OpenLayers.Size(20,20);
-        this.offset = offset ? offset : new OpenLayers.Pixel(-(this.size.w/2), -(this.size.h/2));
-        this.calculateOffset = calculateOffset;
+    h: 0.0,
 
-        var id = OpenLayers.Util.createUniqueID("OL_Icon_");
-        this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
-    },
-    
-    /** 
-     * Method: destroy
-     * Nullify references and remove event listeners to prevent circular 
-     * references and memory leaks
-     */
-    destroy: function() {
-        // erase any drawn elements
-        this.erase();
 
-        OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
-        this.imageDiv.innerHTML = "";
-        this.imageDiv = null;
-    },
-
-    /** 
-     * Method: clone
-     * 
-     * Returns:
-     * {<OpenLayers.Icon>} A fresh copy of the icon.
-     */
-    clone: function() {
-        return new OpenLayers.Icon(this.url, 
-                                   this.size, 
-                                   this.offset, 
-                                   this.calculateOffset);
-    },
-    
     /**
-     * Method: setSize
-     * 
+     * Constructor: OpenLayers.Size
+     * Create an instance of OpenLayers.Size
+     *
      * Parameters:
-     * size - {<OpenLayers.Size>} 
+     * w - {Number} width
+     * h - {Number} height
      */
-    setSize: function(size) {
-        if (size != null) {
-            this.size = size;
-        }
-        this.draw();
+    initialize: function(w, h) {
+        this.w = parseFloat(w);
+        this.h = parseFloat(h);
     },
-    
+
     /**
-     * Method: setUrl
-     * 
-     * Parameters:
-     * url - {String} 
+     * Method: toString
+     * Return the string representation of a size object
+     *
+     * Returns:
+     * {String} The string representation of OpenLayers.Size object. 
+     * (ex. <i>"w=55,h=66"</i>)
      */
-    setUrl: function(url) {
-        if (url != null) {
-            this.url = url;
-        }
-        this.draw();
+    toString:function() {
+        return ("w=" + this.w + ",h=" + this.h);
     },
 
-    /** 
-     * Method: draw
-     * Move the div to the given pixel.
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
-     * 
+    /**
+     * APIMethod: clone
+     * Create a clone of this size object
+     *
      * Returns:
-     * {DOMElement} A new DOM Image of this icon set at the location passed-in
+     * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
+     * values
      */
-    draw: function(px) {
-        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
-                                            null, 
-                                            null, 
-                                            this.size, 
-                                            this.url, 
-                                            "absolute");
-        this.moveTo(px);
-        return this.imageDiv;
-    }, 
+    clone:function() {
+        return new OpenLayers.Size(this.w, this.h);
+    },
 
-    /** 
-     * Method: erase
-     * Erase the underlying image element.
+    /**
      *
-     */
-    erase: function() {
-        if (this.imageDiv != null && this.imageDiv.parentNode != null) {
-            OpenLayers.Element.remove(this.imageDiv);
-        }
-    }, 
-    
-    /** 
-     * Method: setOpacity
-     * Change the icon's opacity
+     * APIMethod: equals
+     * Determine where this size is equal to another
      *
      * Parameters:
-     * opacity - {float} 
-     */
-    setOpacity: function(opacity) {
-        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
-                                            null, null, null, null, opacity);
-
-    },
-    
-    /**
-     * Method: moveTo
-     * move icon to passed in px.
+     * sz - {<OpenLayers.Size>}
      *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
+     * Returns: 
+     * {Boolean} The passed in size has the same h and w properties as this one.
+     * Note that if sz passed in is null, returns false.
+     *
      */
-    moveTo: function (px) {
-        //if no px passed in, use stored location
-        if (px != null) {
-            this.px = px;
+    equals:function(sz) {
+        var equals = false;
+        if (sz != null) {
+            equals = ((this.w == sz.w && this.h == sz.h) ||
+                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
         }
-
-        if (this.imageDiv != null) {
-            if (this.px == null) {
-                this.display(false);
-            } else {
-                if (this.calculateOffset) {
-                    this.offset = this.calculateOffset(this.size);  
-                }
-                var offsetPx = this.px.offset(this.offset);
-                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
-            }
-        }
+        return equals;
     },
-    
-    /** 
-     * Method: display
-     * Hide or show the icon
-     *
-     * Parameters:
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.imageDiv.style.display = (display) ? "" : "none"; 
-    },
-    
 
-    /**
-     * APIMethod: isDrawn
-     * 
-     * Returns:
-     * {Boolean} Whether or not the icon is drawn.
-     */
-    isDrawn: function() {
-        // nodeType 11 for ie, whose nodes *always* have a parentNode
-        // (of type document fragment)
-        var isDrawn = (this.imageDiv && this.imageDiv.parentNode && 
-                       (this.imageDiv.parentNode.nodeType != 11));    
-
-        return isDrawn;   
-    },
-
-    CLASS_NAME: "OpenLayers.Icon"
+    CLASS_NAME: "OpenLayers.Size"
 });
 /* ======================================================================
-    OpenLayers/Popup.js
+    OpenLayers/Util.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
+/**
+ * @requires OpenLayers/BaseTypes.js
+ * @requires OpenLayers/BaseTypes/Bounds.js
+ * @requires OpenLayers/BaseTypes/Element.js
+ * @requires OpenLayers/BaseTypes/LonLat.js
+ * @requires OpenLayers/BaseTypes/Pixel.js
+ * @requires OpenLayers/BaseTypes/Size.js
+ * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
+ */
 
 /**
- * Class: OpenLayers.Popup
- * A popup is a small div that can opened and closed on the map.
- * Typically opened in response to clicking on a marker.  
- * See <OpenLayers.Marker>.  Popup's don't require their own
- * layer and are added the the map using the <OpenLayers.Map.addPopup>
- * method.
- *
- * Example:
- * (code)
- * popup = new OpenLayers.Popup("chicken", 
- *                    new OpenLayers.LonLat(5,40),
- *                    new OpenLayers.Size(200,200),
- *                    "example popup",
- *                    true);
- *       
- * map.addPopup(popup);
- * (end)
+ * Namespace: Util
  */
-OpenLayers.Popup = OpenLayers.Class({
+OpenLayers.Util = OpenLayers.Util || {};
 
-    /** 
-     * Property: events  
-     * {<OpenLayers.Events>} custom event manager 
-     */
-    events: null,
-    
-    /** Property: id
-     * {String} the unique identifier assigned to this popup.
-     */
-    id: "",
+/** 
+ * Function: getElement
+ * This is the old $() from prototype
+ */
+OpenLayers.Util.getElement = function() {
+    var elements = [];
 
-    /** 
-     * Property: lonlat 
-     * {<OpenLayers.LonLat>} the position of this popup on the map
-     */
-    lonlat: null,
-
-    /** 
-     * Property: div 
-     * {DOMElement} the div that contains this popup.
-     */
-    div: null,
-
-    /** 
-     * Property: contentSize 
-     * {<OpenLayers.Size>} the width and height of the content.
-     */
-    contentSize: null,    
-
-    /** 
-     * Property: size 
-     * {<OpenLayers.Size>} the width and height of the popup.
-     */
-    size: null,    
-
-    /** 
-     * Property: contentHTML 
-     * {String} An HTML string for this popup to display.
-     */
-    contentHTML: null,
-    
-    /** 
-     * Property: backgroundColor 
-     * {String} the background color used by the popup.
-     */
-    backgroundColor: "",
-    
-    /** 
-     * Property: opacity 
-     * {float} the opacity of this popup (between 0.0 and 1.0)
-     */
-    opacity: "",
-
-    /** 
-     * Property: border 
-     * {String} the border size of the popup.  (eg 2px)
-     */
-    border: "",
-    
-    /** 
-     * Property: contentDiv 
-     * {DOMElement} a reference to the element that holds the content of
-     *              the div.
-     */
-    contentDiv: null,
-    
-    /** 
-     * Property: groupDiv 
-     * {DOMElement} First and only child of 'div'. The group Div contains the
-     *     'contentDiv' and the 'closeDiv'.
-     */
-    groupDiv: null,
-
-    /** 
-     * Property: closeDiv
-     * {DOMElement} the optional closer image
-     */
-    closeDiv: null,
-
-    /** 
-     * APIProperty: autoSize
-     * {Boolean} Resize the popup to auto-fit the contents.
-     *     Default is false.
-     */
-    autoSize: false,
-
-    /**
-     * APIProperty: minSize
-     * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
-     */
-    minSize: null,
-
-    /**
-     * APIProperty: maxSize
-     * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
-     */
-    maxSize: null,
-
-    /** 
-     * Property: displayClass
-     * {String} The CSS class of the popup.
-     */
-    displayClass: "olPopup",
-
-    /** 
-     * Property: contentDisplayClass
-     * {String} The CSS class of the popup content div.
-     */
-    contentDisplayClass: "olPopupContent",
-
-    /** 
-     * Property: padding 
-     * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal 
-     *     padding of the content div inside the popup. This was originally
-     *     confused with the css padding as specified in style.css's 
-     *     'olPopupContent' class. We would like to get rid of this altogether,
-     *     except that it does come in handy for the framed and anchoredbubble
-     *     popups, who need to maintain yet another barrier between their 
-     *     content and the outer border of the popup itself. 
-     * 
-     *     Note that in order to not break API, we must continue to support 
-     *     this property being set as an integer. Really, though, we'd like to 
-     *     have this specified as a Bounds object so that user can specify
-     *     distinct left, top, right, bottom paddings. With the 3.0 release
-     *     we can make this only a bounds.
-     */
-    padding: 0,
-
-    /** 
-     * Property: disableFirefoxOverflowHack
-     * {Boolean} The hack for overflow in Firefox causes all elements 
-     *     to be re-drawn, which causes Flash elements to be 
-     *     re-initialized, which is troublesome.
-     *     With this property the hack can be disabled.
-     */
-    disableFirefoxOverflowHack: false,
-
-    /**
-     * Method: fixPadding
-     * To be removed in 3.0, this function merely helps us to deal with the 
-     *     case where the user may have set an integer value for padding, 
-     *     instead of an <OpenLayers.Bounds> object.
-     */
-    fixPadding: function() {
-        if (typeof this.padding == "number") {
-            this.padding = new OpenLayers.Bounds(
-                this.padding, this.padding, this.padding, this.padding
-            );
+    for (var i=0, len=arguments.length; i<len; i++) {
+        var element = arguments[i];
+        if (typeof element == 'string') {
+            element = document.getElementById(element);
         }
-    },
-
-    /**
-     * APIProperty: panMapIfOutOfView
-     * {Boolean} When drawn, pan map such that the entire popup is visible in
-     *     the current viewport (if necessary).
-     *     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
-     * {Boolean} When map pans, close the popup.
-     *     Default is false.
-     */
-    closeOnMove: false,
-    
-    /** 
-     * Property: map 
-     * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
-     */
-    map: null,
-
-    /** 
-    * Constructor: OpenLayers.Popup
-    * Create a popup.
-    * 
-    * Parameters: 
-    * id - {String} a unqiue identifier for this popup.  If null is passed
-    *               an identifier will be automatically generated. 
-    * lonlat - {<OpenLayers.LonLat>}  The position on the map the popup will
-    *                                 be shown.
-    * contentSize - {<OpenLayers.Size>} The size of the content.
-    * contentHTML - {String}          An HTML string to display inside the   
-    *                                 popup.
-    * closeBox - {Boolean}            Whether to display a close box inside
-    *                                 the popup.
-    * closeBoxCallback - {Function}   Function to be called on closeBox click.
-    */
-    initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
-        if (id == null) {
-            id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        if (arguments.length == 1) {
+            return element;
         }
+        elements.push(element);
+    }
+    return elements;
+};
 
-        this.id = id;
-        this.lonlat = lonlat;
+/**
+ * Function: isElement
+ * A cross-browser implementation of "e instanceof Element".
+ *
+ * Parameters:
+ * o - {Object} The object to test.
+ *
+ * Returns:
+ * {Boolean}
+ */
+OpenLayers.Util.isElement = function(o) {
+    return !!(o && o.nodeType === 1);
+};
 
-        this.contentSize = (contentSize != null) ? contentSize 
-                                  : new OpenLayers.Size(
-                                                   OpenLayers.Popup.WIDTH,
-                                                   OpenLayers.Popup.HEIGHT);
-        if (contentHTML != null) { 
-             this.contentHTML = contentHTML;
-        }
-        this.backgroundColor = OpenLayers.Popup.COLOR;
-        this.opacity = OpenLayers.Popup.OPACITY;
-        this.border = OpenLayers.Popup.BORDER;
+/** 
+ * Maintain existing definition of $.
+ */
+if(typeof window.$  === "undefined") {
+    window.$ = OpenLayers.Util.getElement;
+}
 
-        this.div = OpenLayers.Util.createDiv(this.id, null, null, 
-                                             null, null, null, "hidden");
-        this.div.className = this.displayClass;
-        
-        var groupDivId = this.id + "_GroupDiv";
-        this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, 
-                                                    null, "relative", null,
-                                                    "hidden");
-
-        var id = this.div.id + "_contentDiv";
-        this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), 
-                                                    null, "relative");
-        this.contentDiv.className = this.contentDisplayClass;
-        this.groupDiv.appendChild(this.contentDiv);
-        this.div.appendChild(this.groupDiv);
-
-        if (closeBox) {
-            this.addCloseBox(closeBoxCallback);
-        } 
-
-        this.registerEvents();
-    },
-
-    /** 
-     * Method: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-
-        this.id = null;
-        this.lonlat = null;
-        this.size = null;
-        this.contentHTML = null;
-        
-        this.backgroundColor = null;
-        this.opacity = null;
-        this.border = null;
-        
-        if (this.closeOnMove && this.map) {
-            this.map.events.unregister("movestart", this, this.hide);
+/** 
+ * Function: removeItem
+ * Remove an object from an array. Iterates through the array
+ *     to find the item, then removes it.
+ *
+ * Parameters:
+ * array - {Array}
+ * item - {Object}
+ * 
+ * Return
+ * {Array} A reference to the array
+ */
+OpenLayers.Util.removeItem = function(array, item) {
+    for(var i = array.length - 1; i >= 0; i--) {
+        if(array[i] == item) {
+            array.splice(i,1);
+            //break;more than once??
         }
+    }
+    return array;
+};
 
-        this.events.destroy();
-        this.events = null;
-        
-        if (this.closeDiv) {
-            OpenLayers.Event.stopObservingElement(this.closeDiv); 
-            this.groupDiv.removeChild(this.closeDiv);
-        }
-        this.closeDiv = null;
-        
-        this.div.removeChild(this.groupDiv);
-        this.groupDiv = null;
+/**
+ * Function: clearArray
+ * *Deprecated*. This function will disappear in 3.0.
+ * Please use "array.length = 0" instead.
+ * 
+ * Parameters:
+ * array - {Array}
+ */
+OpenLayers.Util.clearArray = function(array) {
+    OpenLayers.Console.warn(
+        OpenLayers.i18n(
+            "methodDeprecated", {'newMethod': 'array = []'}
+        )
+    );
+    array.length = 0;
+};
 
-        if (this.map != null) {
-            this.map.removePopup(this);
-        }
-        this.map = null;
-        this.div = null;
-        
-        this.autoSize = null;
-        this.minSize = null;
-        this.maxSize = null;
-        this.padding = null;
-        this.panMapIfOutOfView = null;
-    },
-
-    /** 
-    * Method: draw
-    * Constructs the elements that make up the popup.
-    *
-    * Parameters:
-    * px - {<OpenLayers.Pixel>} the position the popup in pixels.
-    * 
-    * Returns:
-    * {DOMElement} Reference to a div that contains the drawn popup
-    */
-    draw: function(px) {
-        if (px == null) {
-            if ((this.lonlat != null) && (this.map != null)) {
-                px = this.map.getLayerPxFromLonLat(this.lonlat);
+/** 
+ * Function: indexOf
+ * Seems to exist already in FF, but not in MOZ.
+ * 
+ * Parameters:
+ * array - {Array}
+ * obj - {Object}
+ * 
+ * Returns:
+ * {Integer} The index at, which the first object was found in the array.
+ *           If not found, returns -1.
+ */
+OpenLayers.Util.indexOf = function(array, obj) {
+    // use the build-in function if available.
+    if (typeof array.indexOf == "function") {
+        return array.indexOf(obj);
+    } else {
+        for (var i = 0, len = array.length; i < len; i++) {
+            if (array[i] == obj) {
+                return i;
             }
         }
+        return -1;   
+    }
+};
 
-        // this assumes that this.map already exists, which is okay because 
-        // this.draw is only called once the popup has been added to the map.
-        if (this.closeOnMove) {
-            this.map.events.register("movestart", this, this.hide);
-        }
-        
-        //listen to movestart, moveend to disable overflow (FF bug)
-        if (!this.disableFirefoxOverflowHack && OpenLayers.Util.getBrowserName() == 'firefox') {
-            this.map.events.register("movestart", this, function() {
-                var style = document.defaultView.getComputedStyle(
-                    this.contentDiv, null
-                );
-                var currentOverflow = style.getPropertyValue("overflow");
-                if (currentOverflow != "hidden") {
-                    this.contentDiv._oldOverflow = currentOverflow;
-                    this.contentDiv.style.overflow = "hidden";
-                }
-            });
-            this.map.events.register("moveend", this, function() {
-                var oldOverflow = this.contentDiv._oldOverflow;
-                if (oldOverflow) {
-                    this.contentDiv.style.overflow = oldOverflow;
-                    this.contentDiv._oldOverflow = null;
-                }
-            });
-        }
 
-        this.moveTo(px);
-        if (!this.autoSize && !this.size) {
-            this.setSize(this.contentSize);
-        }
-        this.setBackgroundColor();
-        this.setOpacity();
-        this.setBorder();
-        this.setContentHTML();
-        
-        if (this.panMapIfOutOfView) {
-            this.panIntoView();
-        }    
 
-        return this.div;
-    },
+/**
+ * Function: modifyDOMElement
+ * 
+ * Modifies many properties of a DOM element all at once.  Passing in 
+ * null to an individual parameter will avoid setting the attribute.
+ *
+ * Parameters:
+ * id - {String} The element id attribute to set.
+ * px - {<OpenLayers.Pixel>} The left and top style position.
+ * sz - {<OpenLayers.Size>}  The width and height style attributes.
+ * position - {String}       The position attribute.  eg: absolute, 
+ *                           relative, etc.
+ * border - {String}         The style.border attribute.  eg:
+ *                           solid black 2px
+ * overflow - {String}       The style.overview attribute.  
+ * opacity - {Float}         Fractional value (0.0 - 1.0)
+ */
+OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
+                                            border, overflow, opacity) {
 
-    /** 
-     * Method: updatePosition
-     * if the popup has a lonlat and its map members set, 
-     * then have it move itself to its proper position
-     */
-    updatePosition: function() {
-        if ((this.lonlat) && (this.map)) {
-            var px = this.map.getLayerPxFromLonLat(this.lonlat);
-            if (px) {
-                this.moveTo(px);           
-            }    
-        }
-    },
+    if (id) {
+        element.id = id;
+    }
+    if (px) {
+        element.style.left = px.x + "px";
+        element.style.top = px.y + "px";
+    }
+    if (sz) {
+        element.style.width = sz.w + "px";
+        element.style.height = sz.h + "px";
+    }
+    if (position) {
+        element.style.position = position;
+    }
+    if (border) {
+        element.style.border = border;
+    }
+    if (overflow) {
+        element.style.overflow = overflow;
+    }
+    if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
+        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
+        element.style.opacity = opacity;
+    } else if (parseFloat(opacity) == 1.0) {
+        element.style.filter = '';
+        element.style.opacity = '';
+    }
+};
 
-    /**
-     * Method: moveTo
-     * 
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} the top and left position of the popup div. 
-     */
-    moveTo: function(px) {
-        if ((px != null) && (this.div != null)) {
-            this.div.style.left = px.x + "px";
-            this.div.style.top = px.y + "px";
-        }
-    },
+/** 
+ * Function: createDiv
+ * Creates a new div and optionally set some standard attributes.
+ * Null may be passed to each parameter if you do not wish to
+ * set a particular attribute.
+ * Note - zIndex is NOT set on the resulting div.
+ * 
+ * Parameters:
+ * id - {String} An identifier for this element.  If no id is
+ *               passed an identifier will be created 
+ *               automatically.
+ * px - {<OpenLayers.Pixel>} The element left and top position. 
+ * sz - {<OpenLayers.Size>} The element width and height.
+ * imgURL - {String} A url pointing to an image to use as a 
+ *                   background image.
+ * position - {String} The style.position value. eg: absolute,
+ *                     relative etc.
+ * border - {String} The the style.border value. 
+ *                   eg: 2px solid black
+ * overflow - {String} The style.overflow value. Eg. hidden
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * 
+ * Returns: 
+ * {DOMElement} A DOM Div created with the specified attributes.
+ */
+OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
+                                     border, overflow, opacity) {
 
-    /**
-     * Method: visible
-     *
-     * Returns:      
-     * {Boolean} Boolean indicating whether or not the popup is visible
-     */
-    visible: function() {
-        return OpenLayers.Element.visible(this.div);
-    },
+    var dom = document.createElement('div');
 
-    /**
-     * Method: toggle
-     * Toggles visibility of the popup.
-     */
-    toggle: function() {
-        if (this.visible()) {
-            this.hide();
-        } else {
-            this.show();
-        }
-    },
+    if (imgURL) {
+        dom.style.backgroundImage = 'url(' + imgURL + ')';
+    }
 
-    /**
-     * Method: show
-     * Makes the popup visible.
-     */
-    show: function() {
-        OpenLayers.Element.show(this.div);
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "absolute";
+    }
+    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
+                                     border, overflow, opacity);
 
-        if (this.panMapIfOutOfView) {
-            this.panIntoView();
-        }    
-    },
+    return dom;
+};
 
-    /**
-     * Method: hide
-     * Makes the popup invisible.
-     */
-    hide: function() {
-        OpenLayers.Element.hide(this.div);
-    },
+/**
+ * Function: createImage
+ * Creates an img element with specific attribute values.
+ *  
+ * Parameters:
+ * id - {String} The id field for the img.  If none assigned one will be
+ *               automatically generated.
+ * px - {<OpenLayers.Pixel>} The left and top positions.
+ * sz - {<OpenLayers.Size>} The style.width and style.height values.
+ * imgURL - {String} The url to use as the image source.
+ * position - {String} The style.position value.
+ * border - {String} The border to place around the image.
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * delayDisplay - {Boolean} If true waits until the image has been
+ *                          loaded.
+ * 
+ * Returns:
+ * {DOMElement} A DOM Image created with the specified attributes.
+ */
+OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
+                                       opacity, delayDisplay) {
 
-    /**
-     * Method: setSize
-     * Used to adjust the size of the popup. 
-     *
-     * Parameters:
-     * contentSize - {<OpenLayers.Size>} the new size for the popup's 
-     *     contents div (in pixels).
-     */
-    setSize:function(contentSize) { 
-        this.size = contentSize.clone(); 
-        
-        // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
-        //  must add that to the desired "size". 
-        var contentDivPadding = this.getContentDivPadding();
-        var wPadding = contentDivPadding.left + contentDivPadding.right;
-        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+    var image = document.createElement("img");
 
-        // take into account the popup's 'padding' property
-        this.fixPadding();
-        wPadding += this.padding.left + this.padding.right;
-        hPadding += this.padding.top + this.padding.bottom;
+    //set generic properties
+    if (!id) {
+        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
+    }
+    if (!position) {
+        position = "relative";
+    }
+    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
+                                     border, null, opacity);
 
-        // make extra space for the close div
-        if (this.closeDiv) {
-            var closeDivWidth = parseInt(this.closeDiv.style.width);
-            wPadding += closeDivWidth + contentDivPadding.right;
-        }
+    if(delayDisplay) {
+        image.style.display = "none";
+        OpenLayers.Event.observe(image, "load", 
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, image));
+        OpenLayers.Event.observe(image, "error", 
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, image));
+        
+    }
+    
+    //set special properties
+    image.style.alt = id;
+    image.galleryImg = "no";
+    if (imgURL) {
+        image.src = imgURL;
+    }
 
-        //increase size of the main popup div to take into account the 
-        // users's desired padding and close div.        
-        this.size.w += wPadding;
-        this.size.h += hPadding;
 
-        //now if our browser is IE, we need to actually make the contents 
-        // div itself bigger to take its own padding into effect. this makes 
-        // me want to shoot someone, but so it goes.
-        if (OpenLayers.Util.getBrowserName() == "msie") {
-            this.contentSize.w += 
-                contentDivPadding.left + contentDivPadding.right;
-            this.contentSize.h += 
-                contentDivPadding.bottom + contentDivPadding.top;
-        }
-
-        if (this.div != null) {
-            this.div.style.width = this.size.w + "px";
-            this.div.style.height = this.size.h + "px";
-        }
-        if (this.contentDiv != null){
-            this.contentDiv.style.width = contentSize.w + "px";
-            this.contentDiv.style.height = contentSize.h + "px";
-        }
-    },  
-
-    /**
-     * APIMethod: updateSize
-     * Auto size the popup so that it precisely fits its contents (as 
-     *     determined by this.contentDiv.innerHTML). Popup size will, of
-     *     course, be limited by the available space on the current map
-     */
-    updateSize: function() {
         
-        // determine actual render dimensions of the contents by putting its
-        // contents into a fake contentDiv (for the CSS) and then measuring it
-        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,
-                containerElement: containerElement
-            }
-        );
+    return image;
+};
 
-        // is the "real" size of the div is safe to display in our map?
-        var safeSize = this.getSafeContentSize(realSize);
+/**
+ * Function: setOpacity
+ * *Deprecated*.  This function has been deprecated. Instead, please use 
+ *     <OpenLayers.Util.modifyDOMElement> 
+ *     or 
+ *     <OpenLayers.Util.modifyAlphaImageDiv>
+ * 
+ * Set the opacity of a DOM Element
+ *     Note that for this function to work in IE, elements must "have layout"
+ *     according to:
+ *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
+ *
+ * Parameters:
+ * element - {DOMElement} Set the opacity on this DOM element
+ * opacity - {Float} Opacity value (0.0 - 1.0)
+ */
+OpenLayers.Util.setOpacity = function(element, opacity) {
+    OpenLayers.Util.modifyDOMElement(element, null, null, null,
+                                     null, null, null, opacity);
+};
 
-        var newSize = null;
-        if (safeSize.equals(realSize)) {
-            //real size of content is small enough to fit on the map, 
-            // so we use real size.
-            newSize = realSize;
+/**
+ * Function: onImageLoad
+ * Bound to image load events.  For all images created with <createImage> or
+ *     <createAlphaImageDiv>, this function will be bound to the load event.
+ */
+OpenLayers.Util.onImageLoad = function() {
+    // The complex check here is to solve issues described in #480.
+    // Every time a map view changes, it increments the 'viewRequestID' 
+    // property. As the requests for the images for the new map view are sent
+    // out, they are tagged with this unique viewRequestID. 
+    // 
+    // If an image has no viewRequestID property set, we display it regardless, 
+    // but if it does have a viewRequestID property, we check that it matches 
+    // the viewRequestID set on the map.
+    // 
+    // If the viewRequestID on the map has changed, that means that the user
+    // has changed the map view since this specific request was sent out, and
+    // therefore this tile does not need to be displayed (so we do not execute
+    // this code that turns its display on).
+    //
+    if (!this.viewRequestID ||
+        (this.map && this.viewRequestID == this.map.viewRequestID)) { 
+        this.style.display = "";  
+    }
+    OpenLayers.Element.removeClass(this, "olImageLoadError");
+};
 
-        } else {
+/**
+ * Property: IMAGE_RELOAD_ATTEMPTS
+ * {Integer} How many times should we try to reload an image before giving up?
+ *           Default is 0
+ */
+OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
 
-            //make a new OL.Size object with the clipped dimensions 
-            // set or null if not clipped.
-            var fixedSize = new OpenLayers.Size();
-            fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
-            fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
-        
-            if (fixedSize.w && fixedSize.h) {
-                //content is too big in both directions, so we will use 
-                // max popup size (safeSize), knowing well that it will 
-                // overflow both ways.                
-                newSize = safeSize;
-            } else {
-                //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,
-                        containerElement: containerElement
-                    }
-                );
-                
-                //if the clipped size is still the same as the safeSize, 
-                // that means that our content must be fixed in the 
-                // offending direction. If overflow is 'auto', this means 
-                // we are going to have a scrollbar for sure, so we must 
-                // adjust for that.
-                //
-                var currentOverflow = OpenLayers.Element.getStyle(
-                    this.contentDiv, "overflow"
-                );
-                if ( (currentOverflow != "hidden") && 
-                     (clippedSize.equals(safeSize)) ) {
-                    var scrollBar = OpenLayers.Util.getScrollbarWidth();
-                    if (fixedSize.w) {
-                        clippedSize.h += scrollBar;
-                    } else {
-                        clippedSize.w += scrollBar;
-                    }
+/**
+ * Function: onImageLoadError 
+ */
+OpenLayers.Util.onImageLoadError = function() {
+    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
+    if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+        var urls = this.urls;
+        if (urls && urls instanceof Array && urls.length > 1){
+            var src = this.src.toString();
+            var current_url, k;
+            for (k = 0; current_url = urls[k]; k++){
+                if(src.indexOf(current_url) != -1){
+                    break;
                 }
-                
-                newSize = this.getSafeContentSize(clippedSize);
             }
-        }                        
-        this.setSize(newSize);     
-    },    
-
-    /**
-     * Method: setBackgroundColor
-     * Sets the background color of the popup.
-     *
-     * Parameters:
-     * color - {String} the background color.  eg "#FFBBBB"
-     */
-    setBackgroundColor:function(color) { 
-        if (color != undefined) {
-            this.backgroundColor = color; 
+            var guess = Math.floor(urls.length * Math.random());
+            var new_url = urls[guess];
+            k = 0;
+            while(new_url == current_url && k++ < 4){
+                guess = Math.floor(urls.length * Math.random());
+                new_url = urls[guess];
+            }
+            this.src = src.replace(current_url, new_url);
+        } else {
+            this.src = this.src;
         }
-        
-        if (this.div != null) {
-            this.div.style.backgroundColor = this.backgroundColor;
-        }
-    },  
-    
-    /**
-     * Method: setOpacity
-     * Sets the opacity of the popup.
-     * 
-     * Parameters:
-     * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
-     */
-    setOpacity:function(opacity) { 
-        if (opacity != undefined) {
-            this.opacity = opacity; 
-        }
-        
-        if (this.div != null) {
-            // for Mozilla and Safari
-            this.div.style.opacity = this.opacity;
+    } else {
+        OpenLayers.Element.addClass(this, "olImageLoadError");
+    }
+    this.style.display = "";
+};
 
-            // for IE
-            this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
-        }
-    },  
-    
-    /**
-     * Method: setBorder
-     * Sets the border style of the popup.
-     *
-     * Parameters:
-     * border - {String} The border style value. eg 2px 
-     */
-    setBorder:function(border) { 
-        if (border != undefined) {
-            this.border = border;
-        }
-        
-        if (this.div != null) {
-            this.div.style.border = this.border;
-        }
-    },      
-    
-    /**
-     * Method: setContentHTML
-     * Allows the user to set the HTML content of the popup.
-     *
-     * Parameters:
-     * contentHTML - {String} HTML for the div.
-     */
-    setContentHTML:function(contentHTML) {
+/**
+ * Property: alphaHackNeeded
+ * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
+ */
+OpenLayers.Util.alphaHackNeeded = null;
 
-        if (contentHTML != null) {
-            this.contentHTML = contentHTML;
-        }
-       
-        if ((this.contentDiv != null) && 
-            (this.contentHTML != null) &&
-            (this.contentHTML != this.contentDiv.innerHTML)) {
-       
-            this.contentDiv.innerHTML = this.contentHTML;
-       
-            if (this.autoSize) {
-                
-                //if popup has images, listen for when they finish
-                // loading and resize accordingly
-                this.registerImageListeners();
-
-                //auto size the popup to its current contents
-                this.updateSize();
-            }
-        }    
-
-    },
+/**
+ * Function: alphaHack
+ * Checks whether it's necessary (and possible) to use the png alpha
+ * hack which allows alpha transparency for png images under Internet
+ * Explorer.
+ * 
+ * Returns:
+ * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
+ */
+OpenLayers.Util.alphaHack = function() {
+    if (OpenLayers.Util.alphaHackNeeded == null) {
+        var arVersion = navigator.appVersion.split("MSIE");
+        var version = parseFloat(arVersion[1]);
+        var filter = false;
     
-    /**
-     * Method: registerImageListeners
-     * Called when an image contained by the popup loaded. this function
-     *     updates the popup size, then unregisters the image load listener.
-     */   
-    registerImageListeners: function() { 
-
-        // As the images load, this function will call updateSize() to 
-        // resize the popup to fit the content div (which presumably is now
-        // bigger than when the image was not loaded).
-        // 
-        // If the 'panMapIfOutOfView' property is set, we will pan the newly
-        // resized popup back into view.
-        // 
-        // Note that this function, when called, will have 'popup' and 
-        // 'img' properties in the context.
-        //
-        var onImgLoad = function() {
-            
-            this.popup.updateSize();
-     
-            if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
-                this.popup.panIntoView();
-            }
-
-            OpenLayers.Event.stopObserving(
-                this.img, "load", this.img._onImageLoad
-            );
+        // IEs4Lin dies when trying to access document.body.filters, because 
+        // the property is there, but requires a DLL that can't be provided. This
+        // means that we need to wrap this in a try/catch so that this can
+        // continue.
     
-        };
-
-        //cycle through the images and if their size is 0x0, that means that 
-        // they haven't been loaded yet, so we attach the listener, which 
-        // will fire when the images finish loading and will resize the 
-        // popup accordingly to its new size.
-        var images = this.contentDiv.getElementsByTagName("img");
-        for (var i = 0, len = images.length; i < len; i++) {
-            var img = images[i];
-            if (img.width == 0 || img.height == 0) {
-
-                var context = {
-                    'popup': this,
-                    'img': img
-                };
-
-                //expando this function to the image itself before registering
-                // it. This way we can easily and properly unregister it.
-                img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
-
-                OpenLayers.Event.observe(img, 'load', img._onImgLoad);
-            }    
-        } 
-    },
-
-    /**
-     * APIMethod: getSafeContentSize
-     * 
-     * Parameters:
-     * size - {<OpenLayers.Size>} Desired size to make the popup.
-     * 
-     * Returns:
-     * {<OpenLayers.Size>} A size to make the popup which is neither smaller
-     *     than the specified minimum size, nor bigger than the maximum 
-     *     size (which is calculated relative to the size of the viewport).
-     */
-    getSafeContentSize: function(size) {
-
-        var safeContentSize = size.clone();
-
-        // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
-        //  must add that to the desired "size". 
-        var contentDivPadding = this.getContentDivPadding();
-        var wPadding = contentDivPadding.left + contentDivPadding.right;
-        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
-
-        // take into account the popup's 'padding' property
-        this.fixPadding();
-        wPadding += this.padding.left + this.padding.right;
-        hPadding += this.padding.top + this.padding.bottom;
-
-        if (this.closeDiv) {
-            var closeDivWidth = parseInt(this.closeDiv.style.width);
-            wPadding += closeDivWidth + contentDivPadding.right;
-        }
-
-        // prevent the popup from being smaller than a specified minimal size
-        if (this.minSize) {
-            safeContentSize.w = Math.max(safeContentSize.w, 
-                (this.minSize.w - wPadding));
-            safeContentSize.h = Math.max(safeContentSize.h, 
-                (this.minSize.h - hPadding));
-        }
-
-        // prevent the popup from being bigger than a specified maximum size
-        if (this.maxSize) {
-            safeContentSize.w = Math.min(safeContentSize.w, 
-                (this.maxSize.w - wPadding));
-            safeContentSize.h = Math.min(safeContentSize.h, 
-                (this.maxSize.h - hPadding));
-        }
-        
-        //make sure the desired size to set doesn't result in a popup that 
-        // is bigger than the map's viewport.
-        //
-        if (this.map && this.map.size) {
-            
-            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 - extraY;
-            
-            var maxX = this.map.size.w - 
-                this.map.paddingForPopups.left - 
-                this.map.paddingForPopups.right - 
-                wPadding - extraX;
-            
-            safeContentSize.w = Math.min(safeContentSize.w, maxX);
-            safeContentSize.h = Math.min(safeContentSize.h, maxY);
-        }
-        
-        return safeContentSize;
-    },
+        try { 
+            filter = !!(document.body.filters);
+        } catch (e) {}    
     
-    /**
-     * Method: getContentDivPadding
-     * Glorious, oh glorious hack in order to determine the css 'padding' of 
-     *     the contentDiv. IE/Opera return null here unless we actually add the 
-     *     popup's main 'div' element (which contains contentDiv) to the DOM. 
-     *     So we make it invisible and then add it to the document temporarily. 
-     *
-     *     Once we've taken the padding readings we need, we then remove it 
-     *     from the DOM (it will actually get added to the DOM in 
-     *     Map.js's addPopup)
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>}
-     */
-    getContentDivPadding: function() {
+        OpenLayers.Util.alphaHackNeeded = (filter && 
+                                           (version >= 5.5) && (version < 7));
+    }
+    return OpenLayers.Util.alphaHackNeeded;
+};
 
-        //use cached value if we have it
-        var contentDivPadding = this._contentDivPadding;
-        if (!contentDivPadding) {
+/** 
+ * Function: modifyAlphaImageDiv
+ * 
+ * div - {DOMElement} Div containing Alpha-adjusted Image
+ * id - {String}
+ * px - {<OpenLayers.Pixel>}
+ * sz - {<OpenLayers.Size>}
+ * imgURL - {String}
+ * position - {String}
+ * border - {String}
+ * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ */ 
+OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity) {
 
-        	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"),
-                OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
-                OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
-                OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
-            );
-    
-            //cache the value
-            this._contentDivPadding = contentDivPadding;
+    OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
+                                     null, null, opacity);
 
-            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;
-    },
+    var img = div.childNodes[0];
 
-    /**
-     * Method: addCloseBox
-     * 
-     * Parameters:
-     * callback - {Function} The callback to be called when the close button
-     *     is clicked.
-     */
-    addCloseBox: function(callback) {
-
-        this.closeDiv = OpenLayers.Util.createDiv(
-            this.id + "_close", null, new OpenLayers.Size(17, 17)
-        );
-        this.closeDiv.className = "olPopupCloseBox"; 
-        
-        // use the content div's css padding to determine if we should
-        //  padd the close div
-        var contentDivPadding = this.getContentDivPadding();
-         
-        this.closeDiv.style.right = contentDivPadding.right + "px";
-        this.closeDiv.style.top = contentDivPadding.top + "px";
-        this.groupDiv.appendChild(this.closeDiv);
-
-        var closePopup = callback || function(e) {
-            this.hide();
-            OpenLayers.Event.stop(e);
-        };
-        OpenLayers.Event.observe(this.closeDiv, "click", 
-                OpenLayers.Function.bindAsEventListener(closePopup, this));
-    },
-
-    /**
-     * Method: panIntoView
-     * Pans the map such that the popup is totaly viewable (if necessary)
-     */
-    panIntoView: function() {
-        
-        var mapSize = this.map.getSize();
+    if (imgURL) {
+        img.src = imgURL;
+    }
+    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
+                                     "relative", border);
     
-        //start with the top left corner of the popup, in px, 
-        // relative to the viewport
-        var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
-            parseInt(this.div.style.left),
-            parseInt(this.div.style.top)
-        ));
-        var newTL = origTL.clone();
-    
-        //new left (compare to margins, using this.size to calculate right)
-        if (origTL.x < this.map.paddingForPopups.left) {
-            newTL.x = this.map.paddingForPopups.left;
-        } else 
-        if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
-            newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
+    if (OpenLayers.Util.alphaHack()) {
+        if(div.style.display != "none") {
+            div.style.display = "inline-block";
         }
-        
-        //new top (compare to margins, using this.size to calculate bottom)
-        if (origTL.y < this.map.paddingForPopups.top) {
-            newTL.y = this.map.paddingForPopups.top;
-        } else 
-        if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
-            newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
+        if (sizing == null) {
+            sizing = "scale";
         }
         
-        var dx = origTL.x - newTL.x;
-        var dy = origTL.y - newTL.y;
-        
-        this.map.pan(dx, dy);
-    },
-
-    /** 
-     * Method: registerEvents
-     * Registers events on the popup.
-     *
-     * Do this in a separate function so that subclasses can 
-     *   choose to override it if they wish to deal differently
-     *   with mouse events
-     * 
-     *   Note in the following handler functions that some special
-     *    care is needed to deal correctly with mousing and popups. 
-     *   
-     *   Because the user might select the zoom-rectangle option and
-     *    then drag it over a popup, we need a safe way to allow the
-     *    mousemove and mouseup events to pass through the popup when
-     *    they are initiated from outside.
-     * 
-     *   Otherwise, we want to essentially kill the event propagation
-     *    for all other events, though we have to do so carefully, 
-     *    without disabling basic html functionality, like clicking on 
-     *    hyperlinks or drag-selecting text.
-     */
-     registerEvents:function() {
-        this.events = new OpenLayers.Events(this, this.div, null, true);
-
-        this.events.on({
-            "mousedown": this.onmousedown,
-            "mousemove": this.onmousemove,
-            "mouseup": this.onmouseup,
-            "click": this.onclick,
-            "mouseout": this.onmouseout,
-            "dblclick": this.ondblclick,
-            scope: this
-        });
-        
-     },
-
-    /** 
-     * Method: onmousedown 
-     * When mouse goes down within the popup, make a note of
-     *   it locally, and then do not propagate the mousedown 
-     *   (but do so safely so that user can select text inside)
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    onmousedown: function (evt) {
-        this.mousedown = true;
-        OpenLayers.Event.stop(evt, true);
-    },
-
-    /** 
-     * Method: onmousemove
-     * If the drag was started within the popup, then 
-     *   do not propagate the mousemove (but do so safely
-     *   so that user can select text inside)
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    onmousemove: function (evt) {
-        if (this.mousedown) {
-            OpenLayers.Event.stop(evt, true);
+        div.style.filter = "progid:DXImageTransform.Microsoft" +
+                           ".AlphaImageLoader(src='" + img.src + "', " +
+                           "sizingMethod='" + sizing + "')";
+        if (parseFloat(div.style.opacity) >= 0.0 && 
+            parseFloat(div.style.opacity) < 1.0) {
+            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
         }
-    },
 
-    /** 
-     * Method: onmouseup
-     * When mouse comes up within the popup, after going down 
-     *   in it, reset the flag, and then (once again) do not 
-     *   propagate the event, but do so safely so that user can 
-     *   select text inside
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    onmouseup: function (evt) {
-        if (this.mousedown) {
-            this.mousedown = false;
-            OpenLayers.Event.stop(evt, true);
-        }
-    },
+        img.style.filter = "alpha(opacity=0)";
+    }
+};
 
-    /**
-     * Method: onclick
-     * Ignore clicks, but allowing default browser handling
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    onclick: function (evt) {
-        OpenLayers.Event.stop(evt, true);
-    },
-
-    /** 
-     * Method: onmouseout
-     * When mouse goes out of the popup set the flag to false so that
-     *   if they let go and then drag back in, we won't be confused.
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    onmouseout: function (evt) {
-        this.mousedown = false;
-    },
+/** 
+ * Function: createAlphaImageDiv
+ * 
+ * id - {String}
+ * px - {<OpenLayers.Pixel>}
+ * sz - {<OpenLayers.Size>}
+ * imgURL - {String}
+ * position - {String}
+ * border - {String}
+ * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * opacity - {Float} Fractional value (0.0 - 1.0)
+ * delayDisplay - {Boolean} If true waits until the image has been
+ *                          loaded.
+ * 
+ * Returns:
+ * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
+ *              needed for transparency in IE, it is added.
+ */ 
+OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
+                                               position, border, sizing, 
+                                               opacity, delayDisplay) {
     
-    /** 
-     * Method: ondblclick
-     * Ignore double-clicks, but allowing default browser handling
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    ondblclick: function (evt) {
-        OpenLayers.Event.stop(evt, true);
-    },
+    var div = OpenLayers.Util.createDiv();
+    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
+                                          null, false);
+    div.appendChild(img);
 
-    CLASS_NAME: "OpenLayers.Popup"
-});
+    if (delayDisplay) {
+        img.style.display = "none";
+        OpenLayers.Event.observe(img, "load",
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, div));
+        OpenLayers.Event.observe(img, "error",
+            OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, div));
+    }
 
-OpenLayers.Popup.WIDTH = 200;
-OpenLayers.Popup.HEIGHT = 200;
-OpenLayers.Popup.COLOR = "white";
-OpenLayers.Popup.OPACITY = 1;
-OpenLayers.Popup.BORDER = "0px";
-/* ======================================================================
-    OpenLayers/Protocol.js
-   ====================================================================== */
+    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
+                                        border, sizing, opacity);
+    
+    return div;
+};
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
 
-/**
- * Class: OpenLayers.Protocol
- * Abstract vector layer protocol class.  Not to be instantiated directly.  Use
- *     one of the protocol subclasses instead.
+/** 
+ * Function: upperCaseObject
+ * Creates a new hashtable and copies over all the keys from the 
+ *     passed-in object, but storing them under an uppercased
+ *     version of the key at which they were stored.
+ * 
+ * Parameters: 
+ * object - {Object}
+ * 
+ * Returns: 
+ * {Object} A new Object with all the same keys but uppercased
  */
-OpenLayers.Protocol = OpenLayers.Class({
-    
-    /**
-     * Property: format
-     * {<OpenLayers.Format>} The format used by this protocol.
-     */
-    format: null,
-    
-    /**
-     * Property: options
-     * {Object} Any options sent to the constructor.
-     */
-    options: null,
+OpenLayers.Util.upperCaseObject = function (object) {
+    var uObject = {};
+    for (var key in object) {
+        uObject[key.toUpperCase()] = object[key];
+    }
+    return uObject;
+};
 
-    /**
-     * Property: autoDestroy
-     * {Boolean} The creator of the protocol can set autoDestroy to false
-     *      to fully control when the protocol is destroyed. Defaults to
-     *      true.
+/** 
+ * Function: applyDefaults
+ * Takes an object and copies any properties that don't exist from
+ *     another properties, by analogy with OpenLayers.Util.extend() from
+ *     Prototype.js.
+ * 
+ * Parameters:
+ * to - {Object} The destination object.
+ * from - {Object} The source object.  Any properties of this object that
+ *     are undefined in the to object will be set on the to object.
+ *
+ * Returns:
+ * {Object} A reference to the to object.  Note that the to argument is modified
+ *     in place and returned by this function.
+ */
+OpenLayers.Util.applyDefaults = function (to, from) {
+    to = to || {};
+    /*
+     * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
+     * prototype object" when calling hawOwnProperty if the source object is an
+     * instance of window.Event.
      */
-    autoDestroy: true,
-   
-    /**
-     * Property: defaultFilter
-     * {OpenLayers.Filter} Optional default filter to read requests
-     */
-    defaultFilter: null,
-    
-    /**
-     * Constructor: OpenLayers.Protocol
-     * Abstract class for vector protocols.  Create instances of a subclass.
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     */
-    initialize: function(options) {
-        options = options || {};
-        OpenLayers.Util.extend(this, options);
-        this.options = options;
-    },
+    var fromIsEvt = typeof window.Event == "function"
+                    && from instanceof window.Event;
 
-    /**
-     * Method: mergeWithDefaultFilter
-     * Merge filter passed to the read method with the default one
-     *
-     * Parameters:
-     * filter - {OpenLayers.Filter}
-     */
-    mergeWithDefaultFilter: function(filter) {
-        var merged;
-        if (filter && this.defaultFilter) {
-            merged = new OpenLayers.Filter.Logical({
-                type: OpenLayers.Filter.Logical.AND,
-                filters: [this.defaultFilter, filter]
-            });
-        } else {
-            merged = filter || this.defaultFilter || undefined;
+    for (var key in from) {
+        if (to[key] === undefined ||
+            (!fromIsEvt && from.hasOwnProperty
+             && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
+            to[key] = from[key];
         }
-        return merged;
-    },
-
+    }
     /**
-     * APIMethod: destroy
-     * Clean up the protocol.
+     * IE doesn't include the toString property when iterating over an object's
+     * properties with the for(property in object) syntax.  Explicitly check if
+     * the source has its own toString property.
      */
-    destroy: function() {
-        this.options = null;
-        this.format = null;
-    },
+    if(!fromIsEvt && from && from.hasOwnProperty
+       && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
+        to.toString = from.toString;
+    }
     
-    /**
-     * APIMethod: read
-     * Construct a request for reading new features.
-     *
-     * Parameters:
-     * options - {Object} Optional object for configuring the request.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     * object, the same object will be passed to the callback function passed
-     * if one exists in the options object.
-     */
-    read: function(options) {
-        options = options || {};
-        options.filter = this.mergeWithDefaultFilter(options.filter);
-    },
-    
-    
-    /**
-     * APIMethod: create
-     * Construct a request for writing newly created features.
-     *
-     * Parameters:
-     * features - {Array({<OpenLayers.Feature.Vector>})} or
-     *            {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     * object, the same object will be passed to the callback function passed
-     * if one exists in the options object.
-     */
-    create: function() {
-    },
-    
-    /**
-     * APIMethod: update
-     * Construct a request updating modified features.
-     *
-     * Parameters:
-     * features - {Array({<OpenLayers.Feature.Vector>})} or
-     *            {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     * object, the same object will be passed to the callback function passed
-     * if one exists in the options object.
-     */
-    update: function() {
-    },
-    
-    /**
-     * APIMethod: delete
-     * Construct a request deleting a removed feature.
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     * object, the same object will be passed to the callback function passed
-     * if one exists in the options object.
-     */
-    "delete": function() {
-    },
+    return to;
+};
 
-    /**
-     * APIMethod: commit
-     * Go over the features and for each take action
-     * based on the feature state. Possible actions are create,
-     * update and delete.
-     *
-     * Parameters:
-     * features - {Array({<OpenLayers.Feature.Vector>})}
-     * options - {Object} Object whose possible keys are "create", "update",
-     *      "delete", "callback" and "scope", the values referenced by the
-     *      first three are objects as passed to the "create", "update", and
-     *      "delete" methods, the value referenced by the "callback" key is
-     *      a function which is called when the commit operation is complete
-     *      using the scope referenced by the "scope" key.
-     *
-     * Returns:
-     * {Array({<OpenLayers.Protocol.Response>})} An array of
-     * <OpenLayers.Protocol.Response> objects.
-     */
-    commit: function() {
-    },
-
-    /**
-     * Method: abort
-     * Abort an ongoing request.
-     *
-     * Parameters:
-     * response - {<OpenLayers.Protocol.Response>}
-     */
-    abort: function(response) {
-    },
-   
-    /**
-     * Method: createCallback
-     * Returns a function that applies the given public method with resp and
-     *     options arguments.
-     *
-     * Parameters:
-     * method - {Function} The method to be applied by the callback.
-     * response - {<OpenLayers.Protocol.Response>} The protocol response object.
-     * options - {Object} Options sent to the protocol method
-     */
-    createCallback: function(method, response, options) {
-        return OpenLayers.Function.bind(function() {
-            method.apply(this, [response, options]);
-        }, this);
-    },
-   
-    CLASS_NAME: "OpenLayers.Protocol" 
-});
-
 /**
- * Class: OpenLayers.Protocol.Response
- * Protocols return Response objects to their users.
- */
-OpenLayers.Protocol.Response = OpenLayers.Class({
-    /**
-     * Property: code
-     * {Number} - OpenLayers.Protocol.Response.SUCCESS or
-     *            OpenLayers.Protocol.Response.FAILURE
-     */
-    code: null,
-
-    /**
-     * Property: requestType
-     * {String} The type of request this response corresponds to. Either
-     *      "create", "read", "update" or "delete".
-     */
-    requestType: null,
-
-    /**
-     * Property: last
-     * {Boolean} - true if this is the last response expected in a commit,
-     * false otherwise, defaults to true.
-     */
-    last: true,
-
-    /**
-     * Property: features
-     * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
-     * The features returned in the response by the server.
-     */
-    features: null,
-
-    /**
-     * Property: reqFeatures
-     * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
-     * The features provided by the user and placed in the request by the
-     *      protocol.
-     */
-    reqFeatures: null,
-
-    /**
-     * Property: priv
-     */
-    priv: null,
-
-    /**
-     * Constructor: OpenLayers.Protocol.Response
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     */
-    initialize: function(options) {
-        OpenLayers.Util.extend(this, options);
-    },
-
-    /**
-     * Method: success
-     *
-     * Returns:
-     * {Boolean} - true on success, false otherwise
-     */
-    success: function() {
-        return this.code > 0;
-    },
-
-    CLASS_NAME: "OpenLayers.Protocol.Response"
-});
-
-OpenLayers.Protocol.Response.SUCCESS = 1;
-OpenLayers.Protocol.Response.FAILURE = 0;
-/* ======================================================================
-    OpenLayers/Renderer.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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 
- * This is the base class for all renderers.
- *
- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
- * It is largely composed of virtual functions that are to be implemented
- * in technology-specific subclasses, but there is some generic code too.
+ * Function: getParameterString
  * 
- * The functions that *are* implemented here merely deal with the maintenance
- *  of the size and extent variables, as well as the cached 'resolution' 
- *  value. 
+ * Parameters:
+ * params - {Object}
  * 
- * A note to the user that all subclasses should use getResolution() instead
- *  of directly accessing this.resolution in order to correctly use the 
- *  cacheing system.
- *
+ * Returns:
+ * {String} A concatenation of the properties of an object in 
+ *          http parameter notation. 
+ *          (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
+ *          If a parameter is actually a list, that parameter will then
+ *          be set to a comma-seperated list of values (foo,bar) instead
+ *          of being URL escaped (foo%3Abar). 
  */
-OpenLayers.Renderer = OpenLayers.Class({
-
-    /** 
-     * Property: container
-     * {DOMElement} 
-     */
-    container: null,
+OpenLayers.Util.getParameterString = function(params) {
+    var paramsArray = [];
     
-    /**
-     * Property: root
-     * {DOMElement}
-     */
-    root: null,
-
-    /** 
-     * Property: extent
-     * {<OpenLayers.Bounds>}
-     */
-    extent: null,
-
-    /**
-     * Property: locked
-     * {Boolean} If the renderer is currently in a state where many things
-     *     are changing, the 'locked' property is set to true. This means 
-     *     that renderers can expect at least one more drawFeature event to be
-     *     called with the 'locked' property set to 'true': In some renderers,
-     *     this might make sense to use as a 'only update local information'
-     *     flag. 
-     */  
-    locked: false,
-    
-    /** 
-     * Property: size
-     * {<OpenLayers.Size>} 
-     */
-    size: null,
-    
-    /**
-     * Property: resolution
-     * {Float} cache of current map resolution
-     */
-    resolution: null,
-    
-    /**
-     * Property: map  
-     * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
-     */
-    map: null,
-    
-    /**
-     * Constructor: OpenLayers.Renderer 
-     *
-     * Parameters:
-     * containerID - {<String>} 
-     * options - {Object} options for this renderer. See sublcasses for
-     *     supported options.
-     */
-    initialize: function(containerID, options) {
-        this.container = OpenLayers.Util.getElement(containerID);
-    },
-    
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        this.container = null;
-        this.extent = null;
-        this.size =  null;
-        this.resolution = null;
-        this.map = null;
-    },
-
-    /**
-     * APIMethod: supported
-     * This should be overridden by specific subclasses
-     * 
-     * Returns:
-     * {Boolean} Whether or not the browser supports the renderer class
-     */
-    supported: function() {
-        return false;
-    },    
-    
-    /**
-     * Method: setExtent
-     * Set the visible part of the layer.
-     *
-     * Resolution has probably changed, so we nullify the resolution 
-     * cache (this.resolution) -- this way it will be re-computed when 
-     * next it is needed.
-     * We nullify the resolution cache (this.resolution) if resolutionChanged
-     * is set to true - this way it will be re-computed on the next
-     * getResolution() request.
-     *
-     * Parameters:
-     * extent - {<OpenLayers.Bounds>}
-     * resolutionChanged - {Boolean}
-     */
-    setExtent: function(extent, resolutionChanged) {
-        this.extent = extent.clone();
-        if (resolutionChanged) {
-            this.resolution = null;
+    for (var key in params) {
+      var value = params[key];
+      if ((value != null) && (typeof value != 'function')) {
+        var encodedValue;
+        if (typeof value == 'object' && value.constructor == Array) {
+          /* value is an array; encode items and separate with "," */
+          var encodedItemArray = [];
+          var item;
+          for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
+            item = value[itemIndex];
+            encodedItemArray.push(encodeURIComponent(
+                (item === null || item === undefined) ? "" : item)
+            );
+          }
+          encodedValue = encodedItemArray.join(",");
         }
-    },
-    
-    /**
-     * Method: setSize
-     * Sets the size of the drawing surface.
-     * 
-     * Resolution has probably changed, so we nullify the resolution 
-     * cache (this.resolution) -- this way it will be re-computed when 
-     * next it is needed.
-     *
-     * Parameters:
-     * size - {<OpenLayers.Size>} 
-     */
-    setSize: function(size) {
-        this.size = size.clone();
-        this.resolution = null;
-    },
-    
-    /** 
-     * Method: getResolution
-     * Uses cached copy of resolution if available to minimize computing
-     * 
-     * Returns:
-     * The current map's resolution
-     */
-    getResolution: function() {
-        this.resolution = this.resolution || this.map.getResolution();
-        return this.resolution;
-    },
-    
-    /**
-     * Method: drawFeature
-     * Draw the feature.  The optional style argument can be used
-     * to override the feature's own style.  This method should only
-     * be called from layer.drawFeature().
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>} 
-     * style - {<Object>}
-     * 
-     * Returns:
-     * {Boolean} true if the feature has been drawn completely, false if not,
-     *     undefined if the feature had no geometry
-     */
-    drawFeature: function(feature, style) {
-        if(style == null) {
-            style = feature.style;
+        else {
+          /* value is a string; simply encode */
+          encodedValue = encodeURIComponent(value);
         }
-        if (feature.geometry) {
-            var bounds = feature.geometry.getBounds();
-            if(bounds) {
-                if (!bounds.intersectsBounds(this.extent)) {
-                    style = {display: "none"};
-                }
-                var rendered = this.drawGeometry(feature.geometry, style, feature.id);
-                if(style.display != "none" && style.label && rendered !== false) {
-                    var location = feature.geometry.getCentroid(); 
-                    if(style.labelXOffset || style.labelYOffset) {
-                        xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
-                        yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
-                        var res = this.getResolution();
-                        location.move(xOffset*res, yOffset*res);
-                    }
-                    this.drawText(feature.id, style, location);
-                } else {
-                    this.removeText(feature.id);
-                }
-                return rendered;
-            }
-        }
-    },
-
-
-    /** 
-     * Method: drawGeometry
-     * 
-     * Draw a geometry.  This should only be called from the renderer itself.
-     * Use layer.drawFeature() from outside the renderer.
-     * virtual function
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} 
-     * style - {Object} 
-     * featureId - {<String>} 
-     */
-    drawGeometry: function(geometry, style, featureId) {},
-        
-    /**
-     * Method: drawText
-     * 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: removeText
-     * Function for removing text labels.
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * featureId - {String}
-     */
-    removeText: function(featureId) {},
+        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
+      }
+    }
     
-    /**
-     * Method: clear
-     * Clear all vectors from the renderer.
-     * virtual function.
-     */    
-    clear: function() {},
+    return paramsArray.join("&");
+};
 
-    /**
-     * Method: getFeatureIdFromEvent
-     * Returns a feature id from an event on the renderer.  
-     * How this happens is specific to the renderer.  This should be
-     * called from layer.getFeatureFromEvent().
-     * Virtual function.
-     * 
-     * Parameters:
-     * evt - {<OpenLayers.Event>} 
-     *
-     * Returns:
-     * {String} A feature id or null.
-     */
-    getFeatureIdFromEvent: function(evt) {},
-    
-    /**
-     * Method: eraseFeatures 
-     * This is called by the layer to erase features
-     * 
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} 
-     */
-    eraseFeatures: function(features) {
-        if(!(features instanceof Array)) {
-            features = [features];
-        }
-        for(var i=0, len=features.length; i<len; ++i) {
-            var feature = features[i];
-            this.eraseGeometry(feature.geometry, feature.id);
-            this.removeText(feature.id);
-        }
-    },
-    
-    /**
-     * Method: eraseGeometry
-     * Remove a geometry from the renderer (by id).
-     * virtual function.
-     * 
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} 
-     * featureId - {String}
-     */
-    eraseGeometry: function(geometry, featureId) {},
-    
-    /**
-     * 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;
-    },
-    
-    /**
-     * Method: applyDefaultSymbolizer
-     * 
-     * Parameters:
-     * symbolizer - {Object}
-     * 
-     * Returns:
-     * {Object}
-     */
-    applyDefaultSymbolizer: function(symbolizer) {
-        var result = OpenLayers.Util.extend({},
-            OpenLayers.Renderer.defaultSymbolizer);
-        if(symbolizer.stroke === false) {
-            delete result.strokeWidth;
-            delete result.strokeColor;
-        }
-        if(symbolizer.fill === false) {
-            delete result.fillColor;
-        }
-        OpenLayers.Util.extend(result, symbolizer);
-        return result;
-    },
-
-    CLASS_NAME: "OpenLayers.Renderer"
-});
-
 /**
- * Constant: OpenLayers.Renderer.defaultSymbolizer
- * {Object} Properties from this symbolizer will be applied to symbolizers
- *     with missing properties. This can also be used to set a global
- *     symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
- *     following code before rendering any vector features:
- * (code)
- * OpenLayers.Renderer.defaultSymbolizer = {
- *     fillColor: "#808080",
- *     fillOpacity: 1,
- *     strokeColor: "#000000",
- *     strokeOpacity: 1,
- *     strokeWidth: 1,
- *     pointRadius: 3,
- *     graphicName: "square"
- * };
- * (end)
+ * Function: urlAppend
+ * Appends a parameter string to a url. This function includes the logic for
+ * using the appropriate character (none, & or ?) to append to the url before
+ * appending the param string.
+ * 
+ * Parameters:
+ * url - {String} The url to append to
+ * paramStr - {String} The param string to append
+ * 
+ * Returns:
+ * {String} The new url
  */
-OpenLayers.Renderer.defaultSymbolizer = {
-    fillColor: "#000000",
-    strokeColor: "#000000",
-    strokeWidth: 2,
-    fillOpacity: 1,
-    strokeOpacity: 1,
-    pointRadius: 0
+OpenLayers.Util.urlAppend = function(url, paramStr) {
+    var newUrl = url;
+    if(paramStr) {
+        var parts = (url + " ").split(/[?&]/);
+        newUrl += (parts.pop() === " " ?
+            paramStr :
+            parts.length ? "&" + paramStr : "?" + paramStr);
+    }
+    return newUrl;
 };
-    
-/* ======================================================================
-    OpenLayers/Strategy.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * Class: OpenLayers.Strategy
- * Abstract vector layer strategy class.  Not to be instantiated directly.  Use
- *     one of the strategy subclasses instead.
+ * Property: ImgPath
+ * {String} Default is ''.
  */
-OpenLayers.Strategy = OpenLayers.Class({
-    
-    /**
-     * Property: layer
-     * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
-     */
-    layer: null,
-    
-    /**
-     * Property: options
-     * {Object} Any options sent to the constructor.
-     */
-    options: null,
+OpenLayers.ImgPath = '';
 
-    /** 
-     * Property: active 
-     * {Boolean} The control is active.
-     */
-    active: null,
+/** 
+ * Function: getImagesLocation
+ * 
+ * Returns:
+ * {String} The fully formatted image location string
+ */
+OpenLayers.Util.getImagesLocation = function() {
+    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
+};
 
-    /**
-     * Property: autoActivate
-     * {Boolean} The creator of the strategy can set autoActivate to false
-     *      to fully control when the protocol is activated and deactivated.
-     *      Defaults to true.
-     */
-    autoActivate: true,
 
-    /**
-     * Property: autoDestroy
-     * {Boolean} The creator of the strategy can set autoDestroy to false
-     *      to fully control when the strategy is destroyed. Defaults to
-     *      true.
-     */
-    autoDestroy: true,
-
-    /**
-     * Constructor: OpenLayers.Strategy
-     * Abstract class for vector strategies.  Create instances of a subclass.
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     */
-    initialize: function(options) {
-        OpenLayers.Util.extend(this, options);
-        this.options = options;
-        // set the active property here, so that user cannot override it
-        this.active = false;
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Clean up the strategy.
-     */
-    destroy: function() {
-        this.deactivate();
-        this.layer = null;
-        this.options = null;
-    },
-
-    /**
-     * Method: setLayer
-     * Called to set the <layer> property.
-     *
-     * Parameters:
-     * {<OpenLayers.Layer.Vector>}
-     */
-    setLayer: function(layer) {
-        this.layer = layer;
-    },
-    
-    /**
-     * Method: activate
-     * Activate the strategy.  Register any listeners, do appropriate setup.
-     *
-     * Returns:
-     * {Boolean} True if the strategy was successfully activated or false if
-     *      the strategy was already active.
-     */
-    activate: function() {
-        if (!this.active) {
-            this.active = true;
-            return true;
-        }
-        return false;
-    },
-    
-    /**
-     * Method: deactivate
-     * Deactivate the strategy.  Unregister any listeners, do appropriate
-     *     tear-down.
-     *
-     * Returns:
-     * {Boolean} True if the strategy was successfully deactivated or false if
-     *      the strategy was already inactive.
-     */
-    deactivate: function() {
-        if (this.active) {
-            this.active = false;
-            return true;
-        }
-        return false;
-    },
-   
-    CLASS_NAME: "OpenLayers.Strategy" 
-});
-/* ======================================================================
-    OpenLayers/Symbolizer.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * Class: OpenLayers.Symbolizer
- * Base class representing a symbolizer used for feature rendering.
+/** 
+ * Function: Try
+ * Execute functions until one of them doesn't throw an error. 
+ *     Capitalized because "try" is a reserved word in JavaScript.
+ *     Taken directly from OpenLayers.Util.Try()
+ * 
+ * Parameters:
+ * [*] - {Function} Any number of parameters may be passed to Try()
+ *    It will attempt to execute each of them until one of them 
+ *    successfully executes. 
+ *    If none executes successfully, returns null.
+ * 
+ * Returns:
+ * {*} The value returned by the first successfully executed function.
  */
-OpenLayers.Symbolizer = OpenLayers.Class({
-    
+OpenLayers.Util.Try = function() {
+    var returnValue = null;
 
-    /**
-     * APIProperty: zIndex
-     * {Number} The zIndex determines the rendering order for a symbolizer.
-     *     Symbolizers with larger zIndex values are rendered over symbolizers
-     *     with smaller zIndex values.  Default is 0.
-     */
-    zIndex: 0,
-    
-    /**
-     * Constructor: OpenLayers.Symbolizer
-     * Instances of this class are not useful.  See one of the subclasses.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Util.extend(this, config);
-    },
-    
-    /** 
-     * APIMethod: clone
-     * Create a copy of this symbolizer.
-     *
-     * Returns a symbolizer of the same type with the same properties.
-     */
-    clone: function() {
-        var Type = eval(this.CLASS_NAME);
-        return new Type(OpenLayers.Util.extend({}, this));
-    },
-    
-    CLASS_NAME: "OpenLayers.Symbolizer"
-    
-});
+    for (var i=0, len=arguments.length; i<len; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
 
-/* ======================================================================
-    OpenLayers/Control.js
-   ====================================================================== */
+    return returnValue;
+};
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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
+/** 
+ * Function: getNodes
+ * 
+ * These could/should be made namespace aware?
+ * 
+ * Parameters:
+ * p - {}
+ * tagName - {String}
+ * 
+ * Returns:
+ * {Array}
  */
+OpenLayers.Util.getNodes=function(p, tagName) {
+    var nodes = OpenLayers.Util.Try(
+        function () {
+            return OpenLayers.Util._getNodes(p.documentElement.childNodes,
+                                            tagName);
+        },
+        function () {
+            return OpenLayers.Util._getNodes(p.childNodes, tagName);
+        }
+    );
+    return nodes;
+};
 
 /**
- * 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.
+ * Function: _getNodes
  * 
- * Example:
- * The following example shows how to add many of the common controls
- * to a map.
+ * Parameters:
+ * nodes - {Array}
+ * tagName - {String}
  * 
- * > 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);
- * 
+ * Returns:
+ * {Array}
  */
-OpenLayers.Control = OpenLayers.Class({
+OpenLayers.Util._getNodes=function(nodes, tagName) {
+    var retArray = [];
+    for (var i=0, len=nodes.length; i<len; i++) {
+        if (nodes[i].nodeName==tagName) {
+            retArray.push(nodes[i]);
+        }
+    }
 
-    /** 
-     * Property: id 
-     * {String} 
-     */
-    id: null,
-    
-    /** 
-     * Property: map 
-     * {<OpenLayers.Map>} this gets set in the addControl() function in
-     * OpenLayers.Map 
-     */
-    map: null,
+    return retArray;
+};
 
-    /** 
-     * Property: div 
-     * {DOMElement} 
-     */
-    div: null,
 
-    /** 
-     * Property: type 
-     * {Number} Controls can have a 'type'. The type determines the type of
-     * interactions which are possible with them when they are placed in an
-     * <OpenLayers.Control.Panel>. 
-     */
-    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: "",
-
-    /**
-     * APIProperty: autoActivate
-     * {Boolean} Activate the control when it is added to a map.  Default is
-     *     false.
-     */
-    autoActivate: false,
-
-    /** 
-     * 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);
+/**
+ * Function: getTagText
+ * 
+ * Parameters:
+ * parent - {}
+ * item - {String}
+ * index - {Integer}
+ * 
+ * Returns:
+ * {String}
+ */
+OpenLayers.Util.getTagText = function (parent, item, index) {
+    var result = OpenLayers.Util.getNodes(parent, item);
+    if (result && (result.length > 0))
+    {
+        if (!index) {
+            index=0;
         }
-        if (this.id == null) {
-            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        if (result[index].childNodes.length > 1) {
+            return result.childNodes[1].nodeValue; 
         }
-    },
-
-    /**
-     * 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;
+        else if (result[index].childNodes.length == 1) {
+            return result[index].firstChild.nodeValue; 
         }
-        this.eventListeners = null;
+    } else { 
+        return ""; 
+    }
+};
 
-        // 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();
-                }
+/**
+ * Function: getXmlNodeValue
+ * 
+ * Parameters:
+ * node - {XMLNode}
+ * 
+ * Returns:
+ * {String} The text value of the given node, without breaking in firefox or IE
+ */
+OpenLayers.Util.getXmlNodeValue = function(node) {
+    var val = null;
+    OpenLayers.Util.Try( 
+        function() {
+            val = node.text;
+            if (!val) {
+                val = node.textContent;
             }
-            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 = OpenLayers.Function.False; 
-            }    
-            if (this.title != "") {
-                this.div.title = this.title;
+            if (!val) {
+                val = node.firstChild.nodeValue;
             }
-        }
-        if (px != null) {
-            this.position = px.clone();
-        }
-        this.moveTo(this.position);
-        return this.div;
-    },
+        }, 
+        function() {
+            val = node.textContent;
+        }); 
+    return val;
+};
 
-    /**
-     * 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";
-        }
-    },
+/** 
+ * Function: mouseLeft
+ * 
+ * Parameters:
+ * evt - {Event}
+ * div - {HTMLDivElement}
+ * 
+ * Returns:
+ * {Boolean}
+ */
+OpenLayers.Util.mouseLeft = function (evt, div) {
+    // start with the element to which the mouse has moved
+    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
+    // walk up the DOM tree.
+    while (target != div && target != null) {
+        target = target.parentNode;
+    }
+    // if the target we stop at isn't the div, then we've left the div.
+    return (target != div);
+};
 
-    /**
-     * 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;
-        if(this.map) {
-            OpenLayers.Element.addClass(
-                this.map.viewPortDiv,
-                this.displayClass.replace(/ /g, "") + "Active"
-            );
-        }
-        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;
-            if(this.map) {
-                OpenLayers.Element.removeClass(
-                    this.map.viewPortDiv,
-                    this.displayClass.replace(/ /g, "") + "Active"
-                );
-            }
-            this.events.triggerEvent("deactivate");
-            return true;
-        }
-        return false;
-    },
+/**
+ * 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;
 
-    CLASS_NAME: "OpenLayers.Control"
-});
-
 /**
- * Constant: OpenLayers.Control.TYPE_BUTTON
+ * 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.Control.TYPE_BUTTON = 1;
+OpenLayers.Util.toFloat = function (number, precision) {
+    if (precision == null) {
+        precision = OpenLayers.Util.DEFAULT_PRECISION;
+    }
+    if (typeof number !== "number") {
+        number = parseFloat(number);
+    }
+    return precision === 0 ? number :
+                             parseFloat(number.toPrecision(precision));
+};
 
 /**
- * Constant: OpenLayers.Control.TYPE_TOGGLE
+ * Function: rad
+ * 
+ * Parameters:
+ * x - {Float}
+ * 
+ * Returns:
+ * {Float}
  */
-OpenLayers.Control.TYPE_TOGGLE = 2;
+OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
 
 /**
- * Constant: OpenLayers.Control.TYPE_TOOL
+ * Function: deg
+ *
+ * Parameters:
+ * x - {Float}
+ *
+ * Returns:
+ * {Float}
  */
-OpenLayers.Control.TYPE_TOOL   = 3;
-/* ======================================================================
-    OpenLayers/Lang.js
-   ====================================================================== */
+OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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
+ * Property: VincentyConstants
+ * {Object} Constants for Vincenty functions.
  */
+OpenLayers.Util.VincentyConstants = {
+    a: 6378137,
+    b: 6356752.3142,
+    f: 1/298.257223563
+};
 
 /**
- * Namespace: OpenLayers.Lang
- * Internationalization namespace.  Contains dictionaries in various languages
- *     and methods to set and get the current language.
+ * APIFunction: distVincenty
+ * Given two objects representing points with geographic coordinates, this
+ *     calculates the distance between those points on the surface of an
+ *     ellipsoid.
+ *
+ * Parameters:
+ * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
+ * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
+ *
+ * Returns:
+ * {Float} The distance (in km) between the two input points as measured on an
+ *     ellipsoid.  Note that the input point objects must be in geographic
+ *     coordinates (decimal degrees) and the return distance is in kilometers.
  */
-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,
+OpenLayers.Util.distVincenty = function(p1, p2) {
+    var ct = OpenLayers.Util.VincentyConstants;
+    var a = ct.a, b = ct.b, f = ct.f;
 
-    /** 
-     * 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();
+    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
+    var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
+    var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
+    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+    var lambda = L, lambdaP = 2*Math.PI;
+    var iterLimit = 20;
+    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+        if (sinSigma==0) {
+            return 0;  // co-incident points
         }
-        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;
+        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+        var sigma = Math.atan2(sinSigma, cosSigma);
+        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+        lambdaP = lambda;
+        lambda = L + (1-C) * f * Math.sin(alpha) *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
     }
-    
+    if (iterLimit==0) {
+        return NaN;  // formula failed to converge
+    }
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+    var s = b*A*(sigma-deltaSigma);
+    var d = s.toFixed(3)/1000; // round to 1mm precision
+    return d;
 };
 
-
 /**
- * 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>.
+ * APIFunction: destinationVincenty
+ * Calculate destination point given start point lat/long (numeric degrees),
+ * bearing (numeric degrees) & distance (in m).
+ * Adapted from Chris Veness work, see
+ * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
  *
  * Parameters:
- * key - {String} The key for an i18n string value in the dictionary.
- * context - {Object} Optional context to be used with
- *     <OpenLayers.String.format>.
- * 
+ * lonlat  - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
+ *     properties) The start point.
+ * brng     - {Float} The bearing (degrees).
+ * distance - {Float} The ground distance (meters).
+ *
  * Returns:
- * {String} A internationalized string.
+ * {<OpenLayers.LonLat>} The destination point.
  */
-OpenLayers.i18n = OpenLayers.Lang.translate;
-/* ======================================================================
-    OpenLayers/Popup/Anchored.js
-   ====================================================================== */
+OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
+    var u = OpenLayers.Util;
+    var ct = u.VincentyConstants;
+    var a = ct.a, b = ct.b, f = ct.f;
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
+    var lon1 = lonlat.lon;
+    var lat1 = lonlat.lat;
 
+    var s = dist;
+    var alpha1 = u.rad(brng);
+    var sinAlpha1 = Math.sin(alpha1);
+    var cosAlpha1 = Math.cos(alpha1);
 
-/**
- * @requires OpenLayers/Popup.js
- */
+    var tanU1 = (1-f) * Math.tan(u.rad(lat1));
+    var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
+    var sigma1 = Math.atan2(tanU1, cosAlpha1);
+    var sinAlpha = cosU1 * sinAlpha1;
+    var cosSqAlpha = 1 - sinAlpha*sinAlpha;
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
 
-/**
- * Class: OpenLayers.Popup.Anchored
- * 
- * Inherits from:
- *  - <OpenLayers.Popup>
- */
-OpenLayers.Popup.Anchored = 
-  OpenLayers.Class(OpenLayers.Popup, {
+    var sigma = s / (b*A), sigmaP = 2*Math.PI;
+    while (Math.abs(sigma-sigmaP) > 1e-12) {
+        var cos2SigmaM = Math.cos(2*sigma1 + sigma);
+        var sinSigma = Math.sin(sigma);
+        var cosSigma = Math.cos(sigma);
+        var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+            B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+        sigmaP = sigma;
+        sigma = s / (b*A) + deltaSigma;
+    }
 
-    /** 
-     * 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,
+    var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
+    var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
+        (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
+    var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
+    var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+    var L = lambda - (1-C) * f * sinAlpha *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
 
-    /**
-     * Parameter: anchor
-     * {Object} Object to which we'll anchor the popup. Must expose a 
-     *     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
-     */
-    anchor: null,
+    var revAz = Math.atan2(sinAlpha, -tmp);  // final bearing
 
-    /** 
-    * 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);
+    return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
+};
 
-        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();
-        }
-    },
-
-    /**
-     * 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) : this.anchor.size.h;
-        
-        var left = (this.relativePosition.charAt(1) == 'l');
-        newPx.x += (left) ? -(size.w + this.anchor.size.w) : this.anchor.size.w;
-
-        return newPx;   
-    },
-
-    CLASS_NAME: "OpenLayers.Popup.Anchored"
-});
-/* ======================================================================
-    OpenLayers/Renderer/Canvas.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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
+ * Function: getParameters
+ * Parse the parameters from a URL or from the current page itself into a 
+ *     JavaScript Object. Note that parameter values with commas are separated
+ *     out into an Array.
  * 
- * Inherits:
- *  - <OpenLayers.Renderer>
+ * Parameters:
+ * url - {String} Optional url used to extract the query string.
+ *                If null, query string is taken from page location.
+ * 
+ * Returns:
+ * {Object} An object of key/value pairs from the query string.
  */
-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
+OpenLayers.Util.getParameters = function(url) {
+    // if no url specified, take it from the location bar
+    url = url || window.location.href;
 
-    /**
-     * 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, 
-   
-    /**
-     * 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 = {};
-    },
-    
-    /** 
-     * 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>}
-     * featureId - {String}
-     */
-    eraseGeometry: function(geometry, featureId) {
-        this.eraseFeatures(this.features[featureId][0]);
-    },
+    //parse out parameters portion of url string
+    var paramsString = "";
+    if (OpenLayers.String.contains(url, '?')) {
+        var start = url.indexOf('?') + 1;
+        var end = OpenLayers.String.contains(url, "#") ?
+                    url.indexOf('#') : url.length;
+        paramsString = url.substring(start, end);
+    }
 
-    /**
-     * 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) {
-        style = style || feature.style;
-        style = this.applyDefaultSymbolizer(style);  
-        
-        this.features[feature.id] = [feature, style]; 
-        this.redraw();
-    },
+    var parameters = {};
+    var pairs = paramsString.split(/[&;]/);
+    for(var i=0, len=pairs.length; i<len; ++i) {
+        var keyValue = pairs[i].split('=');
+        if (keyValue[0]) {
 
-
-    /** 
-     * Method: drawGeometry
-     * Used when looping (in redraw) over the features; draws
-     * the canvas. 
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} 
-     * style - {Object} 
-     */
-    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);
+            var key = keyValue[0];
+            try {
+                key = decodeURIComponent(key);
+            } catch (err) {
+                key = unescape(key);
             }
-            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;
-        }
-    },
-
-    /**
-     * Method: drawExternalGraphic
-     * Called to draw External graphics. 
-     * 
-     * Parameters: 
-     * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
-     */ 
-    drawExternalGraphic: function(pt, style) {
-       var img = new Image();
-       
-       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 context = { img: img, 
-                       x: (pt[0]+xOffset), 
-                       y: (pt[1]+yOffset), 
-                       width: width, 
-                       height: height, 
-                       opacity: style.graphicOpacity || style.fillOpacity,
-                       canvas: this.canvas };
-
-       img.onload = OpenLayers.Function.bind( function() {
-           this.canvas.globalAlpha = this.opacity;
-           this.canvas.drawImage(this.img, this.x, 
-                                 this.y, this.width, this.height);
-       }, context);
-       img.src = style.externalGraphic;
-    },
-
-    /**
-     * 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) {
-        if(style.graphic !== false) {
-            var pt = this.getLocalXY(geometry);
             
-            if (style.externalGraphic) {
-                this.drawExternalGraphic(pt, style);
-            } else {
-                if(style.fill !== false) {
-                    this.setCanvasStyle("fill", style);
-                    this.canvas.beginPath();
-                    this.canvas.arc(pt[0], pt[1], style.pointRadius, 0, Math.PI*2, true);
-                    this.canvas.fill();
-                }
-                
-                if(style.stroke !== false) {
-                    this.setCanvasStyle("stroke", style);
-                    this.canvas.beginPath();
-                    this.canvas.arc(pt[0], pt[1], style.pointRadius, 0, Math.PI*2, true);
-                    this.canvas.stroke();
-                    this.setCanvasStyle("reset");
-                }
-            }
-        }
-    },
+            // being liberal by replacing "+" with " "
+            var value = (keyValue[1] || '').replace(/\+/g, " ");
 
-    /**
-     * Method: drawLineString
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
-     */ 
-    drawLineString: function(geometry, style) {
-        if(style.stroke !== false) {
-            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]);
+            try {
+                value = decodeURIComponent(value);
+            } catch (err) {
+                value = unescape(value);
             }
-            this.canvas.stroke();
-        }
-        this.setCanvasStyle("reset");
-    },    
-    
-    /**
-     * Method: drawLinearRing
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * geometry - {<OpenLayers.Geometry>}
-     * style    - {Object}
-     */ 
-    drawLinearRing: function(geometry, style) {
-        if(style.fill !== false) {
-            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();
-        }
-        
-        if(style.stroke !== false) {
-            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:
-     * location - {<OpenLayers.Point>}
-     * style    - {Object}
-     */
-    drawText: function(location, style) {
-        style = OpenLayers.Util.extend({
-            fontColor: "#000000",
-            labelAlign: "cm"
-        }, style);
-        var pt = this.getLocalXY(location);
-        
-        this.setCanvasStyle("reset");
-        this.canvas.fillStyle = style.fontColor;
-        this.canvas.globalAlpha = style.fontOpacity || 1.0;
-        var fontStyle = style.fontWeight + " " + style.fontSize + " " + style.fontFamily;
-        if (this.canvas.fillText) {
-            // HTML5
-            var labelAlign =
-                OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
-                "center";
-            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(style.labelAlign[0]) {
-                case "l":
-                    break;
-                case "r":
-                    pt[0] -= len;
-                    break;
-                case "c":
-                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");
-    },
+            // follow OGC convention of comma delimited values
+            value = value.split(",")
 
-    /**
-     * 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];
-    },
+            //if there's only one value, do not return as array                    
+            if (value.length == 1) {
+                value = value[0];
+            }                
+            
+            parameters[key] = value;
+         }
+     }
+    return parameters;
+};
 
-    /**
-     * Method: clear
-     * Clear all vectors from the renderer.
-     */    
-    clear: function() {
-        this.canvas.clearRect(0, 0, this.root.width, this.root.height);
-        this.features = {};
-    },
-
-    /**
-     * Method: getFeatureIdFromEvent
-     * Returns a feature id from an event on the renderer.  
-     * 
-     * Parameters:
-     * evt - {<OpenLayers.Event>} 
-     *
-     * Returns:
-     * {String} A feature id or null.
-     */
-    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;
-    },
-    
-    /**
-     * 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.canvas.clearRect(0, 0, this.root.width, this.root.height);
-            var labelMap = [];
-            var feature, style;
-            for (var id in this.features) {
-                if (!this.features.hasOwnProperty(id)) { continue; }
-                feature = this.features[id][0];
-                style = this.features[id][1];
-                if (!feature.geometry) { continue; }
-                this.drawGeometry(feature.geometry, style);
-                if(style.label) {
-                    labelMap.push([feature, style]);
-                }
-            }
-            var item;
-            for (var i=0, len=labelMap.length; i<len; ++i) {
-                item = labelMap[i];
-                this.drawText(item[0].geometry.getCentroid(), item[1]);
-            }
-        }    
-    },
-
-    CLASS_NAME: "OpenLayers.Renderer.Canvas"
-});
-
 /**
- * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
- * {Object}
+ * Function: getArgs
+ * *Deprecated*.  Will be removed in 3.0.  Please use instead
+ *     <OpenLayers.Util.getParameters>
+ * 
+ * Parameters:
+ * url - {String} Optional url used to extract the query string.
+ *                If null, query string is taken from page location.
+ * 
+ * Returns:
+ * {Object} An object of key/value pairs from the query string.
  */
-OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
-    "l": "left",
-    "r": "right"
+OpenLayers.Util.getArgs = function(url) {
+    OpenLayers.Console.warn(
+        OpenLayers.i18n(
+            "methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
+        )
+    );
+    return OpenLayers.Util.getParameters(url);
 };
-/* ======================================================================
-    OpenLayers/Renderer/Elements.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
+/**
+ * Property: lastSeqID
+ * {Integer} The ever-incrementing count variable.
+ *           Used for generating unique ids.
+ */
+OpenLayers.Util.lastSeqID = 0;
 
 /**
- * @requires OpenLayers/Renderer.js
+ * Function: createUniqueID
+ * Create a unique identifier for this session.  Each time this function
+ *     is called, a counter is incremented.  The return will be the optional
+ *     prefix (defaults to "id_") appended with the counter value.
+ * 
+ * Parameters:
+ * prefix {String} Optionsal string to prefix unique id. Default is "id_".
+ * 
+ * Returns:
+ * {String} A unique id string, built on the passed in prefix.
  */
+OpenLayers.Util.createUniqueID = function(prefix) {
+    if (prefix == null) {
+        prefix = "id_";
+    }
+    OpenLayers.Util.lastSeqID += 1; 
+    return prefix + OpenLayers.Util.lastSeqID;        
+};
 
 /**
- * Class: OpenLayers.ElementsIndexer
- * This class takes care of figuring out which order elements should be
- *     placed in the DOM based on given indexing methods. 
+ * Constant: INCHES_PER_UNIT
+ * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
+ * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
+ * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
+ * and PROJ.4 (http://trac.osgeo.org/proj/)
+ * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
+ * The hardcoded table of PROJ.4 units are in pj_units.c.
  */
-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) {
+OpenLayers.INCHES_PER_UNIT = { 
+    'inches': 1.0,
+    'ft': 12.0,
+    'mi': 63360.0,
+    'm': 39.3701,
+    'km': 39370.1,
+    'dd': 4374754,
+    'yd': 36
+};
+OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
+OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
+OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
 
-        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);       
+// Units from CS-Map
+OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
+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
+});
 
-        var leftIndex = -1;
-        var rightIndex = this.order.length;
-        var middle;
+//unit abbreviations supported by PROJ.4
+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
+});
 
-        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.
-        return this.getNextElement(rightIndex);
-    },
-    
-    /**
-     * 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:
-     * 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);
-    },
+/** 
+ * Constant: DOTS_PER_INCH
+ * {Integer} 72 (A sensible default)
+ */
+OpenLayers.DOTS_PER_INCH = 72;
 
-    /**
-     * 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:
-     * {Integer} The z-index value for the specified node (from the node 
-     *     data itself).
-     */
-    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;
-        }
-    },
+/**
+ * Function: normalizeScale
+ * 
+ * Parameters:
+ * scale - {float}
+ * 
+ * Returns:
+ * {Float} A normalized scale value, in 1 / X format. 
+ *         This means that if a value less than one ( already 1/x) is passed
+ *         in, it just returns scale directly. Otherwise, it returns 
+ *         1 / scale
+ */
+OpenLayers.Util.normalizeScale = function (scale) {
+    var normScale = (scale > 1.0) ? (1.0 / scale) 
+                                  : scale;
+    return normScale;
+};
 
-    /**
-     * APIMethod: getNextElement
-     * Get the next element in the order stack.
-     * 
-     * Parameters:
-     * index - {Integer} The index of the current node in this.order.
-     * 
-     * Returns:
-     * {DOMElement} the node following the index passed in, or
-     *     null.
-     */
-    getNextElement: function(index) {
-        var nextIndex = index + 1;
-        if (nextIndex < this.order.length) {
-            var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
-            if (nextElement == undefined) {
-                nextElement = this.getNextElement(nextIndex);
-            }
-            return nextElement;
-        } else {
-            return null;
-        } 
-    },
-    
-    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.
+ * Function: getResolutionFromScale
+ * 
+ * Parameters:
+ * scale - {Float}
+ * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                  Default is degrees
+ * 
+ * Returns:
+ * {Float} The corresponding resolution given passed-in scale and unit 
+ *     parameters.  If the given scale is falsey, the returned resolution will
+ *     be undefined.
  */
-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.
-     * 
-     * Parameters:
-     * indexer - {<OpenLayers.ElementsIndexer>}
-     * newNode - {DOMElement}
-     * nextNode - {DOMElement}
-     * 
-     * Returns:
-     * {Integer}
-     */
-    Z_ORDER: function(indexer, newNode, nextNode) {
-        var newZIndex = indexer.getZIndex(newNode);
-
-        var returnVal = 0;
-        if (nextNode) {
-            var nextZIndex = indexer.getZIndex(nextNode);
-            returnVal = newZIndex - nextZIndex; 
+OpenLayers.Util.getResolutionFromScale = function (scale, units) {
+    var resolution;
+    if (scale) {
+        if (units == null) {
+            units = "degrees";
         }
-        
-        return returnVal;
-    },
+        var normScale = OpenLayers.Util.normalizeScale(scale);
+        resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
+                                        * OpenLayers.DOTS_PER_INCH);        
+    }
+    return resolution;
+};
 
-    /**
-     * 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;
-    },
+/**
+ * Function: getScaleFromResolution
+ * 
+ * Parameters:
+ * resolution - {Float}
+ * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
+ *                  Default is degrees
+ * 
+ * Returns:
+ * {Float} The corresponding scale given passed-in resolution and unit 
+ *         parameters.
+ */
+OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
 
-    /**
-     * 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 result = nextNode._boundsBottom - newNode._boundsBottom;
-            returnVal = (result === 0) ? 1 : result;
-        }
-        
-        return returnVal;       
+    if (units == null) {
+        units = "degrees";
     }
+
+    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
+                    OpenLayers.DOTS_PER_INCH;
+    return scale;
 };
 
 /**
- * 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. 
+ * Function: safeStopPropagation
+ * *Deprecated*. This function has been deprecated. Please use directly 
+ *     <OpenLayers.Event.stop> passing 'true' as the 2nd 
+ *     argument (preventDefault)
  * 
- * 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. 
+ * Safely stop the propagation of an event *without* preventing
+ *   the default browser action from occurring.
  * 
- * Inherits:
- *  - <OpenLayers.Renderer>
+ * Parameter:
+ * evt - {Event}
  */
-OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
+OpenLayers.Util.safeStopPropagation = function(evt) {
+    OpenLayers.Event.stop(evt, true);
+};
 
-    /**
-     * Property: rendererRoot
-     * {DOMElement}
-     */
-    rendererRoot: null,
-    
-    /**
-     * Property: root
-     * {DOMElement}
-     */
-    root: null,
-    
-    /**
-     * Property: vectorRoot
-     * {DOMElement}
-     */
-    vectorRoot: null,
+/**
+ * Function: pagePositon
+ * Calculates the position of an element on the page (see
+ * http://code.google.com/p/doctype/wiki/ArticlePageOffset)
+ *
+ * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
+ * Copyright (c) 2006, Yahoo! Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use of this software in source and binary forms, with or
+ * without modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * 
+ * * 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.
+ * 
+ * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission of Yahoo! Inc.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Parameters:
+ * forElement - {DOMElement}
+ * 
+ * Returns:
+ * {Array} two item array, Left value then Top value.
+ */
+OpenLayers.Util.pagePosition =  function(forElement) {
+    // NOTE: If element is hidden (display none or disconnected or any the
+    // ancestors are hidden) we get (0,0) by default but we still do the
+    // accumulation of scroll position.
 
-    /**
-     * Property: textRoot
-     * {DOMElement}
-     */
-    textRoot: null,
+    var pos = [0, 0];
+    var viewportElement = OpenLayers.Util.getViewportElement();
+    if (!forElement || forElement == window || forElement == viewportElement) {
+        // viewport is always at 0,0 as that defined the coordinate system for
+        // this function - this avoids special case checks in the code below
+        return pos;
+    }
 
-    /**
-     * 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: BACKGROUND_ID_SUFFIX
-     * {String}
-     */
-    LABEL_ID_SUFFIX: "_label",
-    
-    /**
-     * 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);
+    // Gecko browsers normally use getBoxObjectFor to calculate the position.
+    // When invoked for an element with an implicit absolute position though it
+    // can be off by one. Therefore the recursive implementation is used in
+    // those (relatively rare) cases.
+    var BUGGY_GECKO_BOX_OBJECT =
+        OpenLayers.IS_GECKO && document.getBoxObjectFor &&
+        OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
+        (forElement.style.top == '' || forElement.style.left == '');
 
-        this.rendererRoot = this.createRenderRoot();
-        this.root = this.createRoot("_root");
-        this.vectorRoot = this.createRoot("_vroot");
-        this.textRoot = this.createRoot("_troot");
-        
-        this.root.appendChild(this.vectorRoot);
-        this.root.appendChild(this.textRoot);
-        
-        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() {
+    var parent = null;
+    var box;
 
-        this.clear(); 
+    if (forElement.getBoundingClientRect) { // IE
+        box = forElement.getBoundingClientRect();
+        var scrollTop = viewportElement.scrollTop;
+        var scrollLeft = viewportElement.scrollLeft;
 
-        this.rendererRoot = null;
-        this.root = null;
-        this.xmlns = null;
+        pos[0] = box.left + scrollLeft;
+        pos[1] = box.top + scrollTop;
 
-        OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
-    },
-    
-    /**
-     * Method: clear
-     * Remove all the elements from the root
-     */    
-    clear: function() {
-        var child;
-        var root = this.vectorRoot;
-        if (root) {
-            while (child = root.firstChild) {
-                root.removeChild(child);
-            }
-        }
-        root = this.textRoot;
-        if (root) {
-            while (child = root.firstChild) {
-                root.removeChild(child);
-            }
-        }
-        if (this.indexer) {
-            this.indexer.clear();
-        }
-    },
+    } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
+        // Gecko ignores the scroll values for ancestors, up to 1.9.  See:
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
 
-    /** 
-     * 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) { },
+        box = document.getBoxObjectFor(forElement);
+        var vpBox = document.getBoxObjectFor(viewportElement);
+        pos[0] = box.screenX - vpBox.screenX;
+        pos[1] = box.screenY - vpBox.screenY;
 
-    /** 
-     * 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:
-     * 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;
+    } else { // safari/opera
+        pos[0] = forElement.offsetLeft;
+        pos[1] = forElement.offsetTop;
+        parent = forElement.offsetParent;
+        if (parent != forElement) {
+            while (parent) {
+                pos[0] += parent.offsetLeft;
+                pos[1] += parent.offsetTop;
+                parent = parent.offsetParent;
             }
-            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);
         }
-        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));
-                }
-                node.parentNode.removeChild(node);
-            }
-        }
-        return rendered;
-    },
-    
-    /**
-     * 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) {
-        style = this.applyDefaultSymbolizer(style);
-        // 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._boundsBottom = geometry.getBounds().bottom;
-        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.vectorRoot.insertBefore(node, insert);
-            } else {
-                this.vectorRoot.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.vectorRoot){ 
-                this.vectorRoot.appendChild(node);
-            }
-        }
-        
-        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
-        );
-    },
+        var browser = OpenLayers.BROWSER_NAME;
 
-    /**
-     * 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:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 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)
-     */
-    drawGeometryNode: function(node, geometry, style) {
-        style = style || node._style;
-
-        var options = {
-            'isFilled': style.fill === undefined ?
-                true :
-                style.fill,
-            'isStroked': style.stroke === undefined ?
-                !!style.strokeWidth :
-                style.stroke
-        };
-        var drawn;
-        switch (geometry.CLASS_NAME) {
-            case "OpenLayers.Geometry.Point":
-                if(style.graphic === false) {
-                    options.isFilled = false;
-                    options.isStroked = false;
-                }
-                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;
+        // opera & (safari absolute) incorrectly account for body offsetTop
+        if (browser == "opera" || (browser == "safari" &&
+              OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
+            pos[1] -= document.body.offsetTop;
         }
 
-        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: 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: removeText
-     * Removes a label
-     * 
-     * Parameters:
-     * featureId - {String}
-     */
-    removeText: function(featureId) {
-        var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
-        if (label) {
-            this.textRoot.removeChild(label);
-        }
-    },
-
-    /**
-     * 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:
-     * geometry - {<OpenLayers.Geometry>}
-     * featureId - {String}
-     */
-    eraseGeometry: function(geometry, featureId) {
-        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], featureId);
+        // accumulate the scroll positions for everything but the body element
+        parent = forElement.offsetParent;
+        while (parent && parent != document.body) {
+            pos[0] -= parent.scrollLeft;
+            // see https://bugs.opera.com/show_bug.cgi?id=249965
+            if (browser != "opera" || parent.tagName != 'TR') {
+                pos[1] -= parent.scrollTop;
             }
-        } 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);
-                    }
-                }
-            }
+            parent = parent.offsetParent;
         }
-    },
-
-    /** 
-     * 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:
-     * {DOMElement} A new node of the given type and id.
-     */
-    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) {},
+    return pos;
+};
 
-    /**
-     * 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.
+ * Function: getViewportElement
+ * Returns die viewport element of the document. The viewport element is
+ * usually document.documentElement, except in IE,where it is either
+ * document.body or document.documentElement, depending on the document's
+ * compatibility mode (see
+ * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
  */
-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.Util.getViewportElement = function() {
+    var viewportElement = arguments.callee.viewportElement;
+    if (viewportElement == undefined) {
+        viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
+            document.compatMode != 'CSS1Compat') ? document.body :
+            document.documentElement;
+        arguments.callee.viewportElement = viewportElement;
+    }
+    return viewportElement;
 };
-/* ======================================================================
-    OpenLayers/Strategy/Fixed.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Strategy.js
- */
-
-/**
- * Class: OpenLayers.Strategy.Fixed
- * A simple strategy that requests features once and never requests new data.
+/** 
+ * Function: isEquivalentUrl
+ * Test two URLs for equivalence. 
+ * 
+ * Setting 'ignoreCase' allows for case-independent comparison.
+ * 
+ * Comparison is based on: 
+ *  - Protocol
+ *  - Host (evaluated without the port)
+ *  - Port (set 'ignorePort80' to ignore "80" values)
+ *  - Hash ( set 'ignoreHash' to disable)
+ *  - Pathname (for relative <-> absolute comparison) 
+ *  - Arguments (so they can be out of order)
+ *  
+ * Parameters:
+ * url1 - {String}
+ * url2 - {String}
+ * options - {Object} Allows for customization of comparison:
+ *                    'ignoreCase' - Default is True
+ *                    'ignorePort80' - Default is True
+ *                    'ignoreHash' - Default is True
  *
- * Inherits from:
- *  - <OpenLayers.Strategy>
+ * Returns:
+ * {Boolean} Whether or not the two URLs are equivalent
  */
-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
-    
-    /**
-     * APIProperty: preload
-     * {Boolean} Load data before layer made visible. Enabling this may result
-     *   in considerable overhead if your application loads many data layers
-     *   that are not visible by default. Default is false.
-     */
-    preload: false,
+OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
+    options = options || {};
 
-    /**
-     * Constructor: OpenLayers.Strategy.Fixed
-     * Create a new Fixed strategy.
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
+    OpenLayers.Util.applyDefaults(options, {
+        ignoreCase: true,
+        ignorePort80: true,
+        ignoreHash: true
+    });
 
-    /**
-     * APIMethod: destroy
-     * Clean up the strategy.
-     */
-    destroy: function() {
-        OpenLayers.Strategy.prototype.destroy.apply(this, arguments);
-    },
+    var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
+    var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
 
-    /**
-     * Method: activate
-     * Activate the strategy: load data or add listener to load when visible
-     *
-     * Returns:
-     * {Boolean} True if the strategy was successfully activated or false if
-     *      the strategy was already active.
-     */
-    activate: function() {
-        if(OpenLayers.Strategy.prototype.activate.apply(this, arguments)) {
-            this.layer.events.on({
-                "refresh": this.load,
-                scope: this
-            });
-            if(this.layer.visibility == true || this.preload) {
-                this.load();
-            } else {
-                this.layer.events.on({
-                    "visibilitychanged": this.load,
-                    scope: this
-                });
+    //compare all keys except for "args" (treated below)
+    for(var key in urlObj1) {
+        if(key !== "args") {
+            if(urlObj1[key] != urlObj2[key]) {
+                return false;
             }
-            return true;
         }
+    }
+
+    // compare search args - irrespective of order
+    for(var key in urlObj1.args) {
+        if(urlObj1.args[key] != urlObj2.args[key]) {
+            return false;
+        }
+        delete urlObj2.args[key];
+    }
+    // urlObj2 shouldn't have any args left
+    for(var key in urlObj2.args) {
         return false;
-    },
+    }
     
-    /**
-     * Method: deactivate
-     * Deactivate the strategy.  Undo what is done in <activate>.
-     * 
-     * Returns:
-     * {Boolean} The strategy was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
-        if(deactivated) {
-            this.layer.events.un({
-                "refresh": this.load,
-                "visibilitychanged": this.load,
-                scope: this
-            });
-        }
-        return deactivated;
-    },
+    return true;
+};
 
-    /**
-     * Method: load
-     * Tells protocol to load data and unhooks the visibilitychanged event
-     *
-     * Parameters:
-     * options - {Object} options to pass to protocol read.
-     */
-    load: function(options) {
-        this.layer.events.triggerEvent("loadstart");
-        this.layer.protocol.read(OpenLayers.Util.applyDefaults({
-            callback: this.merge,
-            filter: this.layer.filter,
-            scope: this
-        }, options));
-        this.layer.events.un({
-            "visibilitychanged": this.load,
-            scope: this
-        });
-    },
-
-    /**
-     * Method: merge
-     * Add all features to the layer.
-     */
-    merge: function(resp) {
-        this.layer.destroyFeatures();
-        var features = resp.features;
-        if (features && features.length > 0) {
-            var remote = this.layer.projection;
-            var local = this.layer.map.getProjectionObject();
-            if(!local.equals(remote)) {
-                var geom;
-                for(var i=0, len=features.length; i<len; ++i) {
-                    geom = features[i].geometry;
-                    if(geom) {
-                        geom.transform(remote, local);
-                    }
-                }
-            }
-            this.layer.addFeatures(features);
-        }
-        this.layer.events.triggerEvent("loadend");
-    },
-
-    CLASS_NAME: "OpenLayers.Strategy.Fixed"
-});
-/* ======================================================================
-    OpenLayers/Symbolizer/Line.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Symbolizer.js
+ * Function: createUrlObject
+ * 
+ * Parameters:
+ * url - {String}
+ * options - {Object} A hash of options.  Can be one of:
+ *            ignoreCase: lowercase url,
+ *            ignorePort80: don't include explicit port if port is 80,
+ *            ignoreHash: Don't include part of url after the hash (#).
+ * 
+ * Returns:
+ * {Object} An object with separate url, a, port, host, and args parsed out 
+ *          and ready for comparison
  */
+OpenLayers.Util.createUrlObject = function(url, options) {
+    options = options || {};
 
-/**
- * Class: OpenLayers.Symbolizer.Line
- * A symbolizer used to render line features.
- */
-OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {
+    // deal with relative urls first
+    if(!(/^\w+:\/\//).test(url)) {
+        var loc = window.location;
+        var port = loc.port ? ":" + loc.port : "";
+        var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
+        if(url.indexOf("/") === 0) {
+            // full pathname
+            url = fullUrl + url;
+        } else {
+            // relative to current path
+            var parts = loc.pathname.split("/");
+            parts.pop();
+            url = fullUrl + parts.join("/") + "/" + url;
+        }
+    }
+  
+    if (options.ignoreCase) {
+        url = url.toLowerCase(); 
+    }
 
-    /**
-     * APIProperty: strokeColor
-     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
-     *     for red).
-     */
-    strokeColor: null,
+    var a = document.createElement('a');
+    a.href = url;
     
-    /**
-     * APIProperty: strokeOpacity
-     * {Number} Stroke opacity (0-1).
-     */
-    strokeOpacity: null,
+    var urlObject = {};
     
-    /**
-     * APIProperty: strokeWidth
-     * {Number} Pixel stroke width.
-     */
-    strokeWidth: null,
-    
-    /**
-     * APIProperty: strokeLinecap
-     * {String} Stroke cap type ("butt", "round", or "square").
-     */
-    strokeLinecap: null,
-    
-    /**
-     * Property: strokeDashstyle
-     * {String} Stroke dash style according to the SLD spec. Note that the
-     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
-     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
-     *     most SLD patterns will render correctly in OpenLayers.
-     */
-    strokeDashstyle: null,
+    //host (without port)
+    urlObject.host = a.host.split(":").shift();
 
-    /**
-     * Constructor: OpenLayers.Symbolizer.Line
-     * Create a symbolizer for rendering lines.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new line symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
-    },
-    
-    CLASS_NAME: "OpenLayers.Symbolizer.Line"
-    
-});
+    //protocol
+    urlObject.protocol = a.protocol;  
 
-/* ======================================================================
-    OpenLayers/Symbolizer/Point.js
-   ====================================================================== */
+    //port (get uniform browser behavior with port 80 here)
+    if(options.ignorePort80) {
+        urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
+    } else {
+        urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
+    }
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Symbolizer.js
- */
-
-/**
- * Class: OpenLayers.Symbolizer.Point
- * A symbolizer used to render point features.
- */
-OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {
+    //hash
+    urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;  
     
-    /**
-     * APIProperty: strokeColor
-     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
-     *     for red).
-     */
-    strokeColor: null,
-    
-    /**
-     * APIProperty: strokeOpacity
-     * {Number} Stroke opacity (0-1).
-     */
-    strokeOpacity: null,
-    
-    /**
-     * APIProperty: strokeWidth
-     * {Number} Pixel stroke width.
-     */
-    strokeWidth: null,
-    
-    /**
-     * APIProperty: strokeLinecap
-     * {String} Stroke cap type ("butt", "round", or "square").
-     */
-    strokeLinecap: null,
-    
-    /**
-     * Property: strokeDashstyle
-     * {String} Stroke dash style according to the SLD spec. Note that the
-     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
-     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
-     *     most SLD patterns will render correctly in OpenLayers.
-     */
-    strokeDashstyle: null,
+    //args
+    var queryString = a.search;
+    if (!queryString) {
+        var qMark = url.indexOf("?");
+        queryString = (qMark != -1) ? url.substr(qMark) : "";
+    }
+    urlObject.args = OpenLayers.Util.getParameters(queryString);
 
-    /**
-     * APIProperty: fillColor
-     * {String} RGB hex fill color (e.g. "#ff0000" for red).
-     */
-    fillColor: null,
+    //pathname (uniform browser behavior with leading "/")
+    urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
     
-    /**
-     * APIProperty: fillOpacity
-     * {Number} Fill opacity (0-1).
-     */
-    fillOpacity: null, 
-
-    /**
-     * APIProperty: pointRadius
-     * {Number} Pixel point radius.
-     */
-    pointRadius: null,
-
-    /**
-     * APIProperty: externalGraphic
-     * {String} Url to an external graphic that will be used for rendering 
-     *     points.
-     */
-    externalGraphic: null,
-    
-    /**
-     * APIProperty: graphicWidth
-     * {Number} Pixel width for sizing an external graphic.
-     */
-    graphicWidth: null,
-    
-    /**
-     * APIProperty: graphicHeight
-     * {Number} Pixel height for sizing an external graphic.
-     */
-    graphicHeight: null,
-    
-    /**
-     * APIProperty: graphicOpacity
-     * {Number} Opacity (0-1) for an external graphic.
-     */
-    graphicOpacity: null,
-    
-    /**
-     * APIProperty: graphicXOffset
-     * {Number} Pixel offset along the positive x axis for displacing an 
-     *     external graphic.
-     */
-    graphicXOffset: null,
-    
-    /**
-     * APIProperty: graphicYOffset
-     * {Number} Pixel offset along the positive y axis for displacing an 
-     *     external graphic.
-     */
-    graphicYOffset: null,
-
-    /**
-     * APIProperty: rotation
-     * {Number} The rotation of a graphic in the clockwise direction about its 
-     *     center point (or any point off center as specified by 
-     *     <graphicXOffset> and <graphicYOffset>).
-     */
-    rotation: null,
-    
-    /**
-     * APIProperty: graphicName
-     * {String} Named graphic to use when rendering points.  Supported values 
-     *     include "circle", "square", "star", "x", "cross", and "triangle".
-     */
-    graphicName: null,
-    
-    /**
-     * Constructor: OpenLayers.Symbolizer.Point
-     * Create a symbolizer for rendering points.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new point symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
-    },
-    
-    CLASS_NAME: "OpenLayers.Symbolizer.Point"
-    
-});
-
-/* ======================================================================
-    OpenLayers/Symbolizer/Polygon.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
+    return urlObject; 
+};
+ 
 /**
- * @requires OpenLayers/Symbolizer.js
+ * Function: removeTail
+ * Takes a url and removes everything after the ? and #
+ * 
+ * Parameters:
+ * url - {String} The url to process
+ * 
+ * Returns:
+ * {String} The string with all queryString and Hash removed
  */
-
-/**
- * Class: OpenLayers.Symbolizer.Polygon
- * A symbolizer used to render line features.
- */
-OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {
+OpenLayers.Util.removeTail = function(url) {
+    var head = null;
     
-    /**
-     * APIProperty: strokeColor
-     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
-     *     for red).
-     */
-    strokeColor: null,
-    
-    /**
-     * APIProperty: strokeOpacity
-     * {Number} Stroke opacity (0-1).
-     */
-    strokeOpacity: null,
-    
-    /**
-     * APIProperty: strokeWidth
-     * {Number} Pixel stroke width.
-     */
-    strokeWidth: null,
-    
-    /**
-     * APIProperty: strokeLinecap
-     * {String} Stroke cap type ("butt", "round", or "square").
-     */
-    strokeLinecap: null,
-    
-    /**
-     * Property: strokeDashstyle
-     * {String} Stroke dash style according to the SLD spec. Note that the
-     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
-     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
-     *     most SLD patterns will render correctly in OpenLayers.
-     */
-    strokeDashstyle: null,
+    var qMark = url.indexOf("?");
+    var hashMark = url.indexOf("#");
 
-    /**
-     * APIProperty: fillColor
-     * {String} RGB hex fill color (e.g. "#ff0000" for red).
-     */
-    fillColor: null,
-    
-    /**
-     * APIProperty: fillOpacity
-     * {Number} Fill opacity (0-1).
-     */
-    fillOpacity: null, 
+    if (qMark == -1) {
+        head = (hashMark != -1) ? url.substr(0,hashMark) : url;
+    } else {
+        head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark)) 
+                                  : url.substr(0, qMark);
+    }
+    return head;
+};
 
-    /**
-     * Constructor: OpenLayers.Symbolizer.Polygon
-     * Create a symbolizer for rendering polygons.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new polygon symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
-    },
-    
-    CLASS_NAME: "OpenLayers.Symbolizer.Polygon"
-    
-});
-
-/* ======================================================================
-    OpenLayers/Symbolizer/Raster.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Symbolizer.js
+ * Constant: IS_GECKO
+ * {Boolean} True if the userAgent reports the browser to use the Gecko engine
  */
+OpenLayers.IS_GECKO = (function() {
+    var ua = navigator.userAgent.toLowerCase();
+    return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
+})();
 
 /**
- * Class: OpenLayers.Symbolizer.Raster
- * A symbolizer used to render raster images.
+ * Constant: BROWSER_NAME
+ * {String}
+ * A substring of the navigator.userAgent property.  Depending on the userAgent
+ *     property, this will be the empty string or one of the following:
+ *     * "opera" -- Opera
+ *     * "msie"  -- Internet Explorer
+ *     * "safari" -- Safari
+ *     * "firefox" -- FireFox
+ *     * "mozilla" -- Mozilla
  */
-OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {
-    
-    /**
-     * Constructor: OpenLayers.Symbolizer.Raster
-     * Create a symbolizer for rendering rasters.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new raster symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
-    },
-    
-    CLASS_NAME: "OpenLayers.Symbolizer.Raster"
-    
-});
-/* ======================================================================
-    OpenLayers/Symbolizer/Text.js
-   ====================================================================== */
+OpenLayers.BROWSER_NAME = (function() {
+    var name = "";
+    var ua = navigator.userAgent.toLowerCase();
+    if (ua.indexOf("opera") != -1) {
+        name = "opera";
+    } else if (ua.indexOf("msie") != -1) {
+        name = "msie";
+    } else if (ua.indexOf("safari") != -1) {
+        name = "safari";
+    } else if (ua.indexOf("mozilla") != -1) {
+        if (ua.indexOf("firefox") != -1) {
+            name = "firefox";
+        } else {
+            name = "mozilla";
+        }
+    }
+    return name;
+})();
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Symbolizer.js
+ * Function: getBrowserName
+ * 
+ * Returns:
+ * {String} A string which specifies which is the current 
+ *          browser in which we are running. 
+ * 
+ *          Currently-supported browser detection and codes:
+ *           * 'opera' -- Opera
+ *           * 'msie'  -- Internet Explorer
+ *           * 'safari' -- Safari
+ *           * 'firefox' -- FireFox
+ *           * 'mozilla' -- Mozilla
+ * 
+ *          If we are unable to property identify the browser, we 
+ *           return an empty string.
  */
+OpenLayers.Util.getBrowserName = function() {
+    return OpenLayers.BROWSER_NAME;
+};
 
 /**
- * Class: OpenLayers.Symbolizer.Text
- * A symbolizer used to render text labels for features.
+ * Method: getRenderedDimensions
+ * Renders the contentHTML offscreen to determine actual dimensions for
+ *     popup sizing. As we need layout to determine dimensions the content
+ *     is rendered -9999px to the left and absolute to ensure the 
+ *     scrollbars do not flicker
+ *     
+ * Parameters:
+ * contentHTML
+ * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is 
+ *     specified, we fix that dimension of the div to be measured. This is 
+ *     useful in the case where we have a limit in one dimension and must 
+ *     therefore meaure the flow in the other dimension.
+ * 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}
  */
-OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {
+OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
     
-    /** 
-     * APIProperty: label
-     * {String} The text for the label.
-     */
-    label: null,
+    var w, h;
     
-    /** 
-     * APIProperty: fontFamily
-     * {String} The font family for the label.
-     */
-    fontFamily: null,
+    // create temp container div with restricted size
+    var container = document.createElement("div");
+    container.style.visibility = "hidden";
+        
+    var containerElement = (options && options.containerElement) 
+    	? options.containerElement : document.body;
 
-    /** 
-     * APIProperty: fontSize
-     * {String} The font size for the label.
-     */
-    fontSize: null,
+    //fix a dimension, if specified.
+    if (size) {
+        if (size.w) {
+            w = size.w;
+            container.style.width = w + "px";
+        } else if (size.h) {
+            h = size.h;
+            container.style.height = h + "px";
+        }
+    }
 
-    /** 
-     * APIProperty: fontWeight
-     * {String} The font weight for the label.
-     */
-    fontWeight: null,
+    //add css classes, if specified
+    if (options && options.displayClass) {
+        container.className = options.displayClass;
+    }
     
-    /**
-     * Property: fontStyle
-     * {String} The font style for the label.
-     */
-    fontStyle: null,
-
-    /**
-     * Constructor: OpenLayers.Symbolizer.Text
-     * Create a symbolizer for rendering text labels.
-     *
-     * Parameters:
-     * config - {Object} An object containing properties to be set on the 
-     *     symbolizer.  Any documented symbolizer property can be set at 
-     *     construction.
-     *
-     * Returns:
-     * A new text symbolizer.
-     */
-    initialize: function(config) {
-        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
-    },
+    // create temp content div and assign content
+    var content = document.createElement("div");
+    content.innerHTML = contentHTML;
     
-    CLASS_NAME: "OpenLayers.Symbolizer.Text"
+    // we need overflow visible when calculating the size
+    content.style.overflow = "visible";
+    if (content.childNodes) {
+        for (var i=0, l=content.childNodes.length; i<l; i++) {
+            if (!content.childNodes[i].style) continue;
+            content.childNodes[i].style.overflow = "visible";
+        }
+    }
     
-});
-
-/* ======================================================================
-    OpenLayers/Tween.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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.Tween
- */
-OpenLayers.Tween = OpenLayers.Class({
+    // add content to restricted container 
+    container.appendChild(content);
     
-    /**
-     * Constant: INTERVAL
-     * {int} Interval in milliseconds between 2 steps
-     */
-    INTERVAL: 10,
+    // append container to body for rendering
+    containerElement.appendChild(container);
     
-    /**
-     * APIProperty: easing
-     * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
-     *     Defaultly set to OpenLayers.Easing.Expo.easeOut
-     */
-    easing: null,
-    
-    /**
-     * APIProperty: begin
-     * {Object} Values to start the animation with
-     */
-    begin: null,
-    
-    /**
-     * APIProperty: finish
-     * {Object} Values to finish the animation with
-     */
-    finish: null,
-    
-    /**
-     * APIProperty: duration
-     * {int} duration of the tween (number of steps)
-     */
-    duration: null,
-    
-    /**
-     * APIProperty: callbacks
-     * {Object} An object with start, eachStep and done properties whose values
-     *     are functions to be call during the animation. They are passed the
-     *     current computed value as argument.
-     */
-    callbacks: null,
-    
-    /**
-     * Property: time
-     * {int} Step counter
-     */
-    time: null,
-    
-    /**
-     * Property: interval
-     * {int} Interval id returned by window.setInterval
-     */
-    interval: null,
-    
-    /**
-     * Property: playing
-     * {Boolean} Tells if the easing is currently playing
-     */
-    playing: false,
-    
-    /** 
-     * Constructor: OpenLayers.Tween
-     * Creates a Tween.
-     *
-     * Parameters:
-     * easing - {<OpenLayers.Easing>(Function)} easing function method to use
-     */ 
-    initialize: function(easing) {
-        this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
-    },
-    
-    /**
-     * APIMethod: start
-     * Plays the Tween, and calls the callback method on each step
-     * 
-     * Parameters:
-     * begin - {Object} values to start the animation with
-     * finish - {Object} values to finish the animation with
-     * duration - {int} duration of the tween (number of steps)
-     * options - {Object} hash of options (for example callbacks (start, eachStep, done))
-     */
-    start: function(begin, finish, duration, options) {
-        this.playing = true;
-        this.begin = begin;
-        this.finish = finish;
-        this.duration = duration;
-        this.callbacks = options.callbacks;
-        this.time = 0;
-        if (this.interval) {
-            window.clearInterval(this.interval);
-            this.interval = null;
+    // Opera and IE7 can't handle a node with position:aboslute if it inherits
+    // position:absolute from a parent.
+    var parentHasPositionAbsolute = false;
+    var parent = container.parentNode;
+    while (parent && parent.tagName.toLowerCase()!="body") {
+        var parentPosition = OpenLayers.Element.getStyle(parent, "position");
+        if(parentPosition == "absolute") {
+            parentHasPositionAbsolute = true;
+            break;
+        } else if (parentPosition && parentPosition != "static") {
+            break;
         }
-        if (this.callbacks && this.callbacks.start) {
-            this.callbacks.start.call(this, this.begin);
-        }
-        this.interval = window.setInterval(
-            OpenLayers.Function.bind(this.play, this), this.INTERVAL);
-    },
+        parent = parent.parentNode;
+    }
+
+    if(!parentHasPositionAbsolute) {
+        container.style.position = "absolute";
+    }
     
-    /**
-     * APIMethod: stop
-     * Stops the Tween, and calls the done callback
-     *     Doesn't do anything if animation is already finished
-     */
-    stop: function() {
-        if (!this.playing) {
-            return;
-        }
-        
-        if (this.callbacks && this.callbacks.done) {
-            this.callbacks.done.call(this, this.finish);
-        }
-        window.clearInterval(this.interval);
-        this.interval = null;
-        this.playing = false;
-    },
+    // calculate scroll width of content and add corners and shadow width
+    if (!w) {
+        w = parseInt(content.scrollWidth);
     
-    /**
-     * Method: play
-     * Calls the appropriate easing method
-     */
-    play: function() {
-        var value = {};
-        for (var i in this.begin) {
-            var b = this.begin[i];
-            var f = this.finish[i];
-            if (b == null || f == null || isNaN(b) || isNaN(f)) {
-                OpenLayers.Console.error('invalid value for Tween');
-            }
-            
-            var c = f - b;
-            value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
-        }
-        this.time++;
-        
-        if (this.callbacks && this.callbacks.eachStep) {
-            this.callbacks.eachStep.call(this, value);
-        }
-        
-        if (this.time > this.duration) {
-            this.stop();
-        }
-    },
+        // update container width to allow height to adjust
+        container.style.width = w + "px";
+    }        
+    // capture height and add shadow and corner image widths
+    if (!h) {
+        h = parseInt(content.scrollHeight);
+    }
+
+    // remove elements
+    container.removeChild(content);
+    containerElement.removeChild(container);
     
-    /**
-     * Create empty functions for all easing methods.
-     */
-    CLASS_NAME: "OpenLayers.Tween"
-});
+    return new OpenLayers.Size(w, h);
+};
 
 /**
- * Namespace: OpenLayers.Easing
+ * APIFunction: getScrollbarWidth
+ * This function has been modified by the OpenLayers from the original version,
+ *     written by Matthew Eernisse and released under the Apache 2 
+ *     license here:
  * 
- * Credits:
- *      Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
+ *     http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
+ * 
+ *     It has been modified simply to cache its value, since it is physically 
+ *     impossible that this code could ever run in more than one browser at 
+ *     once. 
+ * 
+ * Returns:
+ * {Integer}
  */
-OpenLayers.Easing = {
-    /**
-     * Create empty functions for all easing methods.
-     */
-    CLASS_NAME: "OpenLayers.Easing"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Linear
- */
-OpenLayers.Easing.Linear = {
+OpenLayers.Util.getScrollbarWidth = function() {
     
-    /**
-     * Function: easeIn
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeIn: function(t, b, c, d) {
-        return c*t/d + b;
-    },
+    var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
     
-    /**
-     * Function: easeOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeOut: function(t, b, c, d) {
-        return c*t/d + b;
-    },
+    if (scrollbarWidth == null) {
+        var scr = null;
+        var inn = null;
+        var wNoScroll = 0;
+        var wScroll = 0;
     
-    /**
-     * Function: easeInOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeInOut: function(t, b, c, d) {
-        return c*t/d + b;
-    },
-
-    CLASS_NAME: "OpenLayers.Easing.Linear"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Expo
- */
-OpenLayers.Easing.Expo = {
+        // Outer scrolling div
+        scr = document.createElement('div');
+        scr.style.position = 'absolute';
+        scr.style.top = '-1000px';
+        scr.style.left = '-1000px';
+        scr.style.width = '100px';
+        scr.style.height = '50px';
+        // Start with no scrollbar
+        scr.style.overflow = 'hidden';
     
-    /**
-     * Function: easeIn
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeIn: function(t, b, c, d) {
-        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
-    },
+        // Inner content div
+        inn = document.createElement('div');
+        inn.style.width = '100%';
+        inn.style.height = '200px';
     
-    /**
-     * Function: easeOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeOut: function(t, b, c, d) {
-        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
-    },
+        // Put the inner div in the scrolling div
+        scr.appendChild(inn);
+        // Append the scrolling div to the doc
+        document.body.appendChild(scr);
     
-    /**
-     * Function: easeInOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeInOut: function(t, b, c, d) {
-        if (t==0) return b;
-        if (t==d) return b+c;
-        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
-        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
-    },
-
-    CLASS_NAME: "OpenLayers.Easing.Expo"
-};
-
-/**
- * Namespace: OpenLayers.Easing.Quad
- */
-OpenLayers.Easing.Quad = {
+        // Width of the inner div sans scrollbar
+        wNoScroll = inn.offsetWidth;
     
-    /**
-     * Function: easeIn
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeIn: function(t, b, c, d) {
-        return c*(t/=d)*t + b;
-    },
+        // Add the scrollbar
+        scr.style.overflow = 'scroll';
+        // Width of the inner div width scrollbar
+        wScroll = inn.offsetWidth;
     
-    /**
-     * Function: easeOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeOut: function(t, b, c, d) {
-        return -c *(t/=d)*(t-2) + b;
-    },
+        // Remove the scrolling div from the doc
+        document.body.removeChild(document.body.lastChild);
     
-    /**
-     * Function: easeInOut
-     * 
-     * Parameters:
-     * t - {Float} time
-     * b - {Float} beginning position
-     * c - {Float} total change
-     * d - {Float} duration of the transition
-     */
-    easeInOut: function(t, b, c, d) {
-        if ((t/=d/2) < 1) return c/2*t*t + b;
-        return -c/2 * ((--t)*(t-2) - 1) + b;
-    },
+        // Pixel width of the scroller
+        OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
+        scrollbarWidth = OpenLayers.Util._scrollbarWidth;
+    }
 
-    CLASS_NAME: "OpenLayers.Easing.Quad"
+    return scrollbarWidth;
 };
-/* ======================================================================
-    OpenLayers/Control/ArgParser.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
 /**
- * @requires OpenLayers/Control.js
- */
-
-/**
- * Class: OpenLayers.Control.ArgParser
- * 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. 
+ * APIFunction: getFormattedLonLat
+ * This function will return latitude or longitude value formatted as 
  *
- * Inherits from:
- *  - <OpenLayers.Control>
+ * Parameters:
+ * coordinate - {Float} the coordinate value to be formatted
+ * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
+ *          to be formatted (default = lat)
+ * dmsOption - {String} specify the precision of the output can be one of:
+ *           'dms' show degrees minutes and seconds
+ *           'dm' show only degrees and minutes
+ *           'd' show only degrees
+ * 
+ * Returns:
+ * {String} the coordinate value formatted as a string
  */
-OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
+OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
+    if (!dmsOption) {
+        dmsOption = 'dms';    //default to show degree, minutes, seconds
+    }
+    var abscoordinate = Math.abs(coordinate)
+    var coordinatedegrees = Math.floor(abscoordinate);
 
-    /**
-     * Parameter: center
-     * {<OpenLayers.LonLat>}
-     */
-    center: null,
-    
-    /**
-     * Parameter: zoom
-     * {int}
-     */
-    zoom: null,
+    var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
+    var tempcoordinateminutes = coordinateminutes;
+    coordinateminutes = Math.floor(coordinateminutes);
+    var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
+    coordinateseconds =  Math.round(coordinateseconds*10);
+    coordinateseconds /= 10;
 
-    /**
-     * Parameter: layers 
-     * {Array(<OpenLayers.Layer>)}
-     */
-    layers: null,
-    
-    /** 
-     * APIProperty: displayProjection
-     * {<OpenLayers.Projection>} Requires proj4js support. 
-     *     Projection used when reading the coordinates from the URL. This will
-     *
-     *     reproject the map coordinates from the URL into the map's
-     *     projection.
-     *
-     *     If you are using this functionality, be aware that any permalink
-     *     which is added to the map will determine the coordinate type which
-     *     is read from the URL, which means you should not add permalinks with
-     *     different displayProjections to the same map. 
-     */
-    displayProjection: null, 
+    if( coordinatedegrees < 10 ) {
+        coordinatedegrees = "0" + coordinatedegrees;
+    }
+    var str = coordinatedegrees + "\u00B0";
 
-    /**
-     * Constructor: OpenLayers.Control.ArgParser
-     *
-     * Parameters:
-     * options - {Object}
-     */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * Method: setMap
-     * Set the map property for the control. 
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>} 
-     */
-    setMap: function(map) {
-        OpenLayers.Control.prototype.setMap.apply(this, arguments);
-
-        //make sure we dont already have an arg parser attached
-        for(var i=0, len=this.map.controls.length; i<len; i++) {
-            var control = this.map.controls[i];
-            if ( (control != this) &&
-                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
-                
-                // If a second argparser is added to the map, then we 
-                // override the displayProjection to be the one added to the
-                // map. 
-                if (control.displayProjection != this.displayProjection) {
-                    this.displayProjection = control.displayProjection;
-                }    
-                
-                break;
-            }
+    if (dmsOption.indexOf('dm') >= 0) {
+        if( coordinateminutes < 10 ) {
+            coordinateminutes = "0" + coordinateminutes;
         }
-        if (i == this.map.controls.length) {
-
-            var args = OpenLayers.Util.getParameters();
-            // Be careful to set layer first, to not trigger unnecessary layer loads
-            if (args.layers) {
-                this.layers = args.layers;
-    
-                // when we add a new layer, set its visibility 
-                this.map.events.register('addlayer', this, 
-                                         this.configureLayers);
-                this.configureLayers();
+        str += coordinateminutes + "'";
+  
+        if (dmsOption.indexOf('dms') >= 0) {
+            if( coordinateseconds < 10 ) {
+                coordinateseconds = "0" + coordinateseconds;
             }
-            if (args.lat && args.lon) {
-                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
-                                                    parseFloat(args.lat));
-                if (args.zoom) {
-                    this.zoom = parseInt(args.zoom);
-                }
-    
-                // when we add a new baselayer to see when we can set the center
-                this.map.events.register('changebaselayer', this, 
-                                         this.setCenter);
-                this.setCenter();
-            }
+            str += coordinateseconds + '"';
         }
-    },
-   
-    /** 
-     * Method: setCenter
-     * As soon as a baseLayer has been loaded, we center and zoom
-     *   ...and remove the handler.
-     */
-    setCenter: function() {
-        
-        if (this.map.baseLayer) {
-            //dont need to listen for this one anymore
-            this.map.events.unregister('changebaselayer', this, 
-                                       this.setCenter);
-            
-            if (this.displayProjection) {
-                this.center.transform(this.displayProjection, 
-                                      this.map.getProjectionObject()); 
-            }      
-
-            this.map.setCenter(this.center, this.zoom);
-        }
-    },
-
-    /** 
-     * Method: configureLayers
-     * As soon as all the layers are loaded, cycle through them and 
-     *   hide or show them. 
-     */
-    configureLayers: function() {
-
-        if (this.layers.length == this.map.layers.length) { 
-            this.map.events.unregister('addlayer', this, this.configureLayers);
-
-            for(var i=0, len=this.layers.length; i<len; i++) {
-                
-                var layer = this.map.layers[i];
-                var c = this.layers.charAt(i);
-                
-                if (c == "B") {
-                    this.map.setBaseLayer(layer);
-                } else if ( (c == "T") || (c == "F") ) {
-                    layer.setVisibility(c == "T");
-                }
-            }
-        }
-    },     
-
-    CLASS_NAME: "OpenLayers.Control.ArgParser"
-});
-/* ======================================================================
-    OpenLayers/Control/PanZoom.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Control.js
- */
-
-/**
- * Class: OpenLayers.Control.PanZoom
- * 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>
- */
-OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
-
-    /** 
-     * APIProperty: slideFactor
-     * {Integer} Number of pixels by which we'll pan the map in any direction 
-     *     on clicking the arrow buttons.  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 
-     */
-    buttons: null,
-
-    /** 
-     * Property: position
-     * {<OpenLayers.Pixel>} 
-     */
-    position: null,
-
-    /**
-     * Constructor: OpenLayers.Control.PanZoom
-     * 
-     * Parameters:
-     * options - {Object}
-     */
-    initialize: function(options) {
-        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
-                                             OpenLayers.Control.PanZoom.Y);
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);
-        this.removeButtons();
-        this.buttons = null;
-        this.position = null;
-    },
-
-    /**
-     * Method: draw
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} 
-     * 
-     * Returns:
-     * {DOMElement} A reference to the container div for the PanZoom control.
-     */
-    draw: function(px) {
-        // initialize our internal div
-        OpenLayers.Control.prototype.draw.apply(this, arguments);
-        px = this.position;
-
-        // place the controls
-        this.buttons = [];
-
-        var sz = new OpenLayers.Size(18,18);
-        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
-
-        this._addButton("panup", "north-mini.png", centered, sz);
-        px.y = centered.y+sz.h;
-        this._addButton("panleft", "west-mini.png", px, sz);
-        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
-        this._addButton("pandown", "south-mini.png", 
-                        centered.add(0, sz.h*2), sz);
-        this._addButton("zoomin", "zoom-plus-mini.png", 
-                        centered.add(0, sz.h*3+5), sz);
-        this._addButton("zoomworld", "zoom-world-mini.png", 
-                        centered.add(0, sz.h*4+5), sz);
-        this._addButton("zoomout", "zoom-minus-mini.png", 
-                        centered.add(0, sz.h*5+5), sz);
-        return this.div;
-    },
+    }
     
-    /**
-     * Method: _addButton
-     * 
-     * Parameters:
-     * id - {String} 
-     * img - {String} 
-     * xy - {<OpenLayers.Pixel>} 
-     * sz - {<OpenLayers.Size>} 
-     * 
-     * Returns:
-     * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
-     *     image of the button, and has all the proper event handlers set.
-     */
-    _addButton:function(id, img, xy, sz) {
-        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
-        var btn = OpenLayers.Util.createAlphaImageDiv(
-                                    this.id + "_" + id, 
-                                    xy, sz, imgLocation, "absolute");
+    if (axis == "lon") {
+        str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
+    } else {
+        str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
+    }
+    return str;
+};
 
-        //we want to add the outer div
-        this.div.appendChild(btn);
-
-        OpenLayers.Event.observe(btn, "mousedown", 
-            OpenLayers.Function.bindAsEventListener(this.buttonDown, btn));
-        OpenLayers.Event.observe(btn, "dblclick", 
-            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
-        OpenLayers.Event.observe(btn, "click", 
-            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
-        btn.action = id;
-        btn.map = this.map;
-    
-        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;
-        btn.getSlideFactor = 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:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean}
-     */
-    doubleClick: function (evt) {
-        OpenLayers.Event.stop(evt);
-        return false;
-    },
-    
-    /**
-     * Method: buttonDown
-     *
-     * Parameters:
-     * evt - {Event} 
-     */
-    buttonDown: function (evt) {
-        if (!OpenLayers.Event.isLeftClick(evt)) {
-            return;
-        }
-
-        switch (this.action) {
-            case "panup": 
-                this.map.pan(0, -this.getSlideFactor("h"));
-                break;
-            case "pandown": 
-                this.map.pan(0, this.getSlideFactor("h"));
-                break;
-            case "panleft": 
-                this.map.pan(-this.getSlideFactor("w"), 0);
-                break;
-            case "panright": 
-                this.map.pan(this.getSlideFactor("w"), 0);
-                break;
-            case "zoomin": 
-                this.map.zoomIn(); 
-                break;
-            case "zoomout": 
-                this.map.zoomOut(); 
-                break;
-            case "zoomworld": 
-                this.map.zoomToMaxExtent(); 
-                break;
-        }
-
-        OpenLayers.Event.stop(evt);
-    },
-
-    CLASS_NAME: "OpenLayers.Control.PanZoom"
-});
-
-/**
- * Constant: X
- * {Integer}
- */
-OpenLayers.Control.PanZoom.X = 4;
-
-/**
- * Constant: Y
- * {Integer}
- */
-OpenLayers.Control.PanZoom.Y = 4;
 /* ======================================================================
-    OpenLayers/Control/ScaleLine.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- */
-
-/**
- * Class: OpenLayers.Control.ScaleLine
- * The ScaleLine displays a small line indicator representing the current 
- * map scale on the map. By default it is drawn in the lower left corner of
- * the map.
- * 
- * Inherits from:
- *  - <OpenLayers.Control>
- *  
- * Is a very close copy of:
- *  - <OpenLayers.Control.Scale>
- */
-OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
-
-    /**
-     * Property: maxWidth
-     * {Integer} Maximum width of the scale line in pixels.  Default is 100.
-     */
-    maxWidth: 100,
-
-    /**
-     * Property: topOutUnits
-     * {String} Units for zoomed out on top bar.  Default is km.
-     */
-    topOutUnits: "km",
-    
-    /**
-     * Property: topInUnits
-     * {String} Units for zoomed in on top bar.  Default is m.
-     */
-    topInUnits: "m",
-
-    /**
-     * Property: bottomOutUnits
-     * {String} Units for zoomed out on bottom bar.  Default is mi.
-     */
-    bottomOutUnits: "mi",
-
-    /**
-     * Property: bottomInUnits
-     * {String} Units for zoomed in on bottom bar.  Default is ft.
-     */
-    bottomInUnits: "ft",
-    
-    /**
-     * Property: eTop
-     * {DOMElement}
-     */
-    eTop: null,
-
-    /**
-     * Property: eBottom
-     * {DOMElement}
-     */
-    eBottom:null,
-    
-    /**
-     * APIProperty: geodesic
-     * {Boolean} Use geodesic measurement. Default is false. The recommended
-     * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
-     * true, the scale will be calculated based on the horizontal size of the
-     * pixel in the center of the map viewport.
-     */
-    geodesic: false,
-
-    /**
-     * Constructor: OpenLayers.Control.ScaleLine
-     * Create a new scale line control.
-     * 
-     * Parameters:
-     * options - {Object} An optional object whose properties will be used
-     *     to extend the control.
-     */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);     
-    },
-
-    /**
-     * Method: draw
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    draw: function() {
-        OpenLayers.Control.prototype.draw.apply(this, arguments);
-        if (!this.eTop) {
-            // stick in the top bar
-            this.eTop = document.createElement("div");
-            this.eTop.className = this.displayClass + "Top";
-            var theLen = this.topInUnits.length;
-            this.div.appendChild(this.eTop);
-            if((this.topOutUnits == "") || (this.topInUnits == "")) {
-                this.eTop.style.visibility = "hidden";
-            } else {
-                this.eTop.style.visibility = "visible";
-            }
-
-            // and the bottom bar
-            this.eBottom = document.createElement("div");
-            this.eBottom.className = this.displayClass + "Bottom";
-            this.div.appendChild(this.eBottom);
-            if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
-                this.eBottom.style.visibility = "hidden";
-            } else {
-                this.eBottom.style.visibility = "visible";
-            }
-        }
-        this.map.events.register('moveend', this, this.update);
-        this.update();
-        return this.div;
-    },
-
-    /** 
-     * Method: getBarLen
-     * Given a number, round it down to the nearest 1,2,5 times a power of 10.
-     * That seems a fairly useful set of number groups to use.
-     * 
-     * Parameters:
-     * maxLen - {float}  the number we're rounding down from
-     * 
-     * Returns:
-     * {Float} the rounded number (less than or equal to maxLen)
-     */
-    getBarLen: function(maxLen) {
-        // nearest power of 10 lower than maxLen
-        var digits = parseInt(Math.log(maxLen) / Math.log(10));
-        var pow10 = Math.pow(10, digits);
-        
-        // ok, find first character
-        var firstChar = parseInt(maxLen / pow10);
-
-        // right, put it into the correct bracket
-        var barLen;
-        if(firstChar > 5) {
-            barLen = 5;
-        } else if(firstChar > 2) {
-            barLen = 2;
-        } else {
-            barLen = 1;
-        }
-
-        // scale it up the correct power of 10
-        return barLen * pow10;
-    },
-
-    /**
-     * Method: update
-     * Update the size of the bars, and the labels they contain.
-     */
-    update: function() {
-        var res = this.map.getResolution();
-        if (!res) {
-            return;
-        }
-
-        var curMapUnits = this.map.getUnits();
-        var inches = OpenLayers.INCHES_PER_UNIT;
-
-        // convert maxWidth to map units
-        var maxSizeData = this.maxWidth * res * inches[curMapUnits];
-        var geodesicRatio = 1;
-        if(this.geodesic === true) {
-            var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||
-                0.000001) * this.maxWidth;
-            var maxSizeKilometers = maxSizeData / inches["km"];
-            geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
-            maxSizeData *= geodesicRatio;
-        }
-
-        // decide whether to use large or small scale units     
-        var topUnits;
-        var bottomUnits;
-        if(maxSizeData > 100000) {
-            topUnits = this.topOutUnits;
-            bottomUnits = this.bottomOutUnits;
-        } else {
-            topUnits = this.topInUnits;
-            bottomUnits = this.bottomInUnits;
-        }
-
-        // and to map units units
-        var topMax = maxSizeData / inches[topUnits];
-        var bottomMax = maxSizeData / inches[bottomUnits];
-
-        // now trim this down to useful block length
-        var topRounded = this.getBarLen(topMax);
-        var bottomRounded = this.getBarLen(bottomMax);
-
-        // and back to display units
-        topMax = topRounded / inches[curMapUnits] * inches[topUnits];
-        bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
-
-        // and to pixel units
-        var topPx = topMax / res / geodesicRatio;
-        var bottomPx = bottomMax / res / geodesicRatio;
-        
-        // now set the pixel widths
-        // and the values inside them
-        
-        if (this.eBottom.style.visibility == "visible"){
-            this.eBottom.style.width = Math.round(bottomPx) + "px"; 
-            this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ;
-        }
-            
-        if (this.eTop.style.visibility == "visible"){
-            this.eTop.style.width = Math.round(topPx) + "px";
-            this.eTop.innerHTML = topRounded + " " + topUnits;
-        }
-        
-    }, 
-
-    CLASS_NAME: "OpenLayers.Control.ScaleLine"
-});
-
-/* ======================================================================
     OpenLayers/Events.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -10969,8 +5114,8 @@
      * Construct an OpenLayers.Events object.
      *
      * Parameters:
-     * object - {Object} The js object to which this Events object  is being
-     * added element - {DOMElement} A dom element to respond to browser events
+     * object - {Object} The js object to which this Events object  is being added
+     * element - {DOMElement} A dom element to respond to browser events
      * eventTypes - {Array(String)} Array of custom application events 
      * fallThrough - {Boolean} Allow events to fall through after these have
      *                         been handled?
@@ -11262,7 +5407,7 @@
 
         // fast path
         if(!listeners || listeners.length == 0) {
-            return;
+            return undefined;
         }
 
         // prep evt object with object & div references
@@ -11278,7 +5423,8 @@
         // execute all callbacks registered for specified type
         // get a clone of the listeners array to
         // allow for splicing during callbacks
-        var listeners = listeners.slice(), continueChain;
+        listeners = listeners.slice();
+        var continueChain;
         for (var i=0, len=listeners.length; i<len; i++) {
             var callback = listeners[i];
             // bind the context to callback.obj
@@ -11306,10 +5452,15 @@
      * evt - {Event} 
      */
     handleBrowserEvent: function (evt) {
+        var type = evt.type, listeners = this.listeners[type];
+        if(!listeners || listeners.length == 0) {
+            // noone's listening, bail out
+            return;
+        }
         if (this.includeXY) {
             evt.xy = this.getMousePosition(evt);
         } 
-        this.triggerEvent(evt.type, evt);
+        this.triggerEvent(type, evt);
     },
 
     /**
@@ -11343,11 +5494,10 @@
         }
         
         if (!this.element.scrolls) {
+            var viewportElement = OpenLayers.Util.getViewportElement();
             this.element.scrolls = [
-                (document.documentElement.scrollLeft
-                         || document.body.scrollLeft),
-                (document.documentElement.scrollTop
-                         || document.body.scrollTop)
+                viewportElement.scrollLeft,
+                viewportElement.scrollTop
             ];
         }
 
@@ -11360,8 +5510,6 @@
         
         if (!this.element.offsets) {
             this.element.offsets = OpenLayers.Util.pagePosition(this.element);
-            this.element.offsets[0] += this.element.scrolls[0];
-            this.element.offsets[1] += this.element.scrolls[1];
         }
         return new OpenLayers.Pixel(
             (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
@@ -11374,4117 +5522,344 @@
     CLASS_NAME: "OpenLayers.Events"
 });
 /* ======================================================================
-    OpenLayers/Format.js
+    OpenLayers/Tween.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
- * @requires OpenLayers/Util.js
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Console.js
  */
 
 /**
- * Class: OpenLayers.Format
- * Base class for format reading/writing a variety of formats.  Subclasses
- *     of OpenLayers.Format are expected to have read and write methods.
+ * Namespace: OpenLayers.Tween
  */
-OpenLayers.Format = OpenLayers.Class({
+OpenLayers.Tween = OpenLayers.Class({
     
     /**
-     * Property: options
-     * {Object} A reference to options passed to the constructor.
+     * Constant: INTERVAL
+     * {int} Interval in milliseconds between 2 steps
      */
-    options: null,
+    INTERVAL: 10,
     
     /**
-     * APIProperty: externalProjection
-     * {<OpenLayers.Projection>} When passed a externalProjection and
-     *     internalProjection, the format will reproject the geometries it
-     *     reads or writes. The externalProjection is the projection used by
-     *     the content which is passed into read or which comes out of write.
-     *     In order to reproject, a projection transformation function for the
-     *     specified projections must be available. This support may be 
-     *     provided via proj4js or via a custom transformation function. See
-     *     {<OpenLayers.Projection.addTransform>} for more information on
-     *     custom transformations.
+     * APIProperty: easing
+     * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
+     *     Defaultly set to OpenLayers.Easing.Expo.easeOut
      */
-    externalProjection: null,
-
-    /**
-     * APIProperty: internalProjection
-     * {<OpenLayers.Projection>} When passed a externalProjection and
-     *     internalProjection, the format will reproject the geometries it
-     *     reads or writes. The internalProjection is the projection used by
-     *     the geometries which are returned by read or which are passed into
-     *     write.  In order to reproject, a projection transformation function
-     *     for the specified projections must be available. This support may be
-     *     provided via proj4js or via a custom transformation function. See
-     *     {<OpenLayers.Projection.addTransform>} for more information on
-     *     custom transformations.
-     */
-    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.
-     *
-     * Parameters:
-     * 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
-     */
-    initialize: function(options) {
-        OpenLayers.Util.extend(this, options);
-        this.options = options;
-    },
+    easing: null,
     
     /**
-     * APIMethod: destroy
-     * Clean up.
+     * APIProperty: begin
+     * {Object} Values to start the animation with
      */
-    destroy: function() {
-    },
-
-    /**
-     * Method: read
-     * Read data from a string, and return an object whose type depends on the
-     * subclass. 
-     * 
-     * Parameters:
-     * data - {string} Data to read/parse.
-     *
-     * Returns:
-     * Depends on the subclass
-     */
-    read: function(data) {
-        OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));
-    },
+    begin: null,
     
     /**
-     * Method: write
-     * Accept an object, and return a string. 
-     *
-     * Parameters:
-     * object - {Object} Object to be serialized
-     *
-     * Returns:
-     * {String} A string representation of the object.
+     * APIProperty: finish
+     * {Object} Values to finish the animation with
      */
-    write: function(object) {
-        OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));
-    },
-
-    CLASS_NAME: "OpenLayers.Format"
-});     
-/* ======================================================================
-    OpenLayers/Lang/en.js
-   ====================================================================== */
-
-/**
- * @requires OpenLayers/Lang.js
- */
-
-/**
- * Namespace: OpenLayers.Lang["en"]
- * Dictionary for English.  Keys for entries are used in calls to
- *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
- *     strings formatted for use with <OpenLayers.String.format> calls.
- */
-OpenLayers.Lang.en = {
-
-    'unhandledRequest': "Unhandled request return ${statusText}",
-
-    'permalink': "Permalink",
-
-    'overlays': "Overlays",
-
-    'baseLayer': "Base Layer",
-
-    'sameProjection':
-        "The overview map only works when it is in the same projection as the main map",
-
-    'readNotImplemented': "Read not implemented.",
-
-    'writeNotImplemented': "Write not implemented.",
-
-    'noFID': "Can't update a feature for which there is no FID.",
-
-    'errorLoadingGML': "Error in loading GML file ${url}",
-
-    'browserNotSupported':
-        "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
-
-    'componentShouldBe': "addFeatures : component should be an ${geomType}",
-
-    // console message
-    'getFeatureError':
-        "getFeatureFromEvent called on layer with no renderer. This usually means you " +
-        "destroyed a layer, but not some handler which is associated with it.",
-
-    // console message
-    'minZoomLevelError':
-        "The minZoomLevel property is only intended for use " +
-        "with the FixedZoomLevels-descendent layers. That this " +
-        "wfs layer checks for minZoomLevel is a relic of the" +
-        "past. We cannot, however, remove it without possibly " +
-        "breaking OL based applications that may depend on it." +
-        " Therefore we are deprecating it -- the minZoomLevel " +
-        "check below will be removed at 3.0. Please instead " +
-        "use min/max resolution setting as described here: " +
-        "http://trac.openlayers.org/wiki/SettingZoomLevels",
-
-    'commitSuccess': "WFS Transaction: SUCCESS ${response}",
-
-    'commitFailed': "WFS Transaction: FAILED ${response}",
-
-    'googleWarning':
-        "The Google Layer was unable to load correctly.<br><br>" +
-        "To get rid of this message, select a new BaseLayer " +
-        "in the layer switcher in the upper-right corner.<br><br>" +
-        "Most likely, this is because the Google Maps library " +
-        "script was either not included, or does not contain the " +
-        "correct API key for your site.<br><br>" +
-        "Developers: For help getting this working correctly, " +
-        "<a href='http://trac.openlayers.org/wiki/Google' " +
-        "target='_blank'>click here</a>",
-
-    'getLayerWarning':
-        "The ${layerType} Layer was unable to load correctly.<br><br>" +
-        "To get rid of this message, select a new BaseLayer " +
-        "in the layer switcher in the upper-right corner.<br><br>" +
-        "Most likely, this is because the ${layerLib} library " +
-        "script was not correctly included.<br><br>" +
-        "Developers: For help getting this working correctly, " +
-        "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
-        "target='_blank'>click here</a>",
-
-    'scale': "Scale = 1 : ${scaleDenom}",
+    finish: null,
     
-    //labels for the graticule control
-    'W': 'W',
-    'E': 'E',
-    'N': 'N',
-    'S': 'S',
-    'graticule': 'Graticule',
-
-    // console message
-    'layerAlreadyAdded':
-        "You tried to add the layer: ${layerName} to the map, but it has already been added",
-
-    // console message
-    'reprojectDeprecated':
-        "You are using the 'reproject' option " +
-        "on the ${layerName} layer. This option is deprecated: " +
-        "its use was designed to support displaying data over commercial " + 
-        "basemaps, but that functionality should now be achieved by using " +
-        "Spherical Mercator support. More information is available from " +
-        "http://trac.openlayers.org/wiki/SphericalMercator.",
-
-    // console message
-    'methodDeprecated':
-        "This method has been deprecated and will be removed in 3.0. " +
-        "Please use ${newMethod} instead.",
-
-    // console message
-    'boundsAddError': "You must pass both x and y values to the add function.",
-
-    // console message
-    'lonlatAddError': "You must pass both lon and lat values to the add function.",
-
-    // console message
-    'pixelAddError': "You must pass both x and y values to the add function.",
-
-    // console message
-    'unsupportedGeometryType': "Unsupported geometry type: ${geomType}",
-
-    // console message
-    'pagePositionFailed':
-        "OpenLayers.Util.pagePosition failed: element with id ${elemId} may be misplaced.",
-
-    // console message
-    'filterEvaluateNotImplemented': "evaluate is not implemented for this filter type.",
-
-    // **** end ****
-    'end': ''
-    
-};
-/* ======================================================================
-    OpenLayers/Popup/AnchoredBubble.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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
- */
-
-/**
- * Class: OpenLayers.Popup.AnchoredBubble
- * 
- * Inherits from: 
- *  - <OpenLayers.Popup.Anchored>
- */
-OpenLayers.Popup.AnchoredBubble = 
-  OpenLayers.Class(OpenLayers.Popup.Anchored, {
-
     /**
-     * Property: rounded
-     * {Boolean} Has the popup been rounded yet?
+     * APIProperty: duration
+     * {int} duration of the tween (number of steps)
      */
-    rounded: false, 
+    duration: null,
     
-    /** 
-     * 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.
+     * APIProperty: callbacks
+     * {Object} An object with start, eachStep and done properties whose values
+     *     are functions to be call during the animation. They are passed the
+     *     current computed value as argument.
      */
-    updateRelativePosition: function() {
-        this.setRicoCorners();
-    },
-
-    /**
-     * 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);
-            }
-        }
-    },  
+    callbacks: null,
     
     /**
-     * APIMethod: setOpacity
-     * 
-     * Parameters: 
-     * opacity - {float}
+     * Property: time
+     * {int} Step counter
      */
-    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() {
+    time: null,
     
-        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/Projection.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Util.js
- */
-
-/**
- * Class: OpenLayers.Projection
- * Class for coordinate transforms between coordinate systems.
- *     Depends on the proj4js library. If proj4js is not available, 
- *     then this is just an empty stub.
- */
-OpenLayers.Projection = OpenLayers.Class({
-
     /**
-     * Property: proj
-     * {Object} Proj4js.Proj instance.
+     * Property: interval
+     * {int} Interval id returned by window.setInterval
      */
-    proj: null,
+    interval: null,
     
     /**
-     * Property: projCode
-     * {String}
+     * Property: playing
+     * {Boolean} Tells if the easing is currently playing
      */
-    projCode: null,
-
-    /**
-     * Constructor: OpenLayers.Projection
-     * This class offers several methods for interacting with a wrapped 
-     *     pro4js projection object. 
-     *
-     * Parameters:
-     * 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.
-     */
-    initialize: function(projCode, options) {
-        OpenLayers.Util.extend(this, options);
-        this.projCode = projCode;
-        if (window.Proj4js) {
-            this.proj = new Proj4js.Proj(projCode);
-        }
-    },
+    playing: false,
     
-    /**
-     * APIMethod: getCode
-     * Get the string SRS code.
-     *
-     * Returns:
-     * {String} The SRS code.
-     */
-    getCode: function() {
-        return this.proj ? this.proj.srsCode : this.projCode;
-    },
-   
-    /**
-     * APIMethod: getUnits
-     * Get the units string for the projection -- returns null if 
-     *     proj4js is not available.
-     *
-     * Returns:
-     * {String} The units abbreviation.
-     */
-    getUnits: function() {
-        return this.proj ? this.proj.units : null;
-    },
-
-    /**
-     * Method: toString
-     * Convert projection to string (getCode wrapper).
-     *
-     * Returns:
-     * {String} The projection code.
-     */
-    toString: function() {
-        return this.getCode();
-    },
-
-    /**
-     * Method: equals
-     * Test equality of two projection instances.  Determines equality based
-     *     soley on the projection code.
-     *
-     * Returns:
-     * {Boolean} The two projections are equivalent.
-     */
-    equals: function(projection) {
-        if (projection && projection.getCode) {
-            return this.getCode() == projection.getCode();
-        } else {
-            return false;
-        }    
-    },
-
-    /* Method: destroy
-     * Destroy projection object.
-     */
-    destroy: function() {
-        delete this.proj;
-        delete this.projCode;
-    },
-    
-    CLASS_NAME: "OpenLayers.Projection" 
-});     
-
-/**
- * Property: transforms
- * Transforms is an object, with from properties, each of which may
- * have a to property. This allows you to define projections without 
- * requiring support for proj4js to be included.
- *
- * This object has keys which correspond to a 'source' projection object.  The
- * keys should be strings, corresponding to the projection.getCode() value.
- * Each source projection object should have a set of destination projection
- * keys included in the object. 
- * 
- * Each value in the destination object should be a transformation function,
- * where the function is expected to be passed an object with a .x and a .y
- * property.  The function should return the object, with the .x and .y
- * transformed according to the transformation function.
- *
- * Note - Properties on this object should not be set directly.  To add a
- *     transform method to this object, use the <addTransform> method.  For an
- *     example of usage, see the OpenLayers.Layer.SphericalMercator file.
- */
-OpenLayers.Projection.transforms = {};
-
-/**
- * APIMethod: addTransform
- * Set a custom transform method between two projections.  Use this method in
- *     cases where the proj4js lib is not available or where custom projections
- *     need to be handled.
- *
- * Parameters:
- * from - {String} The code for the source projection
- * to - {String} the code for the destination projection
- * method - {Function} A function that takes a point as an argument and
- *     transforms that point from the source to the destination projection
- *     in place.  The original point should be modified.
- */
-OpenLayers.Projection.addTransform = function(from, to, method) {
-    if(!OpenLayers.Projection.transforms[from]) {
-        OpenLayers.Projection.transforms[from] = {};
-    }
-    OpenLayers.Projection.transforms[from][to] = method;
-};
-
-/**
- * APIMethod: transform
- * Transform a point coordinate from one projection to another.  Note that
- *     the input point is transformed in place.
- * 
- * Parameters:
- * point - {{OpenLayers.Geometry.Point> | Object} An object with x and y
- *     properties representing coordinates in those dimensions.
- * sourceProj - {OpenLayers.Projection} Source map coordinate system
- * destProj - {OpenLayers.Projection} Destination map coordinate system
- *
- * Returns:
- * point - {object} A transformed coordinate.  The original point is modified.
- */
-OpenLayers.Projection.transform = function(point, source, dest) {
-    if (source.proj && dest.proj) {
-        point = Proj4js.transform(source.proj, dest.proj, point);
-    } else if (source && dest && 
-               OpenLayers.Projection.transforms[source.getCode()] && 
-               OpenLayers.Projection.transforms[source.getCode()][dest.getCode()]) {
-        OpenLayers.Projection.transforms[source.getCode()][dest.getCode()](point); 
-    }
-    return point;
-};
-/* ======================================================================
-    OpenLayers/Renderer/SVG.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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: symbolMetrics
-     * {Object} Cache for symbol metrics according to their svg coordinate
-     *     space. This is an object keyed by the symbol's id, and values are
-     *     an array of [width, centerX, centerY].
-     */
-    symbolMetrics: null,
-    
-    /**
-     * Property: isGecko
-     * {Boolean}
-     */
-    isGecko: null,
-
-    /**
-     * Property: supportUse
-     * {Boolean} true if defs/use is supported - known to not work as expected
-     * at least in some applewebkit/5* builds.
-     * See https://bugs.webkit.org/show_bug.cgi?id=33322
-     */
-    supportUse: null,
-
-    /**
-     * Constructor: OpenLayers.Renderer.SVG
-     * 
-     * Parameters:
-     * containerID - {String}
-     */
-    initialize: function(containerID) {
-        if (!this.supported()) { 
-            return; 
-        }
-        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
-                                                                arguments);
-        this.translationParameters = {x: 0, y: 0};
-        this.supportUse = (navigator.userAgent.toLowerCase().indexOf("applewebkit/5") == -1);
-        this.isGecko = (navigator.userAgent.toLowerCase().indexOf("gecko/") != -1);
-        
-        this.symbolMetrics = {};
-    },
-
-    /**
-     * 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
+     * Constructor: OpenLayers.Tween
+     * Creates a Tween.
      *
      * 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.
-     * 
-     * Returns:
-     * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
-     *           valid range.
+     * easing - {<OpenLayers.Easing>(Function)} easing function method to use
      */ 
-    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);
+    initialize: function(easing) {
+        this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
     },
-
-    /**
-     * 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.
-     */
-    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: translate
-     * Transforms the SVG coordinate system
+     * APIMethod: start
+     * Plays the Tween, and calls the callback method on each step
      * 
      * Parameters:
-     * x - {Float}
-     * y - {Float}
-     * 
-     * Returns:
-     * {Boolean} true if the translation parameters are in the valid coordinates
-     *     range, false otherwise.
+     * begin - {Object} values to start the animation with
+     * finish - {Object} values to finish the animation with
+     * duration - {int} duration of the tween (number of steps)
+     * options - {Object} hash of options (for example callbacks (start, eachStep, done))
      */
-    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;
+    start: function(begin, finish, duration, options) {
+        this.playing = true;
+        this.begin = begin;
+        this.finish = finish;
+        this.duration = duration;
+        this.callbacks = options.callbacks;
+        this.time = 0;
+        if (this.interval) {
+            window.clearInterval(this.interval);
+            this.interval = null;
         }
-    },
-
-    /**
-     * 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 = this.supportUse === false ? "svg" : "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;
+        if (this.callbacks && this.callbacks.start) {
+            this.callbacks.start.call(this, this.begin);
         }
-        return nodeType;
+        this.interval = window.setInterval(
+            OpenLayers.Function.bind(this.play, this), this.INTERVAL);
     },
-
-    /** 
-     * 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) {
-            node.style.visibility = "";
-            if (style.graphic === false) {
-                node.style.visibility = "hidden";
-            } else 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 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);
-                pos = this.getPosition(node);
-                widthFactor = this.symbolMetrics[id][0] * 3 / size;
-                
-                // remove the node from the dom before we modify it. This
-                // prevents various rendering issues in Safari and FF
-                var parent = node.parentNode;
-                var nextSibling = node.nextSibling;
-                if(parent) {
-                    parent.removeChild(node);
-                }
-                
-                if(this.supportUse === false) {
-                    // workaround for webkit versions that cannot do defs/use
-                    // (see https://bugs.webkit.org/show_bug.cgi?id=33322):
-                    // copy the symbol instead of referencing it
-                    var src = document.getElementById(id);
-                    node.firstChild && node.removeChild(node.firstChild);
-                    node.appendChild(src.firstChild.cloneNode(true));
-                    node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
-                } else {
-                    node.setAttributeNS(this.xlinkns, "href", "#" + id);
-                }
-                node.setAttributeNS(null, "width", size);
-                node.setAttributeNS(null, "height", size);
-                node.setAttributeNS(null, "x", pos.x - offset);
-                node.setAttributeNS(null, "y", pos.y - offset);
-                
-                // now that the node has all its new properties, insert it
-                // back into the dom where it was
-                if(nextSibling) {
-                    parent.insertBefore(node, nextSibling);
-                } else if(parent) {
-                    parent.appendChild(node);
-                }
-            } else {
-                node.setAttributeNS(null, "r", style.pointRadius);
-            }
-
-            var rotation = style.rotation;
-            if ((rotation !== undefined || node._rotation !== undefined) && pos) {
-                node._rotation = rotation;
-                rotation |= 0;
-                if(node.nodeName !== "svg") {
-                    node.setAttributeNS(null, "transform",
-                        "rotate(" + rotation + " " + pos.x + " " +
-                        pos.y + ")");
-                } else {
-                     var metrics = this.symbolMetrics[id];
-                     node.firstChild.setAttributeNS(null, "transform",
-                     "rotate(" + style.rotation + " " + metrics[1] +
-                         " " +  metrics[2] + ")");
-                }
-            }
-        }
-        
-        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 || "round");
-            // 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");
-            style.strokeDashstyle && 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: dashStyle
-     * 
-     * Parameters:
-     * style - {Object}
-     * widthFactor - {Number}
-     * 
-     * Returns:
-     * {String} A SVG compliant 'stroke-dasharray' value
-     */
-    dashStyle: function(style, widthFactor) {
-        var w = style.strokeWidth * widthFactor;
-        var str = style.strokeDashstyle;
-        switch (str) {
-            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 OpenLayers.String.trim(str).replace(/\s+/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
+     * APIMethod: stop
+     * Stops the Tween, and calls the done callback
+     *     Doesn't do anything if animation is already finished
      */
-    createRenderRoot: function() {
-        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
-    },
-
-    /**
-     * Method: createRoot
-     * 
-     * Parameter:
-     * suffix - {String} suffix to append to the id
-     * 
-     * Returns:
-     * {DOMElement}
-     */
-    createRoot: function(suffix) {
-        return this.nodeFactory(this.container.id + suffix, "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>}
-     * 
-     * 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;
+    stop: function() {
+        if (!this.playing) {
+            return;
         }
-    },
-    
-    /**
-     * 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;
+        
+        if (this.callbacks && this.callbacks.done) {
+            this.callbacks.done.call(this, this.finish);
         }
+        window.clearInterval(this.interval);
+        this.interval = null;
+        this.playing = 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: drawText
-     * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
-     * featureId - {String}
-     * style -
-     * location - {<OpenLayers.Geometry.Point>}
+     * Method: play
+     * Calls the appropriate easing method
      */
-    drawText: function(featureId, style, location) {
-        var resolution = this.getResolution();
-        
-        var x = (location.x / resolution + this.left);
-        var y = (location.y / resolution - this.top);
-        
-        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
-        var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan");
-
-        label.setAttributeNS(null, "x", x);
-        label.setAttributeNS(null, "y", -y);
-        
-        if (style.fontColor) {
-            label.setAttributeNS(null, "fill", style.fontColor);
-        }
-        if (style.fontOpacity) {
-            label.setAttributeNS(null, "opacity", style.fontOpacity);
-        }
-        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);
-        }
-        if(style.labelSelect === true) {
-            label.setAttributeNS(null, "pointer-events", "visible");
-            label._featureId = featureId;
-            tspan._featureId = featureId;
-            tspan._geometry = location;
-            tspan._geometryClass = location.CLASS_NAME;
-        } else {
-            label.setAttributeNS(null, "pointer-events", "none");
-        }
-        var align = style.labelAlign || "cm";
-        label.setAttributeNS(null, "text-anchor",
-            OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
-
-        if (this.isGecko) {
-            label.setAttributeNS(null, "dominant-baseline",
-                OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
-        } else {
-            tspan.setAttributeNS(null, "baseline-shift",
-                OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
-        }
-
-        tspan.textContent = style.label;
-        
-        if(!label.parentNode) {
-            label.appendChild(tspan);
-            this.textRoot.appendChild(label);
-        }   
-    },
-    
-    /** 
-     * 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;
-        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;
+    play: function() {
+        var value = {};
+        for (var i in this.begin) {
+            var b = this.begin[i];
+            var f = this.finish[i];
+            if (b == null || f == null || isNaN(b) || isNaN(f)) {
+                OpenLayers.Console.error('invalid value for Tween');
             }
+            
+            var c = f - b;
+            value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
         }
-
-        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;
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Method: getPosition
-     * Finds the position of an svg node.
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * 
-     * Returns:
-     * {Object} hash with x and y properties, representing the coordinates
-     *     within the svg coordinate system
-     */
-    getPosition: function(node) {
-        return({
-            x: parseFloat(node.getAttributeNS(null, "cx")),
-            y: parseFloat(node.getAttributeNS(null, "cy"))
-        });
-    },
-
-    /**
-     * 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;
+        this.time++;
         
-        // check if symbol already exists in the defs
-        if (document.getElementById(id) != null) {
-            return id;
+        if (this.callbacks && this.callbacks.eachStep) {
+            this.callbacks.eachStep.call(this, value);
         }
         
-        var symbol = OpenLayers.Renderer.symbol[graphicName];
-        if (!symbol) {
-            throw new Error(graphicName + ' is not a valid symbol name');
+        if (this.time > this.duration) {
+            this.stop();
         }
-
-        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.push(x, ",", y);
-        }
-        
-        node.setAttributeNS(null, "points", points.join(" "));
-        
-        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.symbolMetrics[id] = [
-            Math.max(width, height),
-            symbolExtent.getCenterLonLat().lon,
-            symbolExtent.getCenterLonLat().lat
-        ];
-        
-        this.defs.appendChild(symbolNode);
-        return symbolNode.id;
     },
     
     /**
-     * Method: getFeatureIdFromEvent
-     * 
-     * Parameters:
-     * evt - {Object} An <OpenLayers.Event> object
-     *
-     * Returns:
-     * {<OpenLayers.Geometry>} A geometry from an event that 
-     *     happened on a layer.
+     * Create empty functions for all easing methods.
      */
-    getFeatureIdFromEvent: function(evt) {
-        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
-        if(this.supportUse === false && !featureId) {
-            var target = evt.target;
-            featureId = target.parentNode && target != this.rendererRoot &&
-                target.parentNode._featureId;
-        }
-        return featureId;
-    },
-
-    CLASS_NAME: "OpenLayers.Renderer.SVG"
+    CLASS_NAME: "OpenLayers.Tween"
 });
 
 /**
- * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
- * {Object}
+ * Namespace: OpenLayers.Easing
+ * 
+ * Credits:
+ *      Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
  */
-OpenLayers.Renderer.SVG.LABEL_ALIGN = {
-    "l": "start",
-    "r": "end",
-    "b": "bottom",
-    "t": "hanging"
+OpenLayers.Easing = {
+    /**
+     * Create empty functions for all easing methods.
+     */
+    CLASS_NAME: "OpenLayers.Easing"
 };
 
 /**
- * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
- * {Object}
+ * Namespace: OpenLayers.Easing.Linear
  */
-OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
-    // according to
-    // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
-    // a baseline-shift of -70% shifts the text exactly from the
-    // bottom to the top of the baseline, so -35% moves the text to
-    // the center of the baseline.
-    "t": "-70%",
-    "b": "0"    
-};
-/* ======================================================================
-    OpenLayers/Renderer/VML.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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 (num | 0) to truncate 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",
+OpenLayers.Easing.Linear = {
     
     /**
-     * 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.
-     *
-     * 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();
-            var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; 
-            for (var i = 0, len = shapes.length; i < len; i++) {
-
-                style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
-                              "position: absolute; display: inline-block;");
-            }                  
-        }
-        
-        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
-                                                                arguments);
-    },
-
-    /**
-     * 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}
+     * Function: easeIn
      * 
-     * 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.
-     */
-    setExtent: function(extent, resolutionChanged) {
-        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
-                                                               arguments);
-        var resolution = this.getResolution();
-    
-        var left = (extent.left/resolution) | 0;
-        var top = (extent.top/resolution - this.size.h) | 0;
-        if (resolutionChanged || !this.offset) {
-            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.coordorigin = org;
-        var roots = [this.root, this.vectorRoot, this.textRoot];
-        var root;
-        for(var i=0, len=roots.length; i<len; ++i) {
-            root = roots[i];
-
-            var size = this.size.w + " " + this.size.h;
-            root.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;
-    },
-
-
-    /**
-     * Method: setSize
-     * Set the size of the drawing surface
-     *
      * Parameters:
-     * size - {<OpenLayers.Size>} the size of the drawing surface
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    setSize: function(size) {
-        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
-        
-        // setting width and height on all roots to avoid flicker which we
-        // would get with 100% width and height on child roots
-        var roots = [
-            this.rendererRoot,
-            this.root,
-            this.vectorRoot,
-            this.textRoot
-        ];
-        var w = this.size.w + "px";
-        var h = this.size.h + "px";
-        var root;
-        for(var i=0, len=roots.length; i<len; ++i) {
-            root = roots[i];
-            root.style.width = w;
-            root.style.height = h;
-        }
+    easeIn: function(t, b, c, d) {
+        return c*t/d + b;
     },
-
-    /**
-     * 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 fillColor = style.fillColor;
-
-        if (node._geometryClass === "OpenLayers.Geometry.Point") {
-            if (style.externalGraphic) {
-                if (style.graphicTitle) {
-                    node.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 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) | 0) + "px";
-                node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
-                node.style.width = width + "px";
-                node.style.height = height + "px";
-                node.style.flip = "y";
-                
-                // modify fillColor and options for stroke styling below
-                fillColor = "none";
-                options.isStroked = false;
-            } else if (this.isComplexSymbol(style.graphicName)) {
-                var cache = this.importSymbol(style.graphicName);
-                node.path = cache.path;
-                node.coordorigin = cache.left + "," + cache.bottom;
-                var size = cache.size;
-                node.coordsize = size + "," + size;        
-                this.drawCircle(node, geometry, style.pointRadius);
-                node.style.flip = "y";
-            } else {
-                this.drawCircle(node, geometry, style.pointRadius);
-            }
-        }
-
-        // fill 
-        if (options.isFilled) { 
-            node.fillcolor = fillColor; 
-        } else { 
-            node.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.opacity = style.fillOpacity;
-
-            if (node._geometryClass === "OpenLayers.Geometry.Point" &&
-                    style.externalGraphic) {
-
-                // override fillOpacity
-                if (style.graphicOpacity) {
-                    fill.opacity = style.graphicOpacity;
-                }
-                
-                fill.src = style.externalGraphic;
-                fill.type = "frame";
-                
-                if (!(style.graphicWidth && style.graphicHeight)) {
-                  fill.aspect = "atmost";
-                }                
-            }
-            if (fill.parentNode != node) {
-                node.appendChild(fill);
-            }
-        }
-
-        // additional rendering for rotated graphics or symbols
-        var rotation = style.rotation;
-        if ((rotation !== undefined || node._rotation !== undefined)) {
-            node._rotation = rotation;
-            if (style.externalGraphic) {
-                this.graphicRotate(node, xOffset, yOffset, style);
-                // 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.opacity = 0;
-            } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
-                node.style.rotation = rotation || 0;
-            }
-        }
-
-        // stroke 
-        var strokes = node.getElementsByTagName("stroke");
-        var stroke = (strokes.length == 0) ? null : strokes[0];
-        if (!options.isStroked) {
-            node.stroked = false;
-            if (stroke) {
-                stroke.on = false;
-            }
-        } else {
-            if (!stroke) {
-                stroke = this.createNode('olv:stroke', node.id + "_stroke");
-                node.appendChild(stroke);
-            }
-            stroke.on = true;
-            stroke.color = style.strokeColor; 
-            stroke.weight = style.strokeWidth + "px"; 
-            stroke.opacity = style.strokeOpacity;
-            stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
-                (style.strokeLinecap || 'round');
-            if (style.strokeDashstyle) {
-                stroke.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.
-     * 
-     * Parameters:
-     * node    - {DOMElement}
-     * xOffset - {Number} rotation center relative to image, x coordinate
-     * yOffset - {Number} rotation center relative to image, y coordinate
-     * style   - {Object}
-     */
-    graphicRotate: function(node, xOffset, yOffset, style) {
-        var style = style || node._style;
-        var rotation = style.rotation || 0;
-        
-        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, style);
-                }
-            }, 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 rot = rotation * Math.PI / 180;
-        var sintheta = Math.sin(rot);
-        var costheta = Math.cos(rot);
-
-        // 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
-     * Does some node postprocessing to work around browser issues:
-     * - 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.
-     * - In some cases, a node won't become visible after being drawn. Setting
-     *   style.visibility to "visible" works around that.
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     */
-    postDraw: function(node) {
-        node.style.visibility = "visible";
-        var fillColor = node._style.fillColor;
-        var strokeColor = node._style.strokeColor;
-        if (fillColor == "none" &&
-                node.fillcolor != fillColor) {
-            node.fillcolor = fillColor;
-        }
-        if (strokeColor == "none" &&
-                node.strokecolor != strokeColor) {
-            node.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) | 0,
-                                      (bbox.bottom/resolution - this.offset.y) | 0,
-                                      (bbox.right/resolution - this.offset.x) | 0,
-                                      (bbox.top/resolution - this.offset.y) | 0);
-            
-            // 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
-     */
-    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";
-        }
-    },
-
     /**
-     * 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.id = id;
-        }
-        
-        // IE hack to make elements unselectable, to prevent 'blue flash'
-        // while dragging vectors; #1410
-        node.unselectable = 'on';
-        node.onselectstart = OpenLayers.Function.False;
-        
-        return node;    
-    },
-    
-    /**
-     * Method: nodeTypeCompare
-     * Determine whether a node is of a given type
-     *
-     * Parameters:
-     * node - {DOMElement} An VML element
-     * type - {String} Kind of node
-     *
-     * Returns:
-     * {Boolean} Whether or not the specified node is of the specified type
-     */
-    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
+     * Function: easeOut
      * 
      * Parameters:
-     * suffix - {String} suffix to append to the id
-     *
-     * Returns:
-     * {DOMElement}
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    createRoot: function(suffix) {
-        return this.nodeFactory(this.container.id + suffix, "olv:group");
+    easeOut: function(t, b, c, d) {
+        return c*t/d + b;
     },
     
-    /**************************************
-     *                                    *
-     *     GEOMETRY DRAWING FUNCTIONS     *
-     *                                    *
-     **************************************/
-    
     /**
-     * Method: drawPoint
-     * Render a point
+     * Function: easeInOut
      * 
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement} or false if the point could not be drawn
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    drawPoint: function(node, geometry) {
-        return this.drawCircle(node, geometry, 1);
+    easeInOut: function(t, b, c, d) {
+        return c*t/d + b;
     },
 
-    /**
-     * 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();
+    CLASS_NAME: "OpenLayers.Easing.Linear"
+};
 
-            node.style.left = (((geometry.x /resolution - this.offset.x) | 0) - radius) + "px";
-            node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
+/**
+ * Namespace: OpenLayers.Easing.Expo
+ */
+OpenLayers.Easing.Expo = {
     
-            var diameter = radius * 2;
-            
-            node.style.width = diameter + "px";
-            node.style.height = diameter + "px";
-            return node;
-        }
-        return false;
-    },
-
-
     /**
-     * Method: drawLineString
-     * Render a linestring.
+     * Function: easeIn
      * 
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    drawLineString: function(node, geometry) {
-        return this.drawLine(node, geometry, false);
+    easeIn: function(t, b, c, d) {
+        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
     },
-
-    /**
-     * 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) | 0;
-            y = (comp.y/resolution - this.offset.y) | 0;
-            parts[i] = " " + x + "," + y + " 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) | 0;
-                y = (comp.y / resolution - this.offset.y) | 0;
-                path.push(" " + x + "," + y);
-                if (i==0) {
-                    path.push(" l");
-                }
-            }
-            path.push(" x ");
-        }
-        path.push("e");
-        node.path = path.join("");
-        return node;
-    },
-
     /**
-     * Method: drawRectangle
-     * Render a rectangle
+     * Function: easeOut
      * 
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    drawRectangle: function(node, geometry) {
-        var resolution = this.getResolution();
-    
-        node.style.left = ((geometry.x/resolution - this.offset.x) | 0) + "px";
-        node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
-        node.style.width = ((geometry.width/resolution) | 0) + "px";
-        node.style.height = ((geometry.height/resolution) | 0) + "px";
-        
-        return node;
+    easeOut: function(t, b, c, d) {
+        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
     },
     
     /**
-     * Method: drawText
-     * This method is only called by the renderer itself.
+     * Function: easeInOut
      * 
-     * Parameters: 
-     * featureId - {String}
-     * style -
-     * location - {<OpenLayers.Geometry.Point>}
-     */
-    drawText: function(featureId, style, location) {
-        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
-        var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
-        
-        var resolution = this.getResolution();
-        label.style.left = ((location.x/resolution - this.offset.x) | 0) + "px";
-        label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
-        label.style.flip = "y";
-
-        textbox.innerText = style.label;
-
-        if (style.fontColor) {
-            textbox.style.color = style.fontColor;
-        }
-        if (style.fontOpacity) {
-            textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
-        }
-        if (style.fontFamily) {
-            textbox.style.fontFamily = style.fontFamily;
-        }
-        if (style.fontSize) {
-            textbox.style.fontSize = style.fontSize;
-        }
-        if (style.fontWeight) {
-            textbox.style.fontWeight = style.fontWeight;
-        }
-        if(style.labelSelect === true) {
-            label._featureId = featureId;
-            textbox._featureId = featureId;
-            textbox._geometry = location;
-            textbox._geometryClass = location.CLASS_NAME;
-        }
-        textbox.style.whiteSpace = "nowrap";
-        // fun with IE: IE7 in standards compliant mode does not display any
-        // text with a left inset of 0. So we set this to 1px and subtract one
-        // pixel later when we set label.style.left
-        textbox.inset = "1px,0px,0px,0px";
-
-        if(!label.parentNode) {
-            label.appendChild(textbox);
-            this.textRoot.appendChild(label);
-        }
-
-        var align = style.labelAlign || "cm";
-        if (align.length == 1) {
-            align += "m";
-        }
-        var xshift = textbox.clientWidth *
-            (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
-        var yshift = textbox.clientHeight *
-            (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
-        label.style.left = parseInt(label.style.left)-xshift-1+"px";
-        label.style.top = parseInt(label.style.top)+yshift+"px";
-        
-    },
-
-    /**
-     * Method: drawSurface
-     * 
      * Parameters:
-     * node - {DOMElement}
-     * geometry - {<OpenLayers.Geometry>}
-     * 
-     * Returns:
-     * {DOMElement}
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    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) | 0;
-            y = (comp.y / resolution - this.offset.y) | 0;
-            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;
+    easeInOut: function(t, b, c, d) {
+        if (t==0) return b;
+        if (t==d) return b+c;
+        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
     },
-    
-    /**
-     * Method: moveRoot
-     * moves this renderer's root to a different renderer.
-     * 
-     * Parameters:
-     * 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} true if successful, false otherwise
-     */
-    moveRoot: function(renderer) {
-        var layer = this.map.getLayer(renderer.container.id);
-        if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
-            layer = this.map.getLayer(this.container.id);
-        }
-        layer && layer.renderer.clear();
-        OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
-        layer && layer.redraw();
-    },
-    
-    /**
-     * 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');
-        }
 
-        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) {
-            var x = symbol[i];
-            var 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(" ");
-
-        var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
-        if(diff > 0) {
-            symbolExtent.bottom = symbolExtent.bottom - diff;
-            symbolExtent.top = symbolExtent.top + diff;
-        } else {
-            symbolExtent.left = symbolExtent.left + diff;
-            symbolExtent.right = symbolExtent.right - diff;
-        }
-        
-        cache = {
-            path: path,
-            size: symbolExtent.getWidth(), // equals getHeight() now
-            left: symbolExtent.left,
-            bottom: symbolExtent.bottom
-        };
-        this.symbolCache[id] = cache;
-        
-        return cache;
-    },
-    
-    CLASS_NAME: "OpenLayers.Renderer.VML"
-});
-
-/**
- * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
- * {Object}
- */
-OpenLayers.Renderer.VML.LABEL_SHIFT = {
-    "l": 0,
-    "c": .5,
-    "r": 1,
-    "t": 0,
-    "m": .5,
-    "b": 1
+    CLASS_NAME: "OpenLayers.Easing.Expo"
 };
-/* ======================================================================
-    OpenLayers/Tile.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/*
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Console.js
- */
-
-/*
- * Class: OpenLayers.Tile 
- * This is a class designed to designate a single tile, however
- *     it is explicitly designed to do relatively little. Tiles store 
- *     information about themselves -- such as the URL that they are related
- *     to, and their size - but do not add themselves to the layer div 
- *     automatically, for example. Create a new tile with the 
- *     <OpenLayers.Tile> constructor, or a subclass. 
- * 
- * TBD 3.0 - remove reference to url in above paragraph
- * 
- */
-OpenLayers.Tile = OpenLayers.Class({
-    
-    /** 
-     * Constant: EVENT_TYPES
-     * {Array(String)} Supported application event types
-     */
-    EVENT_TYPES: [ "loadstart", "loadend", "reload", "unload"],
-    
-    /**
-     * APIProperty: events
-     * {<OpenLayers.Events>} An events object that handles all 
-     *                       events on the tile.
-     */
-    events: null,
-
-    /**
-     * Property: id 
-     * {String} null
-     */
-    id: null,
-    
-    /** 
-     * Property: layer 
-     * {<OpenLayers.Layer>} layer the tile is attached to 
-     */
-    layer: null,
-    
-    /**
-     * Property: url
-     * {String} url of the request.
-     *
-     * TBD 3.0 
-     * Deprecated. The base tile class does not need an url. This should be 
-     * handled in subclasses. Does not belong here.
-     */
-    url: null,
-
-    /** 
-     * APIProperty: bounds 
-     * {<OpenLayers.Bounds>} null
-     */
-    bounds: null,
-    
-    /** 
-     * Property: size 
-     * {<OpenLayers.Size>} null
-     */
-    size: null,
-    
-    /** 
-     * Property: position 
-     * {<OpenLayers.Pixel>} Top Left pixel of the tile
-     */    
-    position: null,
-
-    /**
-     * Property: isLoading
-     * {Boolean} Is the tile loading?
-     */
-    isLoading: false,
-        
-    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
-     *             there is no need for the base tile class to have a url.
-     * 
-     * Constructor: OpenLayers.Tile
-     * Constructor for a new <OpenLayers.Tile> instance.
-     * 
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
-     * position - {<OpenLayers.Pixel>}
-     * bounds - {<OpenLayers.Bounds>}
-     * url - {<String>}
-     * size - {<OpenLayers.Size>}
-     */   
-    initialize: function(layer, position, bounds, url, size) {
-        this.layer = layer;
-        this.position = position.clone();
-        this.bounds = bounds.clone();
-        this.url = url;
-        this.size = size.clone();
-
-        //give the tile a unique id based on its BBOX.
-        this.id = OpenLayers.Util.createUniqueID("Tile_");
-        
-        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
-    },
-
-    /**
-     * Method: unload
-     * Call immediately before destroying if you are listening to tile
-     * events, so that counters are properly handled if tile is still
-     * loading at destroy-time. Will only fire an event if the tile is
-     * still loading.
-     */
-    unload: function() {
-       if (this.isLoading) { 
-           this.isLoading = false; 
-           this.events.triggerEvent("unload"); 
-       }
-    },
-    
-    /** 
-     * APIMethod: destroy
-     * Nullify references to prevent circular references and memory leaks.
-     */
-    destroy:function() {
-        this.layer  = null;
-        this.bounds = null;
-        this.size = null;
-        this.position = null;
-        
-        this.events.destroy();
-        this.events = null;
-    },
-    
-    /**
-     * Method: clone
-     *
-     * Parameters:
-     * obj - {<OpenLayers.Tile>} The tile to be cloned
-     *
-     * Returns:
-     * {<OpenLayers.Tile>} An exact clone of this <OpenLayers.Tile>
-     */
-    clone: function (obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Tile(this.layer, 
-                                      this.position, 
-                                      this.bounds, 
-                                      this.url, 
-                                      this.size);
-        } 
-        
-        // catch any randomly tagged-on properties
-        OpenLayers.Util.applyDefaults(obj, this);
-        
-        return obj;
-    },
-
-    /**
-     * Method: draw
-     * Clear whatever is currently in the tile, then return whether or not 
-     *     it should actually be re-drawn.
-     * 
-     * Returns:
-     * {Boolean} Whether or not the tile should actually be drawn. Note that 
-     *     this is not really the best way of doing things, but such is 
-     *     the way the code has been developed. Subclasses call this and
-     *     depend on the return to know if they should draw or not.
-     */
-    draw: function() {
-        var maxExtent = this.layer.maxExtent;
-        var withinMaxExtent = (maxExtent &&
-                               this.bounds.intersectsBounds(maxExtent, false));
- 
-        // The only case where we *wouldn't* want to draw the tile is if the 
-        // tile is outside its layer's maxExtent.
-        this.shouldDraw = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
-                
-        //clear tile's contents and mark as not drawn
-        this.clear();
-        
-        return this.shouldDraw;
-    },
-    
-    /** 
-     * Method: moveTo
-     * Reposition the tile.
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * redraw - {Boolean} Call draw method on tile after moving.
-     *     Default is true
-     */
-    moveTo: function (bounds, position, redraw) {
-        if (redraw == null) {
-            redraw = true;
-        }
-
-        this.bounds = bounds.clone();
-        this.position = position.clone();
-        if (redraw) {
-            this.draw();
-        }
-    },
-
-    /** 
-     * Method: clear
-     * Clear the tile of any bounds/position-related data so that it can 
-     *     be reused in a new location. To be implemented by subclasses.
-     */
-    clear: function() {
-        // to be implemented by subclasses
-    },
-    
-    /**   
-     * Method: getBoundsFromBaseLayer
-     * Take the pixel locations of the corner of the tile, and pass them to 
-     *     the base layer and ask for the location of those pixels, so that 
-     *     displaying tiles over Google works fine.
-     *
-     * Parameters:
-     * position - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * bounds - {<OpenLayers.Bounds>} 
-     */
-    getBoundsFromBaseLayer: function(position) {
-        var msg = OpenLayers.i18n('reprojectDeprecated',
-                                              {'layerName':this.layer.name});
-        OpenLayers.Console.warn(msg);
-        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
-        var bottomRightPx = position.clone();
-        bottomRightPx.x += this.size.w;
-        bottomRightPx.y += this.size.h;
-        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
-        // Handle the case where the base layer wraps around the date line.
-        // Google does this, and it breaks WMS servers to request bounds in 
-        // that fashion.  
-        if (topLeft.lon > bottomRight.lon) {
-            if (topLeft.lon < 0) {
-                topLeft.lon = -180 - (topLeft.lon+180);
-            } else {
-                bottomRight.lon = 180+bottomRight.lon+180;
-            }        
-        }
-        var bounds = new OpenLayers.Bounds(topLeft.lon, 
-                                       bottomRight.lat, 
-                                       bottomRight.lon, 
-                                       topLeft.lat);  
-        return bounds;
-    },        
-        
-    /** 
-     * Method: showTile
-     * Show the tile only if it should be drawn.
-     */
-    showTile: function() { 
-        if (this.shouldDraw) {
-            this.show();
-        }
-    },
-    
-    /** 
-     * Method: show
-     * Show the tile.  To be implemented by subclasses.
-     */
-    show: function() { },
-    
-    /** 
-     * Method: hide
-     * Hide the tile.  To be implemented by subclasses.
-     */
-    hide: function() { },
-    
-    CLASS_NAME: "OpenLayers.Tile"
-});
-/* ======================================================================
-    OpenLayers/Format/XML.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Format.js
+ * Namespace: OpenLayers.Easing.Quad
  */
-
-/**
- * Class: OpenLayers.Format.XML
- * Read and write XML.  For cross-browser XML generation, use methods on an
- *     instance of the XML format class instead of on <code>document<end>.
- *     The DOM creation and traversing methods exposed here all mimic the
- *     W3C XML DOM methods.  Create a new parser with the
- *     <OpenLayers.Format.XML> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Format>
- */
-OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
+OpenLayers.Easing.Quad = {
     
     /**
-     * Property: namespaces
-     * {Object} Mapping of namespace aliases to namespace URIs.  Properties
-     *     of this object should not be set individually.  Read-only.  All
-     *     XML subclasses should have their own namespaces object.  Use
-     *     <setNamespace> to add or set a namespace alias after construction.
-     */
-    namespaces: null,
-    
-    /**
-     * Property: namespaceAlias
-     * {Object} Mapping of namespace URI to namespace alias.  This object
-     *     is read-only.  Use <setNamespace> to add or set a namespace alias.
-     */
-    namespaceAlias: null,
-    
-    /**
-     * Property: defaultPrefix
-     * {String} The default namespace alias for creating element nodes.
-     */
-    defaultPrefix: null,
-    
-    /**
-     * Property: readers
-     * Contains public functions, grouped by namespace prefix, that will
-     *     be applied when a namespaced node is found matching the function
-     *     name.  The function will be applied in the scope of this parser
-     *     with two arguments: the node being read and a context object passed
-     *     from the parent.
-     */
-    readers: {},
-    
-    /**
-     * Property: writers
-     * As a compliment to the <readers> property, this structure contains public
-     *     writing functions grouped by namespace alias and named like the
-     *     node names they produce.
-     */
-    writers: {},
-
-    /**
-     * Property: xmldom
-     * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
-     *     object.  It is not intended to be a browser sniffing property.
-     *     Instead, the xmldom property is used instead of <code>document<end>
-     *     where namespaced node creation methods are not supported. In all
-     *     other browsers, this remains null.
-     */
-    xmldom: null,
-
-    /**
-     * Constructor: OpenLayers.Format.XML
-     * Construct an XML parser.  The parser is used to read and write XML.
-     *     Reading XML from a string returns a DOM element.  Writing XML from
-     *     a DOM element returns a string.
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on
-     *     the object.
-     */
-    initialize: function(options) {
-        if(window.ActiveXObject) {
-            this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
-        }
-        OpenLayers.Format.prototype.initialize.apply(this, [options]);
-        // clone the namespace object and set all namespace aliases
-        this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
-        this.namespaceAlias = {};
-        for(var alias in this.namespaces) {
-            this.namespaceAlias[this.namespaces[alias]] = alias;
-        }
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Clean up.
-     */
-    destroy: function() {
-        this.xmldom = null;
-        OpenLayers.Format.prototype.destroy.apply(this, arguments);
-    },
-    
-    /**
-     * Method: setNamespace
-     * Set a namespace alias and URI for the format.
-     *
-     * Parameters:
-     * alias - {String} The namespace alias (prefix).
-     * uri - {String} The namespace URI.
-     */
-    setNamespace: function(alias, uri) {
-        this.namespaces[alias] = uri;
-        this.namespaceAlias[uri] = alias;
-    },
-
-    /**
-     * APIMethod: read
-     * Deserialize a XML string and return a DOM node.
-     *
-     * Parameters:
-     * text - {String} A XML string
-     
-     * Returns:
-     * {DOMElement} A DOM node
-     */
-    read: function(text) {
-        var index = text.indexOf('<');
-        if(index > 0) {
-            text = text.substring(index);
-        }
-        var node = OpenLayers.Util.Try(
-            OpenLayers.Function.bind((
-                function() {
-                    var xmldom;
-                    /**
-                     * Since we want to be able to call this method on the prototype
-                     * itself, this.xmldom may not exist even if in IE.
-                     */
-                    if(window.ActiveXObject && !this.xmldom) {
-                        xmldom = new ActiveXObject("Microsoft.XMLDOM");
-                    } else {
-                        xmldom = this.xmldom;
-                        
-                    }
-                    xmldom.loadXML(text);
-                    return xmldom;
-                }
-            ), this),
-            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;
-            }
-        );
-
-        if(this.keepData) {
-            this.data = node;
-        }
-
-        return node;
-    },
-
-    /**
-     * APIMethod: write
-     * Serialize a DOM node into a XML string.
+     * Function: easeIn
      * 
      * Parameters:
-     * node - {DOMElement} A DOM node.
-     *
-     * Returns:
-     * {String} The XML string representation of the input node.
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    write: function(node) {
-        var data;
-        if(this.xmldom) {
-            data = node.xml;
-        } else {
-            var serializer = new XMLSerializer();
-            if (node.nodeType == 1) {
-                // Add nodes to a document before serializing. Everything else
-                // is serialized as is. This may need more work. See #1218 .
-                var doc = document.implementation.createDocument("", "", null);
-                if (doc.importNode) {
-                    node = doc.importNode(node, true);
-                }
-                doc.appendChild(node);
-                data = serializer.serializeToString(doc);
-            } else {
-                data = serializer.serializeToString(node);
-            }
-        }
-        return data;
+    easeIn: function(t, b, c, d) {
+        return c*(t/=d)*t + b;
     },
-
-    /**
-     * APIMethod: createElementNS
-     * Create a new element with namespace.  This node can be appended to
-     *     another node with the standard node.appendChild method.  For
-     *     cross-browser support, this method must be used instead of
-     *     document.createElementNS.
-     *
-     * Parameters:
-     * uri - {String} Namespace URI for the element.
-     * name - {String} The qualified name of the element (prefix:localname).
-     * 
-     * Returns:
-     * {Element} A DOM element with namespace.
-     */
-    createElementNS: function(uri, name) {
-        var element;
-        if(this.xmldom) {
-            if(typeof uri == "string") {
-                element = this.xmldom.createNode(1, name, uri);
-            } else {
-                element = this.xmldom.createNode(1, name, "");
-            }
-        } else {
-            element = document.createElementNS(uri, name);
-        }
-        return element;
-    },
-
-    /**
-     * APIMethod: createTextNode
-     * Create a text node.  This node can be appended to another node with
-     *     the standard node.appendChild method.  For cross-browser support,
-     *     this method must be used instead of document.createTextNode.
-     * 
-     * Parameters:
-     * text - {String} The text of the node.
-     * 
-     * Returns: 
-     * {DOMElement} A DOM text node.
-     */
-    createTextNode: function(text) {
-        var node;
-        if (typeof text !== "string") {
-            text = String(text);
-        }
-        if(this.xmldom) {
-            node = this.xmldom.createTextNode(text);
-        } else {
-            node = document.createTextNode(text);
-        }
-        return node;
-    },
-
-    /**
-     * APIMethod: getElementsByTagNameNS
-     * Get a list of elements on a node given the namespace URI and local name.
-     *     To return all nodes in a given namespace, use '*' for the name
-     *     argument.  To return all nodes of a given (local) name, regardless
-     *     of namespace, use '*' for the uri argument.
-     * 
-     * Parameters:
-     * node - {Element} Node on which to search for other nodes.
-     * uri - {String} Namespace URI.
-     * name - {String} Local name of the tag (without the prefix).
-     * 
-     * Returns:
-     * {NodeList} A node list or array of elements.
-     */
-    getElementsByTagNameNS: function(node, uri, name) {
-        var elements = [];
-        if(node.getElementsByTagNameNS) {
-            elements = node.getElementsByTagNameNS(uri, name);
-        } else {
-            // brute force method
-            var allNodes = node.getElementsByTagName("*");
-            var potentialNode, fullName;
-            for(var i=0, len=allNodes.length; i<len; ++i) {
-                potentialNode = allNodes[i];
-                fullName = (potentialNode.prefix) ?
-                           (potentialNode.prefix + ":" + name) : name;
-                if((name == "*") || (fullName == potentialNode.nodeName)) {
-                    if((uri == "*") || (uri == potentialNode.namespaceURI)) {
-                        elements.push(potentialNode);
-                    }
-                }
-            }
-        }
-        return elements;
-    },
-
-    /**
-     * APIMethod: getAttributeNodeNS
-     * Get an attribute node given the namespace URI and local name.
-     * 
-     * Parameters:
-     * node - {Element} Node on which to search for attribute nodes.
-     * uri - {String} Namespace URI.
-     * name - {String} Local name of the attribute (without the prefix).
-     * 
-     * Returns:
-     * {DOMElement} An attribute node or null if none found.
-     */
-    getAttributeNodeNS: function(node, uri, name) {
-        var attributeNode = null;
-        if(node.getAttributeNodeNS) {
-            attributeNode = node.getAttributeNodeNS(uri, name);
-        } else {
-            var attributes = node.attributes;
-            var potentialNode, fullName;
-            for(var i=0, len=attributes.length; i<len; ++i) {
-                potentialNode = attributes[i];
-                if(potentialNode.namespaceURI == uri) {
-                    fullName = (potentialNode.prefix) ?
-                               (potentialNode.prefix + ":" + name) : name;
-                    if(fullName == potentialNode.nodeName) {
-                        attributeNode = potentialNode;
-                        break;
-                    }
-                }
-            }
-        }
-        return attributeNode;
-    },
-
-    /**
-     * APIMethod: getAttributeNS
-     * Get an attribute value given the namespace URI and local name.
-     * 
-     * Parameters:
-     * node - {Element} Node on which to search for an attribute.
-     * uri - {String} Namespace URI.
-     * name - {String} Local name of the attribute (without the prefix).
-     * 
-     * Returns:
-     * {String} An attribute value or and empty string if none found.
-     */
-    getAttributeNS: function(node, uri, name) {
-        var attributeValue = "";
-        if(node.getAttributeNS) {
-            attributeValue = node.getAttributeNS(uri, name) || "";
-        } else {
-            var attributeNode = this.getAttributeNodeNS(node, uri, name);
-            if(attributeNode) {
-                attributeValue = attributeNode.nodeValue;
-            }
-        }
-        return attributeValue;
-    },
     
     /**
-     * APIMethod: getChildValue
-     * Get the textual value of the node if it exists, or return an
-     *     optional default string.  Returns an empty string if no first child
-     *     exists and no default value is supplied.
-     *
-     * Parameters:
-     * node - {DOMElement} The element used to look for a first child value.
-     * def - {String} Optional string to return in the event that no
-     *     first child value exists.
-     *
-     * Returns:
-     * {String} The value of the first child of the given node.
-     */
-    getChildValue: function(node, def) {
-        var value = def || "";
-        if(node) {
-            for(var child=node.firstChild; child; child=child.nextSibling) {
-                switch(child.nodeType) {
-                    case 3: // text node
-                    case 4: // cdata section
-                        value += child.nodeValue;
-                }
-            }
-        }
-        return value;
-    },
-
-    /**
-     * APIMethod: concatChildValues
-     * *Deprecated*. Use <getChildValue> instead.
-     *
-     * Concatenate the value of all child nodes if any exist, or return an
-     *     optional default string.  Returns an empty string if no children
-     *     exist and no default value is supplied.  Not optimized for large
-     *     numbers of child nodes.
-     *
-     * Parameters:
-     * node - {DOMElement} The element used to look for child values.
-     * def - {String} Optional string to return in the event that no
-     *     child exist.
-     *
-     * Returns:
-     * {String} The concatenated value of all child nodes of the given node.
-     */
-    concatChildValues: function(node, def) {
-        var value = "";
-        var child = node.firstChild;
-        var childValue;
-        while(child) {
-            childValue = child.nodeValue;
-            if(childValue) {
-                value += childValue;
-            }
-            child = child.nextSibling;
-        }
-        if(value == "" && def != undefined) {
-            value = def;
-        }
-        return value;
-    },
-    
-    /**
-     * APIMethod: isSimpleContent
-     * Test if the given node has only simple content (i.e. no child element
-     *     nodes).
-     *
-     * Parameters:
-     * node - {DOMElement} An element node.
-     *
-     * Returns:
-     * {Boolean} The node has no child element nodes (nodes of type 1). 
-     */
-    isSimpleContent: function(node) {
-        var simple = true;
-        for(var child=node.firstChild; child; child=child.nextSibling) {
-            if(child.nodeType === 1) {
-                simple = false;
-                break;
-            }
-        }
-        return simple;
-    },
-    
-    /**
-     * APIMethod: contentType
-     * Determine the content type for a given node.
-     *
-     * Parameters:
-     * node - {DOMElement}
-     *
-     * Returns:
-     * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
-     *     if the node has no, simple, complex, or mixed content.
-     */
-    contentType: function(node) {
-        var simple = false,
-            complex = false;
-            
-        var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
-
-        for(var child=node.firstChild; child; child=child.nextSibling) {
-            switch(child.nodeType) {
-                case 1: // element
-                    complex = true;
-                    break;
-                case 8: // comment
-                    break;
-                default:
-                    simple = true;
-            }
-            if(complex && simple) {
-                break;
-            }
-        }
-        
-        if(complex && simple) {
-            type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
-        } else if(complex) {
-            return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
-        } else if(simple) {
-            return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
-        }
-        return type;
-    },
-
-    /**
-     * APIMethod: hasAttributeNS
-     * Determine whether a node has a particular attribute matching the given
-     *     name and namespace.
+     * Function: easeOut
      * 
      * Parameters:
-     * node - {Element} Node on which to search for an attribute.
-     * uri - {String} Namespace URI.
-     * name - {String} Local name of the attribute (without the prefix).
-     * 
-     * Returns:
-     * {Boolean} The node has an attribute matching the name and namespace.
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    hasAttributeNS: function(node, uri, name) {
-        var found = false;
-        if(node.hasAttributeNS) {
-            found = node.hasAttributeNS(uri, name);
-        } else {
-            found = !!this.getAttributeNodeNS(node, uri, name);
-        }
-        return found;
+    easeOut: function(t, b, c, d) {
+        return -c *(t/=d)*(t-2) + b;
     },
     
     /**
-     * APIMethod: setAttributeNS
-     * Adds a new attribute or changes the value of an attribute with the given
-     *     namespace and name.
-     *
-     * Parameters:
-     * node - {Element} Element node on which to set the attribute.
-     * uri - {String} Namespace URI for the attribute.
-     * name - {String} Qualified name (prefix:localname) for the attribute.
-     * value - {String} Attribute value.
-     */
-    setAttributeNS: function(node, uri, name, value) {
-        if(node.setAttributeNS) {
-            node.setAttributeNS(uri, name, value);
-        } else {
-            if(this.xmldom) {
-                if(uri) {
-                    var attribute = node.ownerDocument.createNode(
-                        2, name, uri
-                    );
-                    attribute.nodeValue = value;
-                    node.setAttributeNode(attribute);
-                } else {
-                    node.setAttribute(name, value);
-                }
-            } else {
-                throw "setAttributeNS not implemented";
-            }
-        }
-    },
-
-    /**
-     * Method: createElementNSPlus
-     * Shorthand for creating namespaced elements with optional attributes and
-     *     child text nodes.
-     *
-     * Parameters:
-     * name - {String} The qualified node name.
-     * options - {Object} Optional object for node configuration.
-     *
-     * Valid options:
-     * uri - {String} Optional namespace uri for the element - supply a prefix
-     *     instead if the namespace uri is a property of the format's namespace
-     *     object.
-     * attributes - {Object} Optional attributes to be set using the
-     *     <setAttributes> method.
-     * value - {String} Optional text to be appended as a text node.
-     *
-     * Returns:
-     * {Element} An element node.
-     */
-    createElementNSPlus: function(name, options) {
-        options = options || {};
-        // order of prefix preference
-        // 1. in the uri option
-        // 2. in the prefix option
-        // 3. in the qualified name
-        // 4. from the defaultPrefix
-        var uri = options.uri || this.namespaces[options.prefix];
-        if(!uri) {
-            var loc = name.indexOf(":");
-            uri = this.namespaces[name.substring(0, loc)];
-        }
-        if(!uri) {
-            uri = this.namespaces[this.defaultPrefix];
-        }
-        var node = this.createElementNS(uri, name);
-        if(options.attributes) {
-            this.setAttributes(node, options.attributes);
-        }
-        var value = options.value;
-        if(value != null) {
-            node.appendChild(this.createTextNode(value));
-        }
-        return node;
-    },
-    
-    /**
-     * Method: setAttributes
-     * Set multiple attributes given key value pairs from an object.
-     *
-     * Parameters:
-     * node - {Element} An element node.
-     * obj - {Object || Array} An object whose properties represent attribute
-     *     names and values represent attribute values.  If an attribute name
-     *     is a qualified name ("prefix:local"), the prefix will be looked up
-     *     in the parsers {namespaces} object.  If the prefix is found,
-     *     setAttributeNS will be used instead of setAttribute.
-     */
-    setAttributes: function(node, obj) {
-        var value, uri;
-        for(var name in obj) {
-            if(obj[name] != null && obj[name].toString) {
-                value = obj[name].toString();
-                // check for qualified attribute name ("prefix:local")
-                uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
-                this.setAttributeNS(node, uri, name, value);
-            }
-        }
-    },
-
-    /**
-     * Method: readNode
-     * Shorthand for applying one of the named readers given the node
-     *     namespace and local name.  Readers take two args (node, obj) and
-     *     generally extend or modify the second.
-     *
-     * Parameters:
-     * node - {DOMElement} The node to be read (required).
-     * obj - {Object} The object to be modified (optional).
-     *
-     * Returns:
-     * {Object} The input object, modified (or a new one if none was provided).
-     */
-    readNode: function(node, obj) {
-        if(!obj) {
-            obj = {};
-        }
-        var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
-        if(group) {
-            var local = node.localName || node.nodeName.split(":").pop();
-            var reader = group[local] || group["*"];
-            if(reader) {
-                reader.apply(this, [node, obj]);
-            }
-        }
-        return obj;
-    },
-
-    /**
-     * Method: readChildNodes
-     * Shorthand for applying the named readers to all children of a node.
-     *     For each child of type 1 (element), <readSelf> is called.
-     *
-     * Parameters:
-     * node - {DOMElement} The node to be read (required).
-     * obj - {Object} The object to be modified (optional).
-     *
-     * Returns:
-     * {Object} The input object, modified.
-     */
-    readChildNodes: function(node, obj) {
-        if(!obj) {
-            obj = {};
-        }
-        var children = node.childNodes;
-        var child;
-        for(var i=0, len=children.length; i<len; ++i) {
-            child = children[i];
-            if(child.nodeType == 1) {
-                this.readNode(child, obj);
-            }
-        }
-        return obj;
-    },
-
-    /**
-     * Method: writeNode
-     * Shorthand for applying one of the named writers and appending the
-     *     results to a node.  If a qualified name is not provided for the
-     *     second argument (and a local name is used instead), the namespace
-     *     of the parent node will be assumed.
-     *
-     * Parameters:
-     * name - {String} The name of a node to generate.  If a qualified name
-     *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be
-     *     in the <writers> group.  If a local name is used (e.g. "Name") then
-     *     the namespace of the parent is assumed.  If a local name is used
-     *     and no parent is supplied, then the default namespace is assumed.
-     * obj - {Object} Structure containing data for the writer.
-     * parent - {DOMElement} Result will be appended to this node.  If no parent
-     *     is supplied, the node will not be appended to anything.
-     *
-     * Returns:
-     * {DOMElement} The child node.
-     */
-    writeNode: function(name, obj, parent) {
-        var prefix, local;
-        var split = name.indexOf(":");
-        if(split > 0) {
-            prefix = name.substring(0, split);
-            local = name.substring(split + 1);
-        } else {
-            if(parent) {
-                prefix = this.namespaceAlias[parent.namespaceURI];
-            } else {
-                prefix = this.defaultPrefix;
-            }
-            local = name;
-        }
-        var child = this.writers[prefix][local].apply(this, [obj]);
-        if(parent) {
-            parent.appendChild(child);
-        }
-        return child;
-    },
-
-    /**
-     * APIMethod: getChildEl
-     * Get the first child element.  Optionally only return the first child
-     *     if it matches the given name and namespace URI.
-     *
-     * Parameters:
-     * node - {DOMElement} The parent node.
-     * name - {String} Optional node name (local) to search for.
-     * uri - {String} Optional namespace URI to search for.
-     *
-     * Returns:
-     * {DOMElement} The first child.  Returns null if no element is found, if
-     *     something significant besides an element is found, or if the element
-     *     found does not match the optional name and uri.
-     */
-    getChildEl: function(node, name, uri) {
-        return node && this.getThisOrNextEl(node.firstChild, name, uri);
-    },
-    
-    /**
-     * APIMethod: getNextEl
-     * Get the next sibling element.  Optionally get the first sibling only
-     *     if it matches the given local name and namespace URI.
-     *
-     * Parameters:
-     * node - {DOMElement} The node.
-     * name - {String} Optional local name of the sibling to search for.
-     * uri - {String} Optional namespace URI of the sibling to search for.
-     *
-     * Returns:
-     * {DOMElement} The next sibling element.  Returns null if no element is
-     *     found, something significant besides an element is found, or the
-     *     found element does not match the optional name and uri.
-     */
-    getNextEl: function(node, name, uri) {
-        return node && this.getThisOrNextEl(node.nextSibling, name, uri);
-    },
-    
-    /**
-     * Method: getThisOrNextEl
-     * Return this node or the next element node.  Optionally get the first
-     *     sibling with the given local name or namespace URI.
-     *
-     * Parameters:
-     * node - {DOMElement} The node.
-     * name - {String} Optional local name of the sibling to search for.
-     * uri - {String} Optional namespace URI of the sibling to search for.
-     *
-     * Returns:
-     * {DOMElement} The next sibling element.  Returns null if no element is
-     *     found, something significant besides an element is found, or the
-     *     found element does not match the query.
-     */
-    getThisOrNextEl: function(node, name, uri) {
-        outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
-            switch(sibling.nodeType) {
-                case 1: // Element
-                    if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
-                       (!uri || uri === sibling.namespaceURI)) {
-                        // matches
-                        break outer;
-                    }
-                    sibling = null;
-                    break outer;
-                case 3: // Text
-                    if(/^\s*$/.test(sibling.nodeValue)) {
-                        break;
-                    }
-                case 4: // CDATA
-                case 6: // ENTITY_NODE
-                case 12: // NOTATION_NODE
-                case 10: // DOCUMENT_TYPE_NODE
-                case 11: // DOCUMENT_FRAGMENT_NODE
-                    sibling = null;
-                    break outer;
-            } // ignore comments and processing instructions
-        }
-        return sibling || null;
-    },
-    
-    /**
-     * APIMethod: lookupNamespaceURI
-     * Takes a prefix and returns the namespace URI associated with it on the given
-     *     node if found (and null if not). Supplying null for the prefix will
-     *     return the default namespace.
-     *
-     * For browsers that support it, this calls the native lookupNamesapceURI
-     *     function.  In other browsers, this is an implementation of
-     *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
-     *
-     * For browsers that don't support the attribute.ownerElement property, this
-     *     method cannot be called on attribute nodes.
-     *     
-     * Parameters:
-     * node - {DOMElement} The node from which to start looking.
-     * prefix - {String} The prefix to lookup or null to lookup the default namespace.
+     * Function: easeInOut
      * 
-     * Returns:
-     * {String} The namespace URI for the given prefix.  Returns null if the prefix
-     *     cannot be found or the node is the wrong type.
-     */
-    lookupNamespaceURI: function(node, prefix) {
-        var uri = null;
-        if(node) {
-            if(node.lookupNamespaceURI) {
-                uri = node.lookupNamespaceURI(prefix);
-            } else {
-                outer: switch(node.nodeType) {
-                    case 1: // ELEMENT_NODE
-                        if(node.namespaceURI !== null && node.prefix === prefix) {
-                            uri = node.namespaceURI;
-                            break outer;
-                        }
-                        var len = node.attributes.length;
-                        if(len) {
-                            var attr;
-                            for(var i=0; i<len; ++i) {
-                                attr = node.attributes[i];
-                                if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
-                                    uri = attr.value || null;
-                                    break outer;
-                                } else if(attr.name === "xmlns" && prefix === null) {
-                                    uri = attr.value || null;
-                                    break outer;
-                                }
-                            }
-                        }
-                        uri = this.lookupNamespaceURI(node.parentNode, prefix);
-                        break outer;
-                    case 2: // ATTRIBUTE_NODE
-                        uri = this.lookupNamespaceURI(node.ownerElement, prefix);
-                        break outer;
-                    case 9: // DOCUMENT_NODE
-                        uri = this.lookupNamespaceURI(node.documentElement, prefix);
-                        break outer;
-                    case 6: // ENTITY_NODE
-                    case 12: // NOTATION_NODE
-                    case 10: // DOCUMENT_TYPE_NODE
-                    case 11: // DOCUMENT_FRAGMENT_NODE
-                        break outer;
-                    default: 
-                        // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
-                        // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
-                        uri =  this.lookupNamespaceURI(node.parentNode, prefix);
-                        break outer;
-                }
-            }
-        }
-        return uri;
-    },
-    
-    CLASS_NAME: "OpenLayers.Format.XML" 
-
-});     
-
-OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
-
-/**
- * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
- * Takes a prefix and returns the namespace URI associated with it on the given
- *     node if found (and null if not). Supplying null for the prefix will
- *     return the default namespace.
- *
- * For browsers that support it, this calls the native lookupNamesapceURI
- *     function.  In other browsers, this is an implementation of
- *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
- *
- * For browsers that don't support the attribute.ownerElement property, this
- *     method cannot be called on attribute nodes.
- *     
- * Parameters:
- * node - {DOMElement} The node from which to start looking.
- * prefix - {String} The prefix to lookup or null to lookup the default namespace.
- * 
- * Returns:
- * {String} The namespace URI for the given prefix.  Returns null if the prefix
- *     cannot be found or the node is the wrong type.
- */
-OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
-    OpenLayers.Format.XML.prototype.lookupNamespaceURI,
-    OpenLayers.Format.XML.prototype
-);
-/* ======================================================================
-    OpenLayers/Handler.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Events.js
- */
-
-/**
- * Class: OpenLayers.Handler
- * Base class to construct a higher-level handler for event sequences.  All
- *     handlers have activate and deactivate methods.  In addition, they have
- *     methods named like browser events.  When a handler is activated, any
- *     additional methods named like a browser event is registered as a
- *     listener for the corresponding event.  When a handler is deactivated,
- *     those same methods are unregistered as event listeners.
- *
- * Handlers also typically have a callbacks object with keys named like
- *     the abstracted events or event sequences that they are in charge of
- *     handling.  The controls that wrap handlers define the methods that
- *     correspond to these abstract events - so instead of listening for
- *     individual browser events, they only listen for the abstract events
- *     defined by the handler.
- *     
- * Handlers are created by controls, which ultimately have the responsibility
- *     of making changes to the the state of the application.  Handlers
- *     themselves may make temporary changes, but in general are expected to
- *     return the application in the same state that they found it.
- */
-OpenLayers.Handler = OpenLayers.Class({
-
-    /**
-     * Property: id
-     * {String}
-     */
-    id: null,
-        
-    /**
-     * APIProperty: control
-     * {<OpenLayers.Control>}. The control that initialized this handler.  The
-     *     control is assumed to have a valid map property - that map is used
-     *     in the handler's own setMap method.
-     */
-    control: null,
-
-    /**
-     * Property: map
-     * {<OpenLayers.Map>}
-     */
-    map: null,
-
-    /**
-     * APIProperty: keyMask
-     * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
-     *     constants to construct a keyMask.  The keyMask is used by
-     *     <checkModifiers>.  If the keyMask matches the combination of keys
-     *     down on an event, checkModifiers returns true.
-     *
-     * Example:
-     * (code)
-     *     // handler only responds if the Shift key is down
-     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
-     *
-     *     // handler only responds if Ctrl-Shift is down
-     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
-     *                       OpenLayers.Handler.MOD_CTRL;
-     * (end)
-     */
-    keyMask: null,
-
-    /**
-     * Property: active
-     * {Boolean}
-     */
-    active: false,
-    
-    /**
-     * Property: evt
-     * {Event} This property references the last event handled by the handler.
-     *     Note that this property is not part of the stable API.  Use of the
-     *     evt property should be restricted to controls in the library
-     *     or other applications that are willing to update with changes to
-     *     the OpenLayers code.
-     */
-    evt: null,
-
-    /**
-     * Constructor: OpenLayers.Handler
-     * Construct a handler.
-     *
      * Parameters:
-     * control - {<OpenLayers.Control>} The control that initialized this
-     *     handler.  The control is assumed to have a valid map property; that
-     *     map is used in the handler's own setMap method.  If a map property
-     *     is present in the options argument it will be used instead.
-     * callbacks - {Object} An object whose properties correspond to abstracted
-     *     events or sequences of browser events.  The values for these
-     *     properties are functions defined by the control that get called by
-     *     the handler.
-     * options - {Object} An optional object whose properties will be set on
-     *     the handler.
+     * t - {Float} time
+     * b - {Float} beginning position
+     * c - {Float} total change
+     * d - {Float} duration of the transition
      */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Util.extend(this, options);
-        this.control = control;
-        this.callbacks = callbacks;
-
-        var map = this.map || control.map;
-        if (map) {
-            this.setMap(map); 
-        }
-        
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    easeInOut: function(t, b, c, d) {
+        if ((t/=d/2) < 1) return c/2*t*t + b;
+        return -c/2 * ((--t)*(t-2) - 1) + b;
     },
-    
-    /**
-     * Method: setMap
-     */
-    setMap: function (map) {
-        this.map = map;
-    },
 
-    /**
-     * Method: checkModifiers
-     * Check the keyMask on the handler.  If no <keyMask> is set, this always
-     *     returns true.  If a <keyMask> is set and it matches the combination
-     *     of keys down on an event, this returns true.
-     *
-     * Returns:
-     * {Boolean} The keyMask matches the keys down on an event.
-     */
-    checkModifiers: function (evt) {
-        if(this.keyMask == null) {
-            return true;
-        }
-        /* calculate the keyboard modifier mask for this event */
-        var keyModifiers =
-            (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
-            (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
-            (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
-    
-        /* if it differs from the handler object's key mask,
-           bail out of the event handler */
-        return (keyModifiers == this.keyMask);
-    },
-
-    /**
-     * APIMethod: activate
-     * Turn on the handler.  Returns false if the handler was already active.
-     * 
-     * Returns: 
-     * {Boolean} The handler was activated.
-     */
-    activate: function() {
-        if(this.active) {
-            return false;
-        }
-        // register for event handlers defined on this class.
-        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
-        for (var i=0, len=events.length; i<len; i++) {
-            if (this[events[i]]) {
-                this.register(events[i], this[events[i]]); 
-            }
-        } 
-        this.active = true;
-        return true;
-    },
-    
-    /**
-     * APIMethod: deactivate
-     * Turn off the handler.  Returns false if the handler was already inactive.
-     * 
-     * Returns:
-     * {Boolean} The handler was deactivated.
-     */
-    deactivate: function() {
-        if(!this.active) {
-            return false;
-        }
-        // unregister event handlers defined on this class.
-        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
-        for (var i=0, len=events.length; i<len; i++) {
-            if (this[events[i]]) {
-                this.unregister(events[i], this[events[i]]); 
-            }
-        } 
-        this.active = false;
-        return true;
-    },
-
-    /**
-    * Method: callback
-    * Trigger the control's named callback with the given arguments
-    *
-    * Parameters:
-    * name - {String} The key for the callback that is one of the properties
-    *     of the handler's callbacks object.
-    * args - {Array(*)} An array of arguments (any type) with which to call 
-    *     the callback (defined by the control).
-    */
-    callback: function (name, args) {
-        if (name && this.callbacks[name]) {
-            this.callbacks[name].apply(this.control, args);
-        }
-    },
-
-    /**
-    * Method: register
-    * register an event on the map
-    */
-    register: function (name, method) {
-        // TODO: deal with registerPriority in 3.0
-        this.map.events.registerPriority(name, this, method);
-        this.map.events.registerPriority(name, this, this.setEvent);
-    },
-
-    /**
-    * Method: unregister
-    * unregister an event from the map
-    */
-    unregister: function (name, method) {
-        this.map.events.unregister(name, this, method);   
-        this.map.events.unregister(name, this, this.setEvent);
-    },
-    
-    /**
-     * Method: setEvent
-     * With each registered browser event, the handler sets its own evt
-     *     property.  This property can be accessed by controls if needed
-     *     to get more information about the event that the handler is
-     *     processing.
-     *
-     * This allows modifier keys on the event to be checked (alt, shift,
-     *     and ctrl cannot be checked with the keyboard handler).  For a
-     *     control to determine which modifier keys are associated with the
-     *     event that a handler is currently processing, it should access
-     *     (code)handler.evt.altKey || handler.evt.shiftKey ||
-     *     handler.evt.ctrlKey(end).
-     *
-     * Parameters:
-     * evt - {Event} The browser event.
-     */
-    setEvent: function(evt) {
-        this.evt = evt;
-        return true;
-    },
-
-    /**
-     * Method: destroy
-     * Deconstruct the handler.
-     */
-    destroy: function () {
-        // unregister event listeners
-        this.deactivate();
-        // eliminate circular references
-        this.control = this.map = null;        
-    },
-
-    CLASS_NAME: "OpenLayers.Handler"
-});
-
-/**
- * Constant: OpenLayers.Handler.MOD_NONE
- * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
- */
-OpenLayers.Handler.MOD_NONE  = 0;
-
-/**
- * Constant: OpenLayers.Handler.MOD_SHIFT
- * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
- */
-OpenLayers.Handler.MOD_SHIFT = 1;
-
-/**
- * Constant: OpenLayers.Handler.MOD_CTRL
- * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
- */
-OpenLayers.Handler.MOD_CTRL  = 2;
-
-/**
- * Constant: OpenLayers.Handler.MOD_ALT
- * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
- */
-OpenLayers.Handler.MOD_ALT   = 4;
-
-
+    CLASS_NAME: "OpenLayers.Easing.Quad"
+};
 /* ======================================================================
     OpenLayers/Map.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Util.js
  * @requires OpenLayers/Events.js
  * @requires OpenLayers/Tween.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -15990,7 +6365,7 @@
         this.viewPortDiv.appendChild(this.layerContainerDiv);
 
         this.events = new OpenLayers.Events(this, 
-                                            this.div, 
+                                            this.viewPortDiv, 
                                             this.EVENT_TYPES, 
                                             this.fallThrough, 
                                             {includeXY: true});
@@ -16087,7 +6462,6 @@
     render: function(div) {
         this.div = OpenLayers.Util.getElement(div);
         OpenLayers.Element.addClass(this.div, 'olMap');
-        this.events.attachToElement(this.div);
         this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
         this.div.appendChild(this.viewPortDiv);
         this.updateSize();
@@ -17893,3780 +8267,235 @@
  */
 OpenLayers.Map.TILE_HEIGHT = 256;
 /* ======================================================================
-    OpenLayers/Marker.js
+    OpenLayers/Projection.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Events.js
- * @requires OpenLayers/Icon.js
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
  */
 
 /**
- * Class: OpenLayers.Marker
- * Instances of OpenLayers.Marker are a combination of a 
- * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
- *
- * Markers are generally added to a special layer called
- * <OpenLayers.Layer.Markers>.
- *
- * Example:
- * (code)
- * var markers = new OpenLayers.Layer.Markers( "Markers" );
- * map.addLayer(markers);
- *
- * var size = new OpenLayers.Size(21,25);
- * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
- * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
- *
- * (end)
- *
- * Note that if you pass an icon into the Marker constructor, it will take
- * that icon and use it. This means that you should not share icons between
- * markers -- you use them once, but you should clone() for any additional
- * markers using that same icon.
+ * Class: OpenLayers.Projection
+ * Class for coordinate transforms between coordinate systems.
+ *     Depends on the proj4js library. If proj4js is not available, 
+ *     then this is just an empty stub.
  */
-OpenLayers.Marker = OpenLayers.Class({
-    
-    /** 
-     * Property: icon 
-     * {<OpenLayers.Icon>} The icon used by this marker.
-     */
-    icon: null,
+OpenLayers.Projection = OpenLayers.Class({
 
-    /** 
-     * Property: lonlat 
-     * {<OpenLayers.LonLat>} location of object
-     */
-    lonlat: null,
-    
-    /** 
-     * Property: events 
-     * {<OpenLayers.Events>} the event handler.
-     */
-    events: null,
-    
-    /** 
-     * Property: map 
-     * {<OpenLayers.Map>} the map this marker is attached to
-     */
-    map: null,
-    
-    /** 
-     * Constructor: OpenLayers.Marker
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>} the position of this marker
-     * icon - {<OpenLayers.Icon>}  the icon for this marker
-     */
-    initialize: function(lonlat, icon) {
-        this.lonlat = lonlat;
-        
-        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
-        if (this.icon == null) {
-            this.icon = newIcon;
-        } else {
-            this.icon.url = newIcon.url;
-            this.icon.size = newIcon.size;
-            this.icon.offset = newIcon.offset;
-            this.icon.calculateOffset = newIcon.calculateOffset;
-        }
-        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
-    },
-    
     /**
-     * APIMethod: destroy
-     * Destroy the marker. You must first remove the marker from any 
-     * layer which it has been added to, or you will get buggy behavior.
-     * (This can not be done within the marker since the marker does not
-     * know which layer it is attached to.)
+     * Property: proj
+     * {Object} Proj4js.Proj instance.
      */
-    destroy: function() {
-        // erase any drawn features
-        this.erase();
-
-        this.map = null;
-
-        this.events.destroy();
-        this.events = null;
-
-        if (this.icon != null) {
-            this.icon.destroy();
-            this.icon = null;
-        }
-    },
+    proj: null,
     
-    /** 
-    * Method: draw
-    * Calls draw on the icon, and returns that output.
-    * 
-    * Parameters:
-    * px - {<OpenLayers.Pixel>}
-    * 
-    * Returns:
-    * {DOMElement} A new DOM Image with this marker's icon set at the 
-    * location passed-in
-    */
-    draw: function(px) {
-        return this.icon.draw(px);
-    }, 
-
-    /** 
-    * Method: erase
-    * Erases any drawn elements for this marker.
-    */
-    erase: function() {
-        if (this.icon != null) {
-            this.icon.erase();
-        }
-    }, 
-
     /**
-    * Method: moveTo
-    * Move the marker to the new location.
-    *
-    * Parameters:
-    * px - {<OpenLayers.Pixel>} the pixel position to move to
-    */
-    moveTo: function (px) {
-        if ((px != null) && (this.icon != null)) {
-            this.icon.moveTo(px);
-        }           
-        this.lonlat = this.map.getLonLatFromLayerPx(px);
-    },
-
-    /**
-     * APIMethod: isDrawn
-     * 
-     * Returns:
-     * {Boolean} Whether or not the marker is drawn.
+     * Property: projCode
+     * {String}
      */
-    isDrawn: function() {
-        var isDrawn = (this.icon && this.icon.isDrawn());
-        return isDrawn;   
-    },
-
-    /**
-     * Method: onScreen
-     *
-     * Returns:
-     * {Boolean} Whether or not the marker is currently visible on screen.
-     */
-    onScreen:function() {
-        
-        var onScreen = false;
-        if (this.map) {
-            var screenBounds = this.map.getExtent();
-            onScreen = screenBounds.containsLonLat(this.lonlat);
-        }    
-        return onScreen;
-    },
+    projCode: null,
     
     /**
-     * Method: inflate
-     * Englarges the markers icon by the specified ratio.
-     *
-     * Parameters:
-     * inflate - {float} the ratio to enlarge the marker by (passing 2
-     *                   will double the size).
+     * Property: titleRegEx
+     * {RegEx} regular expression to strip the title from a proj4js definition
      */
-    inflate: function(inflate) {
-        if (this.icon) {
-            var newSize = new OpenLayers.Size(this.icon.size.w * inflate,
-                                              this.icon.size.h * inflate);
-            this.icon.setSize(newSize);
-        }        
-    },
-    
-    /** 
-     * Method: setOpacity
-     * Change the opacity of the marker by changin the opacity of 
-     *   its icon
-     * 
-     * Parameters:
-     * opacity - {float}  Specified as fraction (0.4, etc)
-     */
-    setOpacity: function(opacity) {
-        this.icon.setOpacity(opacity);
-    },
+    titleRegEx: /\+title=[^\+]*/,
 
     /**
-     * Method: setUrl
-     * Change URL of the Icon Image.
-     * 
-     * url - {String} 
-     */
-    setUrl: function(url) {
-        this.icon.setUrl(url);
-    },    
-
-    /** 
-     * Method: display
-     * Hide or show the icon
-     * 
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.icon.display(display);
-    },
-
-    CLASS_NAME: "OpenLayers.Marker"
-});
-
-
-/**
- * Function: defaultIcon
- * Creates a default <OpenLayers.Icon>.
- * 
- * Returns:
- * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
- */
-OpenLayers.Marker.defaultIcon = function() {
-    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
-    var size = new OpenLayers.Size(21, 25);
-    var calculateOffset = function(size) {
-                    return new OpenLayers.Pixel(-(size.w/2), -size.h);
-                 };
-
-    return new OpenLayers.Icon(url, size, null, calculateOffset);        
-};
-    
-
-/* ======================================================================
-    OpenLayers/Request.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Events.js
- */
-
-/**
- * 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.Request = {
-    
-    /**
-     * Constant: DEFAULT_CONFIG
-     * {Object} Default configuration for all requests.
-     */
-    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
-    },
-    
-    /**
-     * APIProperty: events
-     * {<OpenLayers.Events>} An events object that handles all 
-     *     events on the {<OpenLayers.Request>} object.
+     * Constructor: OpenLayers.Projection
+     * This class offers several methods for interacting with a wrapped 
+     *     pro4js projection 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.
-     * 
-     * 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.
-     */
-    events: new OpenLayers.Events(this, null, ["complete", "success", "failure"]),
-    
-    /**
-     * 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:
-     * config - {Object} Object containing properties for configuring the
-     *     request.  Allowed configuration properties are described below.
-     *     This object is modified and should not be reused.
+     * projCode - {String} A string identifying the Well Known Identifier for
+     *    the projection.
+     * options - {Object} An optional object to set additional properties
+     *     on the layer.
      *
-     * 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:
-     * {XMLHttpRequest} Request object.  To abort the request before a response
-     *     is received, call abort() on the request object.
+     * {<OpenLayers.Projection>} A projection object.
      */
-    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);
-
-        // 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;
-            }
+    initialize: function(projCode, options) {
+        OpenLayers.Util.extend(this, options);
+        this.projCode = projCode;
+        if (window.Proj4js) {
+            this.proj = new Proj4js.Proj(projCode);
         }
-        if(config.proxy && (url.indexOf("http") == 0)) {
-            if(typeof config.proxy == "function") {
-                url = config.proxy(url);
-            } else {
-                url = config.proxy + encodeURIComponent(url);
-            }
-        }
-        request.open(
-            config.method, url, config.async, config.user, config.password
-        );
-        for(var header in config.headers) {
-            request.setRequestHeader(header, config.headers[header]);
-        }
-
-        var events = this.events;
-
-        // we want to execute runCallbacks with "this" as the
-        // execution scope
-        var self = this;
-        
-        request.onreadystatechange = function() {
-            if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
-                var proceed = events.triggerEvent(
-                    "complete",
-                    {request: request, config: config, requestUrl: url}
-                );
-                if(proceed !== false) {
-                    self.runCallbacks(
-                        {request: request, config: config, requestUrl: url}
-                    );
-                }
-            }
-        };
-        
-        // 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 {
-            window.setTimeout(function(){
-                if (request._aborted !== true) {
-                    request.send(config.data);
-                }
-            }, 0);
-        }
-        return request;
     },
     
     /**
-     * Method: runCallbacks
-     * Calls the complete, success and failure callbacks. Application
-     *    can listen to the "complete" event, have the listener 
-     *    display a confirm window and always return false, and
-     *    execute OpenLayers.Request.runCallbacks if the user
-     *    hits "yes" in the confirm window.
+     * APIMethod: getCode
+     * Get the string SRS code.
      *
-     * Parameters:
-     * options - {Object} Hash containing request, config and requestUrl keys
-     */
-    runCallbacks: function(options) {
-        var request = options.request;
-        var config = options.config;
-        
-        // bind callbacks to readyState 4 (done)
-        var complete = (config.scope) ?
-            OpenLayers.Function.bind(config.callback, config.scope) :
-            config.callback;
-        
-        // optional success callback
-        var success;
-        if(config.success) {
-            success = (config.scope) ?
-                OpenLayers.Function.bind(config.success, config.scope) :
-                config.success;
-        }
-
-        // optional failure callback
-        var failure;
-        if(config.failure) {
-            failure = (config.scope) ?
-                OpenLayers.Function.bind(config.failure, config.scope) :
-                config.failure;
-        }
-
-        complete(request);
-
-        if (!request.status || (request.status >= 200 && request.status < 300)) {
-            this.events.triggerEvent("success", options);
-            if(success) {
-                success(request);
-            }
-        }
-        if(request.status && (request.status < 200 || request.status >= 300)) {                    
-            this.events.triggerEvent("failure", options);
-            if(failure) {
-                failure(request);
-            }
-        }
-    },
-    
-    /**
-     * APIMethod: GET
-     * Send an HTTP GET request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to GET.
-     *
-     * 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.
-     * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {String} The SRS code.
      */
-    GET: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "GET"});
-        return OpenLayers.Request.issue(config);
+    getCode: function() {
+        return this.proj ? this.proj.srsCode : this.projCode;
     },
-    
+   
     /**
-     * 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".
+     * APIMethod: getUnits
+     * Get the units string for the projection -- returns null if 
+     *     proj4js is not available.
      *
-     * 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.
-     * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {String} The units abbreviation.
      */
-    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";
-        }
-        return OpenLayers.Request.issue(config);
+    getUnits: function() {
+        return this.proj ? this.proj.units : null;
     },
-    
+
     /**
-     * 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: toString
+     * Convert projection to string (getCode wrapper).
      *
-     * 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.
-     * 
      * Returns:
-     * {XMLHttpRequest} Request object.
+     * {String} The projection code.
      */
-    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);
+    toString: function() {
+        return this.getCode();
     },
-    
-    /**
-     * APIMethod: DELETE
-     * Send an HTTP DELETE request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to DELETE.
-     *
-     * 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.
-     * 
-     * Returns:
-     * {XMLHttpRequest} Request object.
-     */
-    DELETE: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "DELETE"});
-        return OpenLayers.Request.issue(config);
-    },
-  
-    /**
-     * APIMethod: HEAD
-     * Send an HTTP HEAD request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to HEAD.
-     *
-     * 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.
-     * 
-     * Returns:
-     * {XMLHttpRequest} Request object.
-     */
-    HEAD: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "HEAD"});
-        return OpenLayers.Request.issue(config);
-    },
-    
-    /**
-     * APIMethod: OPTIONS
-     * Send an HTTP OPTIONS request.  Additional configuration properties are
-     *     documented in the <issue> method, with the method property set
-     *     to OPTIONS.
-     *
-     * 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.
-     * 
-     * Returns:
-     * {XMLHttpRequest} Request object.
-     */
-    OPTIONS: function(config) {
-        config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
-        return OpenLayers.Request.issue(config);
-    }
 
-};
-/* ======================================================================
-    OpenLayers/Tile/Image.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Tile.js
- */
-
-/**
- * Class: OpenLayers.Tile.Image
- * Instances of OpenLayers.Tile.Image are used to manage the image tiles
- * used by various layers.  Create a new image tile with the
- * <OpenLayers.Tile.Image> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Tile>
- */
-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
-
-    /** 
-     * Property: url
-     * {String} The URL of the image being requested. No default. Filled in by
-     * layer.getURL() function. 
-     */
-    url: null,
-    
-    /** 
-     * Property: imgDiv
-     * {DOMElement} The div element which wraps the image.
-     */
-    imgDiv: null,
-
     /**
-     * Property: frame
-     * {DOMElement} The image element is appended to the frame.  Any gutter on
-     * the image will be hidden behind the frame. 
-     */ 
-    frame: null, 
-    
-    /**
-     * Property: layerAlphaHack
-     * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
-     */
-    layerAlphaHack: null,
-    
-    /**
-     * Property: isBackBuffer
-     * {Boolean} Is this tile a back buffer tile?
-     */
-    isBackBuffer: false,
-    
-    /**
-     * Property: lastRatio
-     * {Float} Used in transition code only.  This is the previous ratio
-     *     of the back buffer tile resolution to the map resolution.  Compared
-     *     with the current ratio to determine if zooming occurred.
-     */
-    lastRatio: 1,
-
-    /**
-     * Property: isFirstDraw
-     * {Boolean} Is this the first time the tile is being drawn?
-     *     This is used to force resetBackBuffer to synchronize
-     *     the backBufferTile with the foreground tile the first time
-     *     the foreground tile loads so that if the user zooms
-     *     before the layer has fully loaded, the backBufferTile for
-     *     tiles that have been loaded can be used.
-     */
-    isFirstDraw: true,
-        
-    /**
-     * Property: backBufferTile
-     * {<OpenLayers.Tile>} A clone of the tile used to create transition
-     *     effects when the tile is moved or changes resolution.
-     */
-    backBufferTile: null,
-
-    /** TBD 3.0 - reorder the parameters to the init function to remove 
-     *             URL. the getUrl() function on the layer gets called on 
-     *             each draw(), so no need to specify it here.
-     * 
-     * Constructor: OpenLayers.Tile.Image
-     * Constructor for a new <OpenLayers.Tile.Image> instance.
-     * 
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
-     * position - {<OpenLayers.Pixel>}
-     * bounds - {<OpenLayers.Bounds>}
-     * url - {<String>} Deprecated. Remove me in 3.0.
-     * size - {<OpenLayers.Size>}
-     */   
-    initialize: function(layer, position, bounds, url, size) {
-        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
-
-        this.url = url; //deprecated remove me
-        
-        this.frame = document.createElement('div'); 
-        this.frame.style.overflow = 'hidden'; 
-        this.frame.style.position = 'absolute'; 
-
-        this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
-    },
-
-    /** 
-     * APIMethod: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-        if (this.imgDiv != null)  {
-            if (this.layerAlphaHack) {
-                // unregister the "load" handler
-                OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0]);                
-            }
-
-            // unregister the "load" and "error" handlers. Only the "error" handler if
-            // this.layerAlphaHack is true.
-            OpenLayers.Event.stopObservingElement(this.imgDiv);
-            
-            if (this.imgDiv.parentNode == this.frame) {
-                this.frame.removeChild(this.imgDiv);
-                this.imgDiv.map = null;
-            }
-            this.imgDiv.urls = null;
-            // abort any currently loading image
-            this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
-        }
-        this.imgDiv = null;
-        if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { 
-            this.layer.div.removeChild(this.frame); 
-        }
-        this.frame = null; 
-        
-        /* clean up the backBufferTile if it exists */
-        if (this.backBufferTile) {
-            this.backBufferTile.destroy();
-            this.backBufferTile = null;
-        }
-        
-        this.layer.events.unregister("loadend", this, this.resetBackBuffer);
-        
-        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
-    },
-
-    /**
-     * Method: clone
+     * Method: equals
+     * Test equality of two projection instances.  Determines equality based
+     *     soley on the projection code.
      *
-     * Parameters:
-     * obj - {<OpenLayers.Tile.Image>} The tile to be cloned
-     *
      * Returns:
-     * {<OpenLayers.Tile.Image>} An exact clone of this <OpenLayers.Tile.Image>
+     * {Boolean} The two projections are equivalent.
      */
-    clone: function (obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Tile.Image(this.layer, 
-                                            this.position, 
-                                            this.bounds, 
-                                            this.url, 
-                                            this.size);        
-        } 
-        
-        //pick up properties from superclass
-        obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]);
-        
-        //dont want to directly copy the image div
-        obj.imgDiv = null;
-            
-        
-        return obj;
-    },
-    
-    /**
-     * Method: draw
-     * Check that a tile should be drawn, and draw it.
-     * 
-     * Returns:
-     * {Boolean} Always returns true.
-     */
-    draw: function() {
-        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
-            this.bounds = this.getBoundsFromBaseLayer(this.position);
-        }
-        var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
-        
-        if ((OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) || 
-            this.layer.singleTile) {
-            if (drawTile) {
-                //we use a clone of this tile to create a double buffer for visual
-                //continuity.  The backBufferTile is used to create transition
-                //effects while the tile in the grid is repositioned and redrawn
-                if (!this.backBufferTile) {
-                    this.backBufferTile = this.clone();
-                    this.backBufferTile.hide();
-                    // this is important.  It allows the backBuffer to place itself
-                    // appropriately in the DOM.  The Image subclass needs to put
-                    // the backBufferTile behind the main tile so the tiles can
-                    // load over top and display as soon as they are loaded.
-                    this.backBufferTile.isBackBuffer = true;
-                    
-                    // potentially end any transition effects when the tile loads
-                    this.events.register('loadend', this, this.resetBackBuffer);
-                    
-                    // clear transition back buffer tile only after all tiles in
-                    // this layer have loaded to avoid visual glitches
-                    this.layer.events.register("loadend", this, this.resetBackBuffer);
-                }
-                // run any transition effects
-                this.startTransition();
-            } else {
-                // if we aren't going to draw the tile, then the backBuffer should
-                // be hidden too!
-                if (this.backBufferTile) {
-                    this.backBufferTile.clear();
-                }
+    equals: function(projection) {
+        var p = projection, equals = false;
+        if (p) {
+            if (window.Proj4js && this.proj.defData && p.proj.defData) {
+                equals = this.proj.defData.replace(this.titleRegEx, "") ==
+                    p.proj.defData.replace(this.titleRegEx, "");
+            } else if (p.getCode) {
+                var source = this.getCode(), target = p.getCode();
+                equals = source == target ||
+                    !!OpenLayers.Projection.transforms[source] &&
+                    OpenLayers.Projection.transforms[source][target] ===
+                        OpenLayers.Projection.nullTransform;
             }
-        } else {
-            if (drawTile && this.isFirstDraw) {
-                this.events.register('loadend', this, this.showTile);
-                this.isFirstDraw = false;
-            }   
-        }    
-        
-        if (!drawTile) {
-            return false;
         }
-        
-        if (this.isLoading) {
-            //if we're already loading, send 'reload' instead of 'loadstart'.
-            this.events.triggerEvent("reload"); 
-        } else {
-            this.isLoading = true;
-            this.events.triggerEvent("loadstart");
-        }
-        
-        return this.renderTile();
+        return equals;   
     },
-    
-    /** 
-     * Method: resetBackBuffer
-     * Triggered by two different events, layer loadend, and tile loadend.
-     *     In any of these cases, we check to see if we can hide the 
-     *     backBufferTile yet and update its parameters to match the 
-     *     foreground tile.
-     *
-     * Basic logic:
-     *  - If the backBufferTile hasn't been drawn yet, reset it
-     *  - If layer is still loading, show foreground tile but don't hide
-     *    the backBufferTile yet
-     *  - If layer is done loading, reset backBuffer tile and show 
-     *    foreground tile
-     */
-    resetBackBuffer: function() {
-        this.showTile();
-        if (this.backBufferTile && 
-            (this.isFirstDraw || !this.layer.numLoadingTiles)) {
-            this.isFirstDraw = false;
-            // check to see if the backBufferTile is within the max extents
-            // before rendering it 
-            var maxExtent = this.layer.maxExtent;
-            var withinMaxExtent = (maxExtent &&
-                                   this.bounds.intersectsBounds(maxExtent, false));
-            if (withinMaxExtent) {
-                this.backBufferTile.position = this.position;
-                this.backBufferTile.bounds = this.bounds;
-                this.backBufferTile.size = this.size;
-                this.backBufferTile.imageSize = this.layer.getImageSize(this.bounds) || this.size;
-                this.backBufferTile.imageOffset = this.layer.imageOffset;
-                this.backBufferTile.resolution = this.layer.getResolution();
-                this.backBufferTile.renderTile();
-            }
 
-            this.backBufferTile.hide();
-        }
-    },
-    
-    /**
-     * Method: renderTile
-     * Internal function to actually initialize the image tile,
-     *     position it correctly, and set its url.
+    /* Method: destroy
+     * Destroy projection object.
      */
-    renderTile: function() {
-        if (this.imgDiv == null) {
-            this.initImgDiv();
-        }
-
-        this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
-        
-        if (this.layer.async) {
-            // Asyncronous image requests call the asynchronous getURL method
-            // on the layer to fetch an image that covers 'this.bounds', in the scope of
-            // 'this', setting the 'url' property of the layer itself, and running
-            // the callback 'positionFrame' when the image request returns.
-            this.layer.getURLasync(this.bounds, this, "url", this.positionImage);
-        } else {
-            // syncronous image requests get the url and position the frame immediately,
-            // and don't wait for an image request to come back.
-          
-            // needed for changing to a different server for onload error
-            if (this.layer.url instanceof Array) {
-                this.imgDiv.urls = this.layer.url.slice();
-            }
-          
-            this.url = this.layer.getURL(this.bounds);
-          
-            // position the frame immediately
-            this.positionImage(); 
-        }
-        return true;
+    destroy: function() {
+        delete this.proj;
+        delete this.projCode;
     },
-
-    /**
-     * Method: positionImage
-     * Using the properties currenty set on the layer, position the tile correctly.
-     * This method is used both by the async and non-async versions of the Tile.Image
-     * code.
-     */
-     positionImage: function() {
-        // if the this layer doesn't exist at the point the image is
-        // returned, do not attempt to use it for size computation
-        if (this.layer === null) {
-            return;
-        }
-        // position the frame 
-        OpenLayers.Util.modifyDOMElement(this.frame, 
-                                          null, this.position, this.size);   
-
-        var imageSize = this.layer.getImageSize(this.bounds); 
-        if (this.layerAlphaHack) {
-            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
-                    null, null, imageSize, this.url);
-        } else {
-            OpenLayers.Util.modifyDOMElement(this.imgDiv,
-                    null, null, imageSize) ;
-            this.imgDiv.src = this.url;
-        }
-    },
-
-    /** 
-     * Method: clear
-     *  Clear the tile of any bounds/position-related data so that it can 
-     *   be reused in a new location.
-     */
-    clear: function() {
-        if(this.imgDiv) {
-            this.hide();
-            if (OpenLayers.Tile.Image.useBlankTile) { 
-                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
-            }    
-        }
-    },
-
-    /**
-     * Method: initImgDiv
-     * Creates the imgDiv property on the tile.
-     */
-    initImgDiv: function() {
-        
-        var offset = this.layer.imageOffset; 
-        var size = this.layer.getImageSize(this.bounds); 
-     
-        if (this.layerAlphaHack) {
-            this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
-                                                           offset,
-                                                           size,
-                                                           null,
-                                                           "relative",
-                                                           null,
-                                                           null,
-                                                           null,
-                                                           true);
-        } else {
-            this.imgDiv = OpenLayers.Util.createImage(null,
-                                                      offset,
-                                                      size,
-                                                      null,
-                                                      "relative",
-                                                      null,
-                                                      null,
-                                                      true);
-        }
-        
-        this.imgDiv.className = 'olTileImage';
-
-        /* checkImgURL used to be used to called as a work around, but it
-           ended up hiding problems instead of solving them and broke things
-           like relative URLs. See discussion on the dev list:
-           http://openlayers.org/pipermail/dev/2007-January/000205.html
-
-        OpenLayers.Event.observe( this.imgDiv, "load",
-            OpenLayers.Function.bind(this.checkImgURL, this) );
-        */
-        this.frame.style.zIndex = this.isBackBuffer ? 0 : 1;
-        this.frame.appendChild(this.imgDiv); 
-        this.layer.div.appendChild(this.frame); 
-
-        if(this.layer.opacity != null) {
-            
-            OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
-                                             null, null, null, 
-                                             this.layer.opacity);
-        }
-
-        // we need this reference to check back the viewRequestID
-        this.imgDiv.map = this.layer.map;
-
-        //bind a listener to the onload of the image div so that we 
-        // can register when a tile has finished loading.
-        var onload = function() {
-            
-            //normally isLoading should always be true here but there are some 
-            // right funky conditions where loading and then reloading a tile
-            // with the same url *really*fast*. this check prevents sending 
-            // a 'loadend' if the msg has already been sent
-            //
-            if (this.isLoading) { 
-                this.isLoading = false; 
-                this.events.triggerEvent("loadend"); 
-            }
-        };
-        
-        if (this.layerAlphaHack) { 
-            OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load', 
-                                     OpenLayers.Function.bind(onload, this));    
-        } else { 
-            OpenLayers.Event.observe(this.imgDiv, 'load', 
-                                 OpenLayers.Function.bind(onload, this)); 
-        } 
-        
-
-        // Bind a listener to the onerror of the image div so that we
-        // can registere when a tile has finished loading with errors.
-        var onerror = function() {
-
-            // If we have gone through all image reload attempts, it is time
-            // to realize that we are done with this image. Since
-            // OpenLayers.Util.onImageLoadError already has taken care about
-            // the error, we can continue as if the image was loaded
-            // successfully.
-            if (this.imgDiv._attempts > OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
-                onload.call(this);
-            }
-        };
-        OpenLayers.Event.observe(this.imgDiv, "error",
-                                 OpenLayers.Function.bind(onerror, this));
-    },
-
-    /**
-     * Method: checkImgURL
-     * Make sure that the image that just loaded is the one this tile is meant
-     * to display, since panning/zooming might have changed the tile's URL in
-     * the meantime. If the tile URL did change before the image loaded, set
-     * the imgDiv display to 'none', as either (a) it will be reset to visible
-     * when the new URL loads in the image, or (b) we don't want to display
-     * this tile after all because its new bounds are outside our maxExtent.
-     * 
-     * This function should no longer  be neccesary with the improvements to
-     * Grid.js in OpenLayers 2.3. The lack of a good isEquivilantURL function
-     * caused problems in 2.2, but it's possible that with the improved 
-     * isEquivilant URL function, this might be neccesary at some point.
-     * 
-     * See discussion in the thread at 
-     * http://openlayers.org/pipermail/dev/2007-January/000205.html
-     */
-    checkImgURL: function () {
-        // Sometimes our image will load after it has already been removed
-        // from the map, in which case this check is not needed.  
-        if (this.layer) {
-            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src;
-            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
-                this.hide();
-            }
-        }
-    },
     
-    /**
-     * Method: startTransition
-     * This method is invoked on tiles that are backBuffers for tiles in the
-     *     grid.  The grid tile is about to be cleared and a new tile source
-     *     loaded.  This is where the transition effect needs to be started
-     *     to provide visual continuity.
-     */
-    startTransition: function() {
-        // backBufferTile has to be valid and ready to use
-        if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
-            return;
-        }
+    CLASS_NAME: "OpenLayers.Projection" 
+});     
 
-        // calculate the ratio of change between the current resolution of the
-        // backBufferTile and the layer.  If several animations happen in a
-        // row, then the backBufferTile will scale itself appropriately for
-        // each request.
-        var ratio = 1;
-        if (this.backBufferTile.resolution) {
-            ratio = this.backBufferTile.resolution / this.layer.getResolution();
-        }
-        
-        // if the ratio is not the same as it was last time (i.e. we are
-        // zooming), then we need to adjust the backBuffer tile
-        if (ratio != this.lastRatio) {
-            if (this.layer.transitionEffect == 'resize') {
-                // In this case, we can just immediately resize the 
-                // backBufferTile.
-                var upperLeft = new OpenLayers.LonLat(
-                    this.backBufferTile.bounds.left, 
-                    this.backBufferTile.bounds.top
-                );
-                var size = new OpenLayers.Size(
-                    this.backBufferTile.size.w * ratio,
-                    this.backBufferTile.size.h * ratio
-                );
-
-                var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
-                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame, 
-                                                 null, px, size);
-                var imageSize = this.backBufferTile.imageSize;
-                imageSize = new OpenLayers.Size(imageSize.w * ratio, 
-                                                imageSize.h * ratio);
-                var imageOffset = this.backBufferTile.imageOffset;
-                if(imageOffset) {
-                    imageOffset = new OpenLayers.Pixel(
-                        imageOffset.x * ratio, imageOffset.y * ratio
-                    );
-                }
-
-                OpenLayers.Util.modifyDOMElement(
-                    this.backBufferTile.imgDiv, null, imageOffset, imageSize
-                ) ;
-
-                this.backBufferTile.show();
-            }
-        } else {
-            // default effect is just to leave the existing tile
-            // until the new one loads if this is a singleTile and
-            // there was no change in resolution.  Otherwise we
-            // don't bother to show the backBufferTile at all
-            if (this.layer.singleTile) {
-                this.backBufferTile.show();
-            } else {
-                this.backBufferTile.hide();
-            }
-        }
-        this.lastRatio = ratio;
-
-    },
-    
-    /** 
-     * Method: show
-     * Show the tile by showing its frame.
-     */
-    show: function() {
-        this.frame.style.display = '';
-        // Force a reflow on gecko based browsers to actually show the element
-        // before continuing execution.
-        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, 
-                this.layer.transitionEffect) != -1) {
-            if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) { 
-                this.frame.scrollLeft = this.frame.scrollLeft; 
-            } 
-        }
-    },
-    
-    /** 
-     * Method: hide
-     * Hide the tile by hiding its frame.
-     */
-    hide: function() {
-        this.frame.style.display = 'none';
-    },
-    
-    CLASS_NAME: "OpenLayers.Tile.Image"
-  }
-);
-
-OpenLayers.Tile.Image.useBlankTile = ( 
-    OpenLayers.Util.getBrowserName() == "safari" || 
-    OpenLayers.Util.getBrowserName() == "opera"); 
-/* ======================================================================
-    OpenLayers/Control/OverviewMap.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/** 
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/BaseTypes.js
- * @requires OpenLayers/Events.js
- */
-
 /**
- * Class: OpenLayers.Control.OverviewMap
- * 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.
+ * Property: transforms
+ * Transforms is an object, with from properties, each of which may
+ * have a to property. This allows you to define projections without 
+ * requiring support for proj4js to be included.
  *
- * Inerits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
-
-    /**
-     * Property: element
-     * {DOMElement} The DOM element that contains the overview map
-     */
-    element: null,
-    
-    /**
-     * APIProperty: ovmap
-     * {<OpenLayers.Map>} A reference to the overview map itself.
-     */
-    ovmap: null,
-
-    /**
-     * APIProperty: size
-     * {<OpenLayers.Size>} The overvew map size in pixels.  Note that this is
-     * the size of the map itself - the element that contains the map (default
-     * class name olControlOverviewMapElement) may have padding or other style
-     * attributes added via CSS.
-     */
-    size: new OpenLayers.Size(180, 90),
-
-    /**
-     * APIProperty: layers
-     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
-     * If none are sent at construction, the base layer for the main map is used.
-     */
-    layers: null,
-    
-    /**
-     * APIProperty: minRectSize
-     * {Integer} The minimum width or height (in pixels) of the extent
-     *     rectangle on the overview map.  When the extent rectangle reaches
-     *     this size, it will be replaced depending on the value of the
-     *     <minRectDisplayClass> property.  Default is 15 pixels.
-     */
-    minRectSize: 15,
-    
-    /**
-     * APIProperty: minRectDisplayClass
-     * {String} Replacement style class name for the extent rectangle when
-     *     <minRectSize> is reached.  This string will be suffixed on to the
-     *     displayClass.  Default is "RectReplacement".
-     *
-     * Example CSS declaration:
-     * (code)
-     * .olControlOverviewMapRectReplacement {
-     *     overflow: hidden;
-     *     cursor: move;
-     *     background-image: url("img/overview_replacement.gif");
-     *     background-repeat: no-repeat;
-     *     background-position: center;
-     * }
-     * (end)
-     */
-    minRectDisplayClass: "RectReplacement",
-
-    /**
-     * APIProperty: minRatio
-     * {Float} The ratio of the overview map resolution to the main map
-     *     resolution at which to zoom farther out on the overview map.
-     */
-    minRatio: 8,
-
-    /**
-     * APIProperty: maxRatio
-     * {Float} The ratio of the overview map resolution to the main map
-     *     resolution at which to zoom farther in on the overview map.
-     */
-    maxRatio: 32,
-    
-    /**
-     * APIProperty: mapOptions
-     * {Object} An object containing any non-default properties to be sent to
-     *     the overview map's map constructor.  These should include any
-     *     non-default options that the main map was constructed with.
-     */
-    mapOptions: null,
-
-    /**
-     * 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
-     * {Object}
-     */
-    handlers: null,
-
-    /**
-     * Property: resolutionFactor
-     * {Object}
-     */
-    resolutionFactor: 1,
-
-    /**
-     * APIProperty: maximized
-     * {Boolean} Start as maximized (visible). Defaults to false.
-     */
-    maximized: false,
-
-    /**
-     * Constructor: OpenLayers.Control.OverviewMap
-     * Create a new overview map
-     *
-     * Parameters:
-     * object - {Object} Properties of this object will be set on the overview
-     * map object.  Note, to set options on the map object contained in this
-     * control, set <mapOptions> as one of the options properties.
-     */
-    initialize: function(options) {
-        this.layers = [];
-        this.handlers = {};
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Deconstruct the control
-     */
-    destroy: function() {
-        if (!this.mapDiv) { // we've already been destroyed
-            return;
-        }
-        if (this.handlers.click) {
-            this.handlers.click.destroy();
-        }
-        if (this.handlers.drag) {
-            this.handlers.drag.destroy();
-        }
-
-        this.mapDiv.removeChild(this.extentRectangle);
-        this.extentRectangle = null;
-
-        if (this.rectEvents) {
-            this.rectEvents.destroy();
-            this.rectEvents = null;
-        }
-
-        if (this.ovmap) {
-            this.ovmap.destroy();
-            this.ovmap = null;
-        }
-        
-        this.element.removeChild(this.mapDiv);
-        this.mapDiv = null;
-
-        this.div.removeChild(this.element);
-        this.element = null;
-
-        if (this.maximizeDiv) {
-            OpenLayers.Event.stopObservingElement(this.maximizeDiv);
-            this.div.removeChild(this.maximizeDiv);
-            this.maximizeDiv = null;
-        }
-        
-        if (this.minimizeDiv) {
-            OpenLayers.Event.stopObservingElement(this.minimizeDiv);
-            this.div.removeChild(this.minimizeDiv);
-            this.minimizeDiv = null;
-        }
-
-        this.map.events.un({
-            "moveend": this.update,
-            "changebaselayer": this.baseLayerDraw,
-            scope: this
-        });
-
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);    
-    },
-
-    /**
-     * Method: draw
-     * Render the control in the browser.
-     */    
-    draw: function() {
-        OpenLayers.Control.prototype.draw.apply(this, arguments);
-        if(!(this.layers.length > 0)) {
-            if (this.map.baseLayer) {
-                var layer = this.map.baseLayer.clone();
-                this.layers = [layer];
-            } else {
-                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
-                return this.div;
-            }
-        }
-
-        // create overview map DOM elements
-        this.element = document.createElement('div');
-        this.element.className = this.displayClass + 'Element';
-        this.element.style.display = 'none';
-
-        this.mapDiv = document.createElement('div');
-        this.mapDiv.style.width = this.size.w + 'px';
-        this.mapDiv.style.height = this.size.h + 'px';
-        this.mapDiv.style.position = 'relative';
-        this.mapDiv.style.overflow = 'hidden';
-        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
-        
-        this.extentRectangle = document.createElement('div');
-        this.extentRectangle.style.position = 'absolute';
-        this.extentRectangle.style.zIndex = 1000;  //HACK
-        this.extentRectangle.className = this.displayClass+'ExtentRectangle';
-        this.mapDiv.appendChild(this.extentRectangle);
-
-        this.element.appendChild(this.mapDiv);  
-
-        this.div.appendChild(this.element);
-
-        // Optionally add min/max buttons if the control will go in the
-        // map viewport.
-        if(!this.outsideViewport) {
-            this.div.className += " " + this.displayClass + 'Container';
-            var imgLocation = OpenLayers.Util.getImagesLocation();
-            // maximize button div
-            var img = imgLocation + 'layer-switcher-maximize.png';
-            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
-                                        this.displayClass + 'MaximizeButton', 
-                                        null, 
-                                        new OpenLayers.Size(18,18), 
-                                        img, 
-                                        'absolute');
-            this.maximizeDiv.style.display = 'none';
-            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
-            OpenLayers.Event.observe(this.maximizeDiv, 'click', 
-                OpenLayers.Function.bindAsEventListener(this.maximizeControl,
-                                                        this)
-            );
-            this.div.appendChild(this.maximizeDiv);
-    
-            // minimize button div
-            var img = imgLocation + 'layer-switcher-minimize.png';
-            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
-                                        'OpenLayers_Control_minimizeDiv', 
-                                        null, 
-                                        new OpenLayers.Size(18,18), 
-                                        img, 
-                                        'absolute');
-            this.minimizeDiv.style.display = 'none';
-            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
-            OpenLayers.Event.observe(this.minimizeDiv, 'click', 
-                OpenLayers.Function.bindAsEventListener(this.minimizeControl,
-                                                        this)
-            );
-            this.div.appendChild(this.minimizeDiv);
-            
-            var eventsToStop = ['dblclick','mousedown'];
-            
-            for (var i=0, len=eventsToStop.length; i<len; i++) {
-
-                OpenLayers.Event.observe(this.maximizeDiv, 
-                                         eventsToStop[i], 
-                                         OpenLayers.Event.stop);
-
-                OpenLayers.Event.observe(this.minimizeDiv,
-                                         eventsToStop[i], 
-                                         OpenLayers.Event.stop);
-            }
-            
-            this.minimizeControl();
-        } else {
-            // show the overview map
-            this.element.style.display = '';
-        }
-        if(this.map.getExtent()) {
-            this.update();
-        }
-        
-        this.map.events.register('moveend', this, this.update);
-        
-        if (this.maximized) {
-            this.maximizeControl();
-        }
-        return this.div;
-    },
-    
-    /**
-     * Method: baseLayerDraw
-     * Draw the base layer - called if unable to complete in the initial draw
-     */
-    baseLayerDraw: function() {
-        this.draw();
-        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
-    },
-
-    /**
-     * Method: rectDrag
-     * Handle extent rectangle drag
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>} The pixel location of the drag.
-     */
-    rectDrag: function(px) {
-        var deltaX = this.handlers.drag.last.x - px.x;
-        var deltaY = this.handlers.drag.last.y - px.y;
-        if(deltaX != 0 || deltaY != 0) {
-            var rectTop = this.rectPxBounds.top;
-            var rectLeft = this.rectPxBounds.left;
-            var rectHeight = Math.abs(this.rectPxBounds.getHeight());
-            var rectWidth = this.rectPxBounds.getWidth();
-            // don't allow dragging off of parent element
-            var newTop = Math.max(0, (rectTop - deltaY));
-            newTop = Math.min(newTop,
-                              this.ovmap.size.h - this.hComp - rectHeight);
-            var newLeft = Math.max(0, (rectLeft - deltaX));
-            newLeft = Math.min(newLeft,
-                               this.ovmap.size.w - this.wComp - rectWidth);
-            this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
-                                                       newTop + rectHeight,
-                                                       newLeft + rectWidth,
-                                                       newTop));
-        }
-    },
-    
-    /**
-     * Method: mapDivClick
-     * Handle browser events
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>} evt
-     */
-    mapDivClick: function(evt) {
-        var pxCenter = this.rectPxBounds.getCenterPixel();
-        var deltaX = evt.xy.x - pxCenter.x;
-        var deltaY = evt.xy.y - pxCenter.y;
-        var top = this.rectPxBounds.top;
-        var left = this.rectPxBounds.left;
-        var height = Math.abs(this.rectPxBounds.getHeight());
-        var width = this.rectPxBounds.getWidth();
-        var newTop = Math.max(0, (top + deltaY));
-        newTop = Math.min(newTop, this.ovmap.size.h - height);
-        var newLeft = Math.max(0, (left + deltaX));
-        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
-        this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
-                                                   newTop + height,
-                                                   newLeft + width,
-                                                   newTop));
-        this.updateMapToRect();
-    },
-
-    /**
-     * Method: maximizeControl
-     * Unhide the control.  Called when the control is in the map viewport.
-     *
-     * Parameters:
-     * e - {<OpenLayers.Event>}
-     */
-    maximizeControl: function(e) {
-        this.element.style.display = '';
-        this.showToggle(false);
-        if (e != null) {
-            OpenLayers.Event.stop(e);                                            
-        }
-    },
-
-    /**
-     * Method: minimizeControl
-     * Hide all the contents of the control, shrink the size, 
-     * add the maximize icon
-     * 
-     * Parameters:
-     * e - {<OpenLayers.Event>}
-     */
-    minimizeControl: function(e) {
-        this.element.style.display = 'none';
-        this.showToggle(true);
-        if (e != null) {
-            OpenLayers.Event.stop(e);                                            
-        }
-    },
-
-    /**
-     * Method: showToggle
-     * Hide/Show the toggle depending on whether the control is minimized
-     *
-     * Parameters:
-     * minimize - {Boolean} 
-     */
-    showToggle: function(minimize) {
-        this.maximizeDiv.style.display = minimize ? '' : 'none';
-        this.minimizeDiv.style.display = minimize ? 'none' : '';
-    },
-
-    /**
-     * Method: update
-     * Update the overview map after layers move.
-     */
-    update: function() {
-        if(this.ovmap == null) {
-            this.createMap();
-        }
-        
-        if(this.autoPan || !this.isSuitableOverview()) {
-            this.updateOverview();
-        }
-        
-        // update extent rectangle
-        this.updateRectToMap();
-    },
-    
-    /**
-     * Method: isSuitableOverview
-     * Determines if the overview map is suitable given the extent and
-     * resolution of the main map.
-     */
-    isSuitableOverview: function() {
-        var mapExtent = this.map.getExtent();
-        var maxExtent = this.map.maxExtent;
-        var testExtent = new OpenLayers.Bounds(
-                                Math.max(mapExtent.left, maxExtent.left),
-                                Math.max(mapExtent.bottom, maxExtent.bottom),
-                                Math.min(mapExtent.right, maxExtent.right),
-                                Math.min(mapExtent.top, maxExtent.top));        
-
-        if (this.ovmap.getProjection() != this.map.getProjection()) {
-            testExtent = testExtent.transform(
-                this.map.getProjectionObject(),
-                this.ovmap.getProjectionObject() );
-        }
-
-        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
-        return ((resRatio > this.minRatio) &&
-                (resRatio <= this.maxRatio) &&
-                (this.ovmap.getExtent().containsBounds(testExtent)));
-    },
-    
-    /**
-     * Method updateOverview
-     * Called by <update> if <isSuitableOverview> returns true
-     */
-    updateOverview: function() {
-        var mapRes = this.map.getResolution();
-        var targetRes = this.ovmap.getResolution();
-        var resRatio = targetRes / mapRes;
-        if(resRatio > this.maxRatio) {
-            // zoom in overview map
-            targetRes = this.minRatio * mapRes;            
-        } else if(resRatio <= this.minRatio) {
-            // zoom out overview map
-            targetRes = this.maxRatio * mapRes;
-        }
-        var center;
-        if (this.ovmap.getProjection() != this.map.getProjection()) {
-            center = this.map.center.clone();
-            center.transform(this.map.getProjectionObject(),
-                this.ovmap.getProjectionObject() );
-        } else {
-            center = this.map.center;
-        }
-        this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
-            targetRes * this.resolutionFactor));
-        this.updateRectToMap();
-    },
-    
-    /**
-     * Method: createMap
-     * Construct the map that this control contains
-     */
-    createMap: function() {
-        // create the overview map
-        var options = OpenLayers.Util.extend(
-                        {controls: [], maxResolution: 'auto', 
-                         fallThrough: false}, this.mapOptions);
-        this.ovmap = new OpenLayers.Map(this.mapDiv, options);
-        
-        // prevent ovmap from being destroyed when the page unloads, because
-        // the OverviewMap control has to do this (and does it).
-        OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
-        
-        this.ovmap.addLayers(this.layers);
-        this.ovmap.zoomToMaxExtent();
-        // check extent rectangle border width
-        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-left-width')) +
-                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-right-width'));
-        this.wComp = (this.wComp) ? this.wComp : 2;
-        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-top-width')) +
-                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
-                                               'border-bottom-width'));
-        this.hComp = (this.hComp) ? this.hComp : 2;
-
-        this.handlers.drag = new OpenLayers.Handler.Drag(
-            this, {move: this.rectDrag, done: this.updateMapToRect},
-            {map: this.ovmap}
-        );
-        this.handlers.click = new OpenLayers.Handler.Click(
-            this, {
-                "click": this.mapDivClick
-            },{
-                "single": true, "double": false,
-                "stopSingle": true, "stopDouble": true,
-                "pixelTolerance": 1,
-                map: this.ovmap
-            }
-        );
-        this.handlers.click.activate();
-        
-        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
-                                                null, true);
-        this.rectEvents.register("mouseover", this, function(e) {
-            if(!this.handlers.drag.active && !this.map.dragging) {
-                this.handlers.drag.activate();
-            }
-        });
-        this.rectEvents.register("mouseout", this, function(e) {
-            if(!this.handlers.drag.dragging) {
-                this.handlers.drag.deactivate();
-            }
-        });
-
-        if (this.ovmap.getProjection() != this.map.getProjection()) {
-            var sourceUnits = this.map.getProjectionObject().getUnits() ||
-                this.map.units || this.map.baseLayer.units;
-            var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
-                this.ovmap.units || this.ovmap.baseLayer.units;
-            this.resolutionFactor = sourceUnits && targetUnits ?
-                OpenLayers.INCHES_PER_UNIT[sourceUnits] /
-                OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
-        }
-    },
-        
-    /**
-     * Method: updateRectToMap
-     * Updates the extent rectangle position and size to match the map extent
-     */
-    updateRectToMap: function() {
-        // If the projections differ we need to reproject
-        var bounds;
-        if (this.ovmap.getProjection() != this.map.getProjection()) {
-            bounds = this.map.getExtent().transform(
-                this.map.getProjectionObject(), 
-                this.ovmap.getProjectionObject() );
-        } else {
-            bounds = this.map.getExtent();
-        }
-        var pxBounds = this.getRectBoundsFromMapBounds(bounds);
-        if (pxBounds) {
-            this.setRectPxBounds(pxBounds);
-        }
-    },
-    
-    /**
-     * Method: updateMapToRect
-     * Updates the map extent to match the extent rectangle position and size
-     */
-    updateMapToRect: function() {
-        var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
-        if (this.ovmap.getProjection() != this.map.getProjection()) {
-            lonLatBounds = lonLatBounds.transform(
-                this.ovmap.getProjectionObject(),
-                this.map.getProjectionObject() );
-        }
-        this.map.panTo(lonLatBounds.getCenterLonLat());
-    },
-
-    /**
-     * Method: setRectPxBounds
-     * Set extent rectangle pixel bounds.
-     *
-     * Parameters:
-     * pxBounds - {<OpenLayers.Bounds>}
-     */
-    setRectPxBounds: function(pxBounds) {
-        var top = Math.max(pxBounds.top, 0);
-        var left = Math.max(pxBounds.left, 0);
-        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
-                              this.ovmap.size.h - this.hComp);
-        var right = Math.min(pxBounds.left + pxBounds.getWidth(),
-                             this.ovmap.size.w - this.wComp);
-        var width = Math.max(right - left, 0);
-        var height = Math.max(bottom - top, 0);
-        if(width < this.minRectSize || height < this.minRectSize) {
-            this.extentRectangle.className = this.displayClass +
-                                             this.minRectDisplayClass;
-            var rLeft = left + (width / 2) - (this.minRectSize / 2);
-            var rTop = top + (height / 2) - (this.minRectSize / 2);
-            this.extentRectangle.style.top = Math.round(rTop) + 'px';
-            this.extentRectangle.style.left = Math.round(rLeft) + 'px';
-            this.extentRectangle.style.height = this.minRectSize + 'px';
-            this.extentRectangle.style.width = this.minRectSize + 'px';
-        } else {
-            this.extentRectangle.className = this.displayClass +
-                                             'ExtentRectangle';
-            this.extentRectangle.style.top = Math.round(top) + 'px';
-            this.extentRectangle.style.left = Math.round(left) + 'px';
-            this.extentRectangle.style.height = Math.round(height) + 'px';
-            this.extentRectangle.style.width = Math.round(width) + 'px';
-        }
-        this.rectPxBounds = new OpenLayers.Bounds(
-            Math.round(left), Math.round(bottom),
-            Math.round(right), Math.round(top)
-        );
-    },
-
-    /**
-     * Method: getRectBoundsFromMapBounds
-     * Get the rect bounds from the map bounds.
-     *
-     * Parameters:
-     * lonLatBounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
-     * translated into pixel bounds for the overview map
-     */
-    getRectBoundsFromMapBounds: function(lonLatBounds) {
-        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left,
-                                                     lonLatBounds.bottom);
-        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right,
-                                                   lonLatBounds.top);
-        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
-        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
-        var bounds = null;
-        if (leftBottomPx && rightTopPx) {
-            bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
-                                           rightTopPx.x, rightTopPx.y);
-        }
-        return bounds;
-    },
-
-    /**
-     * Method: getMapBoundsFromRectBounds
-     * Get the map bounds from the rect bounds.
-     *
-     * Parameters:
-     * pxBounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
-     * translated into lon/lat bounds for the overview map
-     */
-    getMapBoundsFromRectBounds: function(pxBounds) {
-        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left,
-                                                pxBounds.bottom);
-        var rightTopPx = new OpenLayers.Pixel(pxBounds.right,
-                                              pxBounds.top);
-        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
-        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
-        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
-                                     rightTopLonLat.lon, rightTopLonLat.lat);
-    },
-
-    /**
-     * Method: getLonLatFromOverviewPx
-     * Get a map location from a pixel location
-     *
-     * Parameters:
-     * overviewMapPx - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.LonLat>} Location which is the passed-in overview map
-     * OpenLayers.Pixel, translated into lon/lat by the overview map
-     */
-    getLonLatFromOverviewPx: function(overviewMapPx) {
-        var size = this.ovmap.size;
-        var res  = this.ovmap.getResolution();
-        var center = this.ovmap.getExtent().getCenterLonLat();
-    
-        var delta_x = overviewMapPx.x - (size.w / 2);
-        var delta_y = overviewMapPx.y - (size.h / 2);
-        
-        return new OpenLayers.LonLat(center.lon + delta_x * res ,
-                                     center.lat - delta_y * res); 
-    },
-
-    /**
-     * Method: getOverviewPxFromLonLat
-     * Get a pixel location from a map location
-     *
-     * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} Location which is the passed-in OpenLayers.LonLat, 
-     * translated into overview map pixels
-     */
-    getOverviewPxFromLonLat: function(lonlat) {
-        var res  = this.ovmap.getResolution();
-        var extent = this.ovmap.getExtent();
-        var px = null;
-        if (extent) {
-            px = new OpenLayers.Pixel(
-                        Math.round(1/res * (lonlat.lon - extent.left)),
-                        Math.round(1/res * (extent.top - lonlat.lat)));
-        } 
-        return px;
-    },
-
-    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
-});
-/* ======================================================================
-    OpenLayers/Feature.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Marker.js
- * @requires OpenLayers/Popup/AnchoredBubble.js
- */
-
-/**
- * Class: OpenLayers.Feature
- * Features are combinations of geography and attributes. The OpenLayers.Feature
- *     class specifically combines a marker and a lonlat.
- */
-OpenLayers.Feature = OpenLayers.Class({
-
-    /** 
-     * Property: layer 
-     * {<OpenLayers.Layer>} 
-     */
-    layer: null,
-
-    /** 
-     * Property: id 
-     * {String} 
-     */
-    id: null,
-    
-    /** 
-     * Property: lonlat 
-     * {<OpenLayers.LonLat>} 
-     */
-    lonlat: null,
-
-    /** 
-     * Property: data 
-     * {Object} 
-     */
-    data: null,
-
-    /** 
-     * Property: marker 
-     * {<OpenLayers.Marker>} 
-     */
-    marker: null,
-
-    /**
-     * APIProperty: popupClass
-     * {<OpenLayers.Class>} The class which will be used to instantiate
-     *     a new Popup. Default is <OpenLayers.Popup.AnchoredBubble>.
-     */
-    popupClass: OpenLayers.Popup.AnchoredBubble,
-
-    /** 
-     * Property: popup 
-     * {<OpenLayers.Popup>} 
-     */
-    popup: null,
-
-    /** 
-     * Constructor: OpenLayers.Feature
-     * Constructor for features.
-     *
-     * Parameters:
-     * layer - {<OpenLayers.Layer>} 
-     * lonlat - {<OpenLayers.LonLat>} 
-     * data - {Object} 
-     * 
-     * Returns:
-     * {<OpenLayers.Feature>}
-     */
-    initialize: function(layer, lonlat, data) {
-        this.layer = layer;
-        this.lonlat = lonlat;
-        this.data = (data != null) ? data : {};
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); 
-    },
-
-    /** 
-     * Method: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-
-        //remove the popup from the map
-        if ((this.layer != null) && (this.layer.map != null)) {
-            if (this.popup != null) {
-                this.layer.map.removePopup(this.popup);
-            }
-        }
-        // remove the marker from the layer
-        if (this.layer != null && this.marker != null) {
-            this.layer.removeMarker(this.marker);
-        }
-
-        this.layer = null;
-        this.id = null;
-        this.lonlat = null;
-        this.data = null;
-        if (this.marker != null) {
-            this.destroyMarker(this.marker);
-            this.marker = null;
-        }
-        if (this.popup != null) {
-            this.destroyPopup(this.popup);
-            this.popup = null;
-        }
-    },
-    
-    /**
-     * Method: onScreen
-     * 
-     * Returns:
-     * {Boolean} Whether or not the feature is currently visible on screen
-     *           (based on its 'lonlat' property)
-     */
-    onScreen:function() {
-        
-        var onScreen = false;
-        if ((this.layer != null) && (this.layer.map != null)) {
-            var screenBounds = this.layer.map.getExtent();
-            onScreen = screenBounds.containsLonLat(this.lonlat);
-        }    
-        return onScreen;
-    },
-    
-
-    /**
-     * Method: createMarker
-     * Based on the data associated with the Feature, create and return a marker object.
-     *
-     * Returns: 
-     * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
-     *          set in this.data. If no 'lonlat' is set, returns null. If no
-     *          'icon' is set, OpenLayers.Marker() will load the default image.
-     *          
-     *          Note - this.marker is set to return value
-     * 
-     */
-    createMarker: function() {
-
-        if (this.lonlat != null) {
-            this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
-        }
-        return this.marker;
-    },
-
-    /**
-     * Method: destroyMarker
-     * Destroys marker.
-     * If user overrides the createMarker() function, s/he should be able
-     *   to also specify an alternative function for destroying it
-     */
-    destroyMarker: function() {
-        this.marker.destroy();  
-    },
-
-    /**
-     * Method: createPopup
-     * Creates a popup object created from the 'lonlat', 'popupSize',
-     *     and 'popupContentHTML' properties set in this.data. It uses
-     *     this.marker.icon as default anchor. 
-     *  
-     *  If no 'lonlat' is set, returns null. 
-     *  If no this.marker has been created, no anchor is sent.
-     *
-     *  Note - the returned popup object is 'owned' by the feature, so you
-     *      cannot use the popup's destroy method to discard the popup.
-     *      Instead, you must use the feature's destroyPopup
-     * 
-     *  Note - this.popup is set to return value
-     * 
-     * Parameters: 
-     * closeBox - {Boolean} create popup with closebox or not
-     * 
-     * Returns:
-     * {<OpenLayers.Popup>} Returns the created popup, which is also set
-     *     as 'popup' property of this feature. Will be of whatever type
-     *     specified by this feature's 'popupClass' property, but must be
-     *     of type <OpenLayers.Popup>.
-     * 
-     */
-    createPopup: function(closeBox) {
-
-        if (this.lonlat != null) {
-            
-            var id = this.id + "_popup";
-            var anchor = (this.marker) ? this.marker.icon : null;
-
-            if (!this.popup) {
-                this.popup = new this.popupClass(id, 
-                                                 this.lonlat,
-                                                 this.data.popupSize,
-                                                 this.data.popupContentHTML,
-                                                 anchor, 
-                                                 closeBox); 
-            }    
-            if (this.data.overflow != null) {
-                this.popup.contentDiv.style.overflow = this.data.overflow;
-            }    
-            
-            this.popup.feature = this;
-        }        
-        return this.popup;
-    },
-
-    
-    /**
-     * Method: destroyPopup
-     * Destroys the popup created via createPopup.
-     *
-     * As with the marker, if user overrides the createPopup() function, s/he 
-     *   should also be able to override the destruction
-     */
-    destroyPopup: function() {
-        if (this.popup) {
-            this.popup.feature = null;
-            this.popup.destroy();
-            this.popup = null;
-        }    
-    },
-
-    CLASS_NAME: "OpenLayers.Feature"
-});
-/* ======================================================================
-    OpenLayers/Handler/Click.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- */
-
-/**
- * Class: OpenLayers.Handler.Click
- * A handler for mouse clicks.  The intention of this handler is to give
- *     controls more flexibility with handling clicks.  Browsers trigger
- *     click events twice for a double-click.  In addition, the mousedown,
- *     mousemove, mouseup sequence fires a click event.  With this handler,
- *     controls can decide whether to ignore clicks associated with a double
- *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
- *     that include a drag.  Create a new instance with the
- *     <OpenLayers.Handler.Click> constructor.
+ * This object has keys which correspond to a 'source' projection object.  The
+ * keys should be strings, corresponding to the projection.getCode() value.
+ * Each source projection object should have a set of destination projection
+ * keys included in the object. 
  * 
- * Inherits from:
- *  - <OpenLayers.Handler> 
- */
-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
-
-    /**
-     * APIProperty: delay
-     * {Number} Number of milliseconds between clicks before the event is
-     *     considered a double-click.
-     */
-    delay: 300,
-    
-    /**
-     * APIProperty: single
-     * {Boolean} Handle single clicks.  Default is true.  If false, clicks
-     * will not be reported.  If true, single-clicks will be reported.
-     */
-    single: true,
-    
-    /**
-     * APIProperty: double
-     * {Boolean} Handle double-clicks.  Default is false.
-     */
-    'double': false,
-    
-    /**
-     * APIProperty: pixelTolerance
-     * {Number} Maximum number of pixels between mouseup and mousedown for an
-     *     event to be considered a click.  Default is 0.  If set to an
-     *     integer value, clicks with a drag greater than the value will be
-     *     ignored.  This property can only be set when the handler is
-     *     constructed.
-     */
-    pixelTolerance: 0,
-    
-    /**
-     * APIProperty: stopSingle
-     * {Boolean} Stop other listeners from being notified of clicks.  Default
-     *     is false.  If true, any click listeners registered before this one
-     *     will not be notified of *any* click event (associated with double
-     *     or single clicks).
-     */
-    stopSingle: false,
-    
-    /**
-     * APIProperty: stopDouble
-     * {Boolean} Stop other listeners from being notified of double-clicks.
-     *     Default is false.  If true, any click listeners registered before
-     *     this one will not be notified of *any* double-click events.
-     * 
-     * The one caveat with stopDouble is that given a map with two click
-     *     handlers, one with stopDouble true and the other with stopSingle
-     *     true, the stopSingle handler should be activated last to get
-     *     uniform cross-browser performance.  Since IE triggers one click
-     *     with a dblclick and FF triggers two, if a stopSingle handler is
-     *     activated first, all it gets in IE is a single click when the
-     *     second handler stops propagation on the dblclick.
-     */
-    stopDouble: false,
-
-    /**
-     * Property: timerId
-     * {Number} The id of the timeout waiting to clear the <delayedCall>.
-     */
-    timerId: null,
-    
-    /**
-     * Property: down
-     * {<OpenLayers.Pixel>} The pixel location of the last mousedown.
-     */
-    down: null,
-    
-    /**
-     * Property: rightclickTimerId
-     * {Number} The id of the right mouse timeout waiting to clear the 
-     *     <delayedEvent>.
-     */
-    rightclickTimerId: null,
-    
-    /**
-     * Constructor: OpenLayers.Handler.Click
-     * Create a new click handler.
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that is making use of
-     *     this handler.  If a handler is being used without a control, the
-     *     handler's setMap method must be overridden to deal properly with
-     *     the map.
-     * callbacks - {Object} An object with keys corresponding to callbacks
-     *     that will be called by the handler. The callbacks should
-     *     expect to recieve a single argument, the click event.
-     *     Callbacks for 'click' and 'dblclick' are supported.
-     * options - {Object} Optional object whose properties will be set on the
-     *     handler.
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        // optionally register for mouseup and mousedown
-        if(this.pixelTolerance != null) {
-            this.mousedown = function(evt) {
-                this.down = evt.xy;
-                return true;
-            };
-        }
-    },
-    
-    /**
-     * Method: mousedown
-     * Handle mousedown.  Only registered as a listener if pixelTolerance is
-     *     a non-zero value at construction.
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    mousedown: null,
-
-    /**
-     * Method: mouseup
-     * Handle mouseup.  Installed to support collection of right mouse events.
-     * 
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    mouseup:  function (evt) {
-        var propagate = true;
-
-        // Collect right mouse clicks from the mouseup
-        //  IE - ignores the second right click in mousedown so using
-        //  mouseup instead
-        if (this.checkModifiers(evt) && 
-            this.control.handleRightClicks && 
-            OpenLayers.Event.isRightClick(evt)) {
-          propagate = this.rightclick(evt);
-        }
-
-        return propagate;
-    },
-    
-    /**
-     * Method: rightclick
-     * Handle rightclick.  For a dblrightclick, we get two clicks so we need 
-     *     to always register for dblrightclick to properly handle single 
-     *     clicks.
-     *     
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    rightclick: function(evt) {
-        if(this.passesTolerance(evt)) {
-           if(this.rightclickTimerId != null) {
-                //Second click received before timeout this must be 
-                // a double click
-                this.clearTimer();      
-                this.callback('dblrightclick', [evt]);
-                return !this.stopDouble;
-            } else { 
-                //Set the rightclickTimerId, send evt only if double is 
-                // true else trigger single
-                var clickEvent = this['double'] ?
-                    OpenLayers.Util.extend({}, evt) : 
-                    this.callback('rightclick', [evt]);
-
-                var delayedRightCall = OpenLayers.Function.bind(
-                    this.delayedRightCall, 
-                    this, 
-                    clickEvent
-                );
-                this.rightclickTimerId = window.setTimeout(
-                    delayedRightCall, this.delay
-                );
-            } 
-        }
-        return !this.stopSingle;
-    },
-    
-    /**
-     * Method: delayedRightCall
-     * Sets <rightclickTimerId> to null.  And optionally triggers the 
-     *     rightclick callback if evt is set.
-     */
-    delayedRightCall: function(evt) {
-        this.rightclickTimerId = null;
-        if (evt) {
-           this.callback('rightclick', [evt]);
-        }
-        return !this.stopSingle;
-    },
-    
-    /**
-     * Method: dblclick
-     * Handle dblclick.  For a dblclick, we get two clicks in some browsers
-     *     (FF) and one in others (IE).  So we need to always register for
-     *     dblclick to properly handle single clicks.
-     *     
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    dblclick: function(evt) {
-        if(this.passesTolerance(evt)) {
-            if(this["double"]) {
-                this.callback('dblclick', [evt]);
-            }
-            this.clearTimer();
-        }
-        return !this.stopDouble;
-    },
-    
-    /**
-     * Method: click
-     * Handle click.
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    click: function(evt) {
-        if(this.passesTolerance(evt)) {
-            if(this.timerId != null) {
-                // already received a click
-                this.clearTimer();
-            } else {
-                // set the timer, send evt only if single is true
-                //use a clone of the event object because it will no longer 
-                //be a valid event object in IE in the timer callback
-                var clickEvent = this.single ?
-                    OpenLayers.Util.extend({}, evt) : null;
-                this.timerId = window.setTimeout(
-                    OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
-                    this.delay
-                );
-            }
-        }
-        return !this.stopSingle;
-    },
-    
-    /**
-     * Method: passesTolerance
-     * Determine whether the event is within the optional pixel tolerance.  Note
-     *     that the pixel tolerance check only works if mousedown events get to
-     *     the listeners registered here.  If they are stopped by other elements,
-     *     the <pixelTolerance> will have no effect here (this method will always
-     *     return true).
-     *
-     * Returns:
-     * {Boolean} The click is within the pixel tolerance (if specified).
-     */
-    passesTolerance: function(evt) {
-        var passes = true;
-        if(this.pixelTolerance != null && this.down) {
-            var dpx = Math.sqrt(
-                Math.pow(this.down.x - evt.xy.x, 2) +
-                Math.pow(this.down.y - evt.xy.y, 2)
-            );
-            if(dpx > this.pixelTolerance) {
-                passes = false;
-            }
-        }
-        return passes;
-    },
-
-    /**
-     * Method: clearTimer
-     * Clear the timer and set <timerId> to null.
-     */
-    clearTimer: function() {
-        if(this.timerId != null) {
-            window.clearTimeout(this.timerId);
-            this.timerId = null;
-        }
-        if(this.rightclickTimerId != null) {
-            window.clearTimeout(this.rightclickTimerId);
-            this.rightclickTimerId = null;
-        }
-    },
-    
-    /**
-     * Method: delayedCall
-     * Sets <timerId> to null.  And optionally triggers the click callback if
-     *     evt is set.
-     */
-    delayedCall: function(evt) {
-        this.timerId = null;
-        if(evt) {
-            this.callback('click', [evt]);
-        }
-    },
-
-    /**
-     * APIMethod: deactivate
-     * Deactivate the handler.
-     *
-     * Returns:
-     * {Boolean} The handler was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.clearTimer();
-            this.down = null;
-            deactivated = true;
-        }
-        return deactivated;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Click"
-});
-/* ======================================================================
-    OpenLayers/Handler/Drag.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- */
-
-/**
- * Class: OpenLayers.Handler.Drag
- * The drag handler is used to deal with sequences of browser events related
- *     to dragging.  The handler is used by controls that want to know when
- *     a drag sequence begins, when a drag is happening, and when it has
- *     finished.
+ * Each value in the destination object should be a transformation function,
+ * where the function is expected to be passed an object with a .x and a .y
+ * property.  The function should return the object, with the .x and .y
+ * transformed according to the transformation function.
  *
- * Controls that use the drag handler typically construct it with callbacks
- *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
- *     when the drag begins, with each move, and when the drag is done.  In
- *     addition, controls can have callbacks keyed to 'up' and 'out' if they
- *     care to differentiate between the types of events that correspond with
- *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
- *     the 'down' and 'up' callbacks will be called, but not the 'done'
- *     callback.
- *
- * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Handler>
+ * Note - Properties on this object should not be set directly.  To add a
+ *     transform method to this object, use the <addTransform> method.  For an
+ *     example of usage, see the OpenLayers.Layer.SphericalMercator file.
  */
-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
-  
-    /** 
-     * Property: started
-     * {Boolean} When a mousedown event is received, we want to record it, but
-     *     not set 'dragging' until the mouse moves after starting. 
-     */
-    started: false,
-    
-    /**
-     * Property: stopDown
-     * {Boolean} Stop propagation of mousedown events from getting to listeners
-     *     on the same element.  Default is true.
-     */
-    stopDown: true,
+OpenLayers.Projection.transforms = {};
 
-    /** 
-     * Property: dragging 
-     * {Boolean} 
-     */
-    dragging: false,
-
-    /** 
-     * Property: last
-     * {<OpenLayers.Pixel>} The last pixel location of the drag.
-     */
-    last: null,
-
-    /** 
-     * Property: start
-     * {<OpenLayers.Pixel>} The first pixel location of the drag.
-     */
-    start: null,
-
-    /**
-     * Property: oldOnselectstart
-     * {Function}
-     */
-    oldOnselectstart: null,
-    
-    /**
-     * Property: interval
-     * {Integer} In order to increase performance, an interval (in 
-     *     milliseconds) can be set to reduce the number of drag events 
-     *     called. If set, a new drag event will not be set until the 
-     *     interval has passed. 
-     *     Defaults to 0, meaning no interval. 
-     */
-    interval: 0,
-    
-    /**
-     * Property: timeoutId
-     * {String} The id of the timeout used for the mousedown interval.
-     *     This is "private", and should be left alone.
-     */
-    timeoutId: null,
-    
-    /**
-     * APIProperty: documentDrag
-     * {Boolean} If set to true, the handler will also handle mouse moves when
-     *     the cursor has moved out of the map viewport. Default is false.
-     */
-    documentDrag: false,
-    
-    /**
-     * Property: documentEvents
-     * {<OpenLayers.Events>} Event instance for observing document events. Will
-     *     be set on mouseout if documentDrag is set to true.
-     */
-    documentEvents: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Drag
-     * Returns OpenLayers.Handler.Drag
-     * 
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that is making use of
-     *     this handler.  If a handler is being used without a control, the
-     *     handlers setMap method must be overridden to deal properly with
-     *     the map.
-     * callbacks - {Object} An object containing a single function to be
-     *     called when the drag operation is finished. The callback should
-     *     expect to recieve a single argument, the pixel location of the event.
-     *     Callbacks for 'move' and 'done' are supported. You can also speficy
-     *     callbacks for 'down', 'up', and 'out' to respond to those events.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-    },
-    
-    /**
-     * The four methods below (down, move, up, and out) are used by subclasses
-     *     to do their own processing related to these mouse events.
-     */
-    
-    /**
-     * Method: down
-     * This method is called during the handling of the mouse down event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse down event
-     */
-    down: function(evt) {
-    },
-    
-    /**
-     * Method: move
-     * This method is called during the handling of the mouse move event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse move event
-     *
-     */
-    move: function(evt) {
-    },
-
-    /**
-     * Method: up
-     * This method is called during the handling of the mouse up event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse up event
-     */
-    up: function(evt) {
-    },
-
-    /**
-     * Method: out
-     * This method is called during the handling of the mouse out event.
-     *     Subclasses can do their own processing here.
-     *
-     * Parameters:
-     * evt - {Event} The mouse out event
-     */
-    out: function(evt) {
-    },
-
-    /**
-     * The methods below are part of the magic of event handling.  Because
-     *     they are named like browser events, they are registered as listeners
-     *     for the events they represent.
-     */
-
-    /**
-     * Method: mousedown
-     * Handle mousedown events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mousedown: function (evt) {
-        var propagate = true;
-        this.dragging = false;
-        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
-            this.started = true;
-            this.start = evt.xy;
-            this.last = evt.xy;
-            OpenLayers.Element.addClass(
-                this.map.viewPortDiv, "olDragDown"
-            );
-            this.down(evt);
-            this.callback("down", [evt.xy]);
-            OpenLayers.Event.stop(evt);
-            
-            if(!this.oldOnselectstart) {
-                this.oldOnselectstart = (document.onselectstart) ? document.onselectstart : OpenLayers.Function.True;
-            }
-            document.onselectstart = OpenLayers.Function.False;
-            
-            propagate = !this.stopDown;
-        } else {
-            this.started = false;
-            this.start = null;
-            this.last = null;
-        }
-        return propagate;
-    },
-
-    /**
-     * Method: mousemove
-     * Handle mousemove events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mousemove: function (evt) {
-        if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {
-            if(this.documentDrag === true && this.documentEvents) {
-                if(evt.element === document) {
-                    this.adjustXY(evt);
-                    // do setEvent manually because the documentEvents are not
-                    // registered with the map
-                    this.setEvent(evt);
-                } else {
-                    this.destroyDocumentEvents();
-                }
-            }
-            if (this.interval > 0) {
-                this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval);
-            }
-            this.dragging = true;
-            this.move(evt);
-            this.callback("move", [evt.xy]);
-            if(!this.oldOnselectstart) {
-                this.oldOnselectstart = document.onselectstart;
-                document.onselectstart = OpenLayers.Function.False;
-            }
-            this.last = this.evt.xy;
-        }
-        return true;
-    },
-    
-    /**
-     * Method: removeTimeout
-     * Private. Called by mousemove() to remove the drag timeout.
-     */
-    removeTimeout: function() {
-        this.timeoutId = null;
-    },
-
-    /**
-     * Method: mouseup
-     * Handle mouseup events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mouseup: function (evt) {
-        if (this.started) {
-            if(this.documentDrag === true && this.documentEvents) {
-                this.adjustXY(evt);
-                this.destroyDocumentEvents();
-            }
-            var dragged = (this.start != this.last);
-            this.started = false;
-            this.dragging = false;
-            OpenLayers.Element.removeClass(
-                this.map.viewPortDiv, "olDragDown"
-            );
-            this.up(evt);
-            this.callback("up", [evt.xy]);
-            if(dragged) {
-                this.callback("done", [evt.xy]);
-            }
-            document.onselectstart = this.oldOnselectstart;
-        }
-        return true;
-    },
-
-    /**
-     * Method: mouseout
-     * Handle mouseout events
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    mouseout: function (evt) {
-        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
-            if(this.documentDrag === true) {
-                this.documentEvents = new OpenLayers.Events(this, document,
-                                            null, null, {includeXY: true});
-                this.documentEvents.on({
-                    mousemove: this.mousemove,
-                    mouseup: this.mouseup
-                });
-                OpenLayers.Element.addClass(
-                    document.body, "olDragDown"
-                );
-            } else {
-                var dragged = (this.start != this.last);
-                this.started = false; 
-                this.dragging = false;
-                OpenLayers.Element.removeClass(
-                    this.map.viewPortDiv, "olDragDown"
-                );
-                this.out(evt);
-                this.callback("out", []);
-                if(dragged) {
-                    this.callback("done", [evt.xy]);
-                }
-                if(document.onselectstart) {
-                    document.onselectstart = this.oldOnselectstart;
-                }
-            }
-        }
-        return true;
-    },
-
-    /**
-     * Method: click
-     * The drag handler captures the click event.  If something else registers
-     *     for clicks on the same element, its listener will not be called 
-     *     after a drag.
-     * 
-     * Parameters: 
-     * evt - {Event} 
-     * 
-     * Returns:
-     * {Boolean} Let the event propagate.
-     */
-    click: function (evt) {
-        // let the click event propagate only if the mouse moved
-        return (this.start == this.last);
-    },
-
-    /**
-     * Method: activate
-     * Activate the handler.
-     * 
-     * Returns:
-     * {Boolean} The handler was successfully activated.
-     */
-    activate: function() {
-        var activated = false;
-        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            this.dragging = false;
-            activated = true;
-        }
-        return activated;
-    },
-
-    /**
-     * Method: deactivate 
-     * Deactivate the handler.
-     * 
-     * Returns:
-     * {Boolean} The handler was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.started = false;
-            this.dragging = false;
-            this.start = null;
-            this.last = null;
-            deactivated = true;
-            OpenLayers.Element.removeClass(
-                this.map.viewPortDiv, "olDragDown"
-            );
-        }
-        return deactivated;
-    },
-    
-    /**
-     * Method: adjustXY
-     * Converts event coordinates that are relative to the document body to
-     * ones that are relative to the map viewport. The latter is the default in
-     * OpenLayers.
-     * 
-     * Parameters:
-     * evt - {Object}
-     */
-    adjustXY: function(evt) {
-        var pos = OpenLayers.Util.pagePosition(this.map.div);
-        evt.xy.x -= pos[0];
-        evt.xy.y -= pos[1];
-    },
-    
-    /**
-     * Method: destroyDocumentEvents
-     * Destroys the events instance that gets added to the document body when
-     * documentDrag is true and the mouse cursor leaves the map viewport while
-     * dragging.
-     */
-    destroyDocumentEvents: function() {
-        OpenLayers.Element.removeClass(
-            document.body, "olDragDown"
-        );
-        this.documentEvents.destroy();
-        this.documentEvents = null;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Drag"
-});
-/* ======================================================================
-    OpenLayers/Handler/Feature.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
 /**
- * @requires OpenLayers/Handler.js
- */
-
-/**
- * Class: OpenLayers.Handler.Feature 
- * Handler to respond to mouse events related to a drawn feature.  Callbacks
- *     with the following keys will be notified of the following events
- *     associated with features: click, clickout, over, out, and dblclick.
+ * APIMethod: addTransform
+ * Set a custom transform method between two projections.  Use this method in
+ *     cases where the proj4js lib is not available or where custom projections
+ *     need to be handled.
  *
- * This handler stops event propagation for mousedown and mouseup if those
- *     browser events target features that can be selected.
+ * Parameters:
+ * from - {String} The code for the source projection
+ * to - {String} the code for the destination projection
+ * method - {Function} A function that takes a point as an argument and
+ *     transforms that point from the source to the destination projection
+ *     in place.  The original point should be modified.
  */
-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
+OpenLayers.Projection.addTransform = function(from, to, method) {
+    if(!OpenLayers.Projection.transforms[from]) {
+        OpenLayers.Projection.transforms[from] = {};
+    }
+    OpenLayers.Projection.transforms[from][to] = method;
+};
 
-    /**
-     * Property: EVENTMAP
-     * {Object} A object mapping the browser events to objects with callback
-     *     keys for in and out.
-     */
-    EVENTMAP: {
-        'click': {'in': 'click', 'out': 'clickout'},
-        'mousemove': {'in': 'over', 'out': 'out'},
-        'dblclick': {'in': 'dblclick', 'out': null},
-        'mousedown': {'in': null, 'out': null},
-        'mouseup': {'in': null, 'out': null}
-    },
-
-    /**
-     * Property: feature
-     * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
-     */
-    feature: null,
-
-    /**
-     * Property: lastFeature
-     * {<OpenLayers.Feature.Vector>} The last feature that was handled.
-     */
-    lastFeature: null,
-
-    /**
-     * Property: down
-     * {<OpenLayers.Pixel>} The location of the last mousedown.
-     */
-    down: null,
-
-    /**
-     * Property: up
-     * {<OpenLayers.Pixel>} The location of the last mouseup.
-     */
-    up: null,
-    
-    /**
-     * Property: clickTolerance
-     * {Number} The number of pixels the mouse can move between mousedown
-     *     and mouseup for the event to still be considered a click.
-     *     Dragging the map should not trigger the click and clickout callbacks
-     *     unless the map is moved by less than this tolerance. Defaults to 4.
-     */
-    clickTolerance: 4,
-
-    /**
-     * Property: geometryTypes
-     * To restrict dragging to a limited set of geometry types, send a list
-     * of strings corresponding to the geometry class names.
-     * 
-     * @type Array(String)
-     */
-    geometryTypes: null,
-
-    /**
-     * Property: stopClick
-     * {Boolean} If stopClick is set to true, handled clicks do not
-     *      propagate to other click listeners. Otherwise, handled clicks
-     *      do propagate. Unhandled clicks always propagate, whatever the
-     *      value of stopClick. Defaults to true.
-     */
-    stopClick: true,
-
-    /**
-     * Property: stopDown
-     * {Boolean} If stopDown is set to true, handled mousedowns do not
-     *      propagate to other mousedown listeners. Otherwise, handled
-     *      mousedowns do propagate. Unhandled mousedowns always propagate,
-     *      whatever the value of stopDown. Defaults to true.
-     */
-    stopDown: true,
-
-    /**
-     * Property: stopUp
-     * {Boolean} If stopUp is set to true, handled mouseups do not
-     *      propagate to other mouseup listeners. Otherwise, handled mouseups
-     *      do propagate. Unhandled mouseups always propagate, whatever the
-     *      value of stopUp. Defaults to false.
-     */
-    stopUp: false,
-    
-    /**
-     * Constructor: OpenLayers.Handler.Feature
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * 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.
-     * options - {Object} 
-     */
-    initialize: function(control, layer, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
-        this.layer = layer;
-    },
-
-
-    /**
-     * Method: mousedown
-     * Handle mouse down.  Stop propagation if a feature is targeted by this
-     *     event (stops map dragging during feature selection).
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    mousedown: function(evt) {
-        this.down = evt.xy;
-        return this.handle(evt) ? !this.stopDown : true;
-    },
-    
-    /**
-     * Method: mouseup
-     * Handle mouse up.  Stop propagation if a feature is targeted by this
-     *     event.
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    mouseup: function(evt) {
-        this.up = evt.xy;
-        return this.handle(evt) ? !this.stopUp : true;
-    },
-
-    /**
-     * Method: click
-     * Handle click.  Call the "click" callback if click on a feature,
-     *     or the "clickout" callback if click outside any feature.
-     * 
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean}
-     */
-    click: function(evt) {
-        return this.handle(evt) ? !this.stopClick : true;
-    },
-        
-    /**
-     * Method: mousemove
-     * Handle mouse moves.  Call the "over" callback if moving in to a feature,
-     *     or the "out" callback if moving out of a feature.
-     * 
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean}
-     */
-    mousemove: function(evt) {
-        if (!this.callbacks['over'] && !this.callbacks['out']) {
-            return true;
-        }     
-        this.handle(evt);
-        return true;
-    },
-    
-    /**
-     * Method: dblclick
-     * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {Boolean}
-     */
-    dblclick: function(evt) {
-        return !this.handle(evt);
-    },
-
-    /**
-     * Method: geometryTypeMatches
-     * Return true if the geometry type of the passed feature matches
-     *     one of the geometry types in the geometryTypes array.
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Vector.Feature>}
-     *
-     * Returns:
-     * {Boolean}
-     */
-    geometryTypeMatches: function(feature) {
-        return this.geometryTypes == null ||
-            OpenLayers.Util.indexOf(this.geometryTypes,
-                                    feature.geometry.CLASS_NAME) > -1;
-    },
-
-    /**
-     * Method: handle
-     *
-     * Parameters:
-     * evt - {Event}
-     *
-     * Returns:
-     * {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) {
-            // 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
-                    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
-                    this.triggerCallback(type, 'in', [this.feature]);
-                }
-                this.lastFeature = this.feature;
-                handled = true;
-            } else {
-                // not in to a feature
-                if(this.lastFeature && (previouslyIn && inNew || click)) {
-                    // out of last feature for the first time
-                    this.triggerCallback(type, 'out', [this.lastFeature]);
-                }
-                // next time the mouse goes in a feature whose geometry type
-                // doesn't match we don't want to call the 'out' callback
-                // again, so let's set this.feature to null so that
-                // previouslyIn will evaluate to false the next time
-                // we enter handle. Yes, a bit hackish...
-                this.feature = null;
-            }
-        } else {
-            if(this.lastFeature && (previouslyIn || click)) {
-                this.triggerCallback(type, 'out', [this.lastFeature]);
-            }
-        }
-        return handled;
-    },
-    
-    /**
-     * Method: triggerCallback
-     * Call the callback keyed in the event map with the supplied arguments.
-     *     For click and clickout, the <clickTolerance> is checked first.
-     *
-     * Parameters:
-     * type - {String}
-     */
-    triggerCallback: function(type, mode, args) {
-        var key = this.EVENTMAP[type][mode];
-        if(key) {
-            if(type == 'click' && this.up && this.down) {
-                // for click/clickout, only trigger callback if tolerance is met
-                var dpx = Math.sqrt(
-                    Math.pow(this.up.x - this.down.x, 2) +
-                    Math.pow(this.up.y - this.down.y, 2)
-                );
-                if(dpx <= this.clickTolerance) {
-                    this.callback(key, args);
-                }
-            } else {
-                this.callback(key, args);
-            }
-        }
-    },
-
-    /**
-     * Method: activate 
-     * Turn on the handler.  Returns false if the handler was already active.
-     *
-     * Returns:
-     * {Boolean}
-     */
-    activate: function() {
-        var activated = false;
-        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            this.moveLayerToTop();
-            this.map.events.on({
-                "removelayer": this.handleMapEvents,
-                "changelayer": this.handleMapEvents,
-                scope: this
-            });
-            activated = true;
-        }
-        return activated;
-    },
-    
-    /**
-     * Method: deactivate 
-     * Turn off the handler.  Returns false if the handler was already active.
-     *
-     * Returns: 
-     * {Boolean}
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.moveLayerBack();
-            this.feature = null;
-            this.lastFeature = null;
-            this.down = null;
-            this.up = null;
-            this.map.events.un({
-                "removelayer": this.handleMapEvents,
-                "changelayer": this.handleMapEvents,
-                scope: this
-            });
-            deactivated = true;
-        }
-        return deactivated;
-    },
-    
-    /**
-     * Method handleMapEvents
-     * 
-     * Parameters:
-     * evt - {Object}
-     */
-    handleMapEvents: function(evt) {
-        if (!evt.property || evt.property == "order") {
-            this.moveLayerToTop();
-        }
-    },
-    
-    /**
-     * Method: moveLayerToTop
-     * Moves the layer for this handler to the top, so mouse events can reach
-     * it.
-     */
-    moveLayerToTop: function() {
-        var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
-            this.layer.getZIndex()) + 1;
-        this.layer.setZIndex(index);
-        
-    },
-    
-    /**
-     * Method: moveLayerBack
-     * Moves the layer back to the position determined by the map's layers
-     * array.
-     */
-    moveLayerBack: function() {
-        var index = this.layer.getZIndex() - 1;
-        if (index >= this.map.Z_INDEX_BASE['Feature']) {
-            this.layer.setZIndex(index);
-        } else {
-            this.map.setLayerZIndex(this.layer,
-                this.map.getLayerIndex(this.layer));
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Feature"
-});
-/* ======================================================================
-    OpenLayers/Handler/Hover.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Handler.js
- */
-
-/**
- * Class: OpenLayers.Handler.Hover
- * The hover handler is to be used to emulate mouseovers on objects
- *      on the map that aren't DOM elements. For example one can use
- *      this handler to send WMS/GetFeatureInfo requests as the user
- *      moves the mouve over the map.
+ * APIMethod: transform
+ * Transform a point coordinate from one projection to another.  Note that
+ *     the input point is transformed in place.
  * 
- * Inherits from:
- *  - <OpenLayers.Handler> 
+ * Parameters:
+ * point - {{OpenLayers.Geometry.Point> | Object} An object with x and y
+ *     properties representing coordinates in those dimensions.
+ * sourceProj - {OpenLayers.Projection} Source map coordinate system
+ * destProj - {OpenLayers.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate.  The original point is modified.
  */
-OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
+OpenLayers.Projection.transform = function(point, source, dest) {
+    if (source.proj && dest.proj) {
+        point = Proj4js.transform(source.proj, dest.proj, point);
+    } else if (source && dest && 
+               OpenLayers.Projection.transforms[source.getCode()] && 
+               OpenLayers.Projection.transforms[source.getCode()][dest.getCode()]) {
+        OpenLayers.Projection.transforms[source.getCode()][dest.getCode()](point); 
+    }
+    return point;
+};
 
-    /**
-     * APIProperty: delay
-     * {Integer} - Number of milliseconds between mousemoves before
-     *      the event is considered a hover. Default is 500.
-     */
-    delay: 500,
-    
-    /**
-     * APIProperty: pixelTolerance
-     * {Integer} - Maximum number of pixels between mousemoves for
-     *      an event to be considered a hover. Default is null.
-     */
-    pixelTolerance: null,
-
-    /**
-     * APIProperty: stopMove
-     * {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
-     *      in pixels.
-     */
-    px: null,
-
-    /**
-     * Property: timerId
-     * {Number} - The id of the timer.
-     */
-    timerId: null,
- 
-    /**
-     * Constructor: OpenLayers.Handler.Hover
-     * Construct a hover handler.
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} The control that initialized this
-     *     handler.  The control is assumed to have a valid map property; that
-     *     map is used in the handler's own setMap method.
-     * callbacks - {Object} An object 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.
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-    },
-
-    /**
-     * Method: mousemove
-     * Called when the mouse moves on the map.
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>}
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    mousemove: function(evt) {
-        if(this.passesTolerance(evt.xy)) {
-            this.clearTimer();
-            this.callback('move', [evt]);
-            this.px = evt.xy;
-            // clone the evt so original properties can be accessed even
-            // if the browser deletes them during the delay
-            evt = OpenLayers.Util.extend({}, evt);
-            this.timerId = window.setTimeout(
-                OpenLayers.Function.bind(this.delayedCall, this, evt),
-                this.delay
-            );
-        }
-        return !this.stopMove;
-    },
-
-    /**
-     * Method: mouseout
-     * Called when the mouse goes out of the map.
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>}
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
-     */
-    mouseout: function(evt) {
-        if (OpenLayers.Util.mouseLeft(evt, this.map.div)) {
-            this.clearTimer();
-            this.callback('move', [evt]);
-        }
-        return true;
-    },
-
-    /**
-     * Method: passesTolerance
-     * Determine whether the mouse move is within the optional pixel tolerance.
-     *
-     * Parameters:
-     * px - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {Boolean} The mouse move is within the pixel tolerance.
-     */
-    passesTolerance: function(px) {
-        var passes = true;
-        if(this.pixelTolerance && this.px) {
-            var dpx = Math.sqrt(
-                Math.pow(this.px.x - px.x, 2) +
-                Math.pow(this.px.y - px.y, 2)
-            );
-            if(dpx < this.pixelTolerance) {
-                passes = false;
-            }
-        }
-        return passes;
-    },
-
-    /**
-     * Method: clearTimer
-     * Clear the timer and set <timerId> to null.
-     */
-    clearTimer: function() {
-        if(this.timerId != null) {
-            window.clearTimeout(this.timerId);
-            this.timerId = null;
-        }
-    },
-
-    /**
-     * Method: delayedCall
-     * Triggers pause callback.
-     *
-     * Parameters:
-     * evt - {<OpenLayers.Event>}
-     */
-    delayedCall: function(evt) {
-        this.callback('pause', [evt]);
-    },
-
-    /**
-     * APIMethod: deactivate
-     * Deactivate the handler.
-     *
-     * Returns:
-     * {Boolean} The handler was successfully deactivated.
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.clearTimer();
-            deactivated = true;
-        }
-        return deactivated;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Hover"
-});
-/* ======================================================================
-    OpenLayers/Handler/MouseWheel.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
 /**
- * @requires OpenLayers/Handler.js
+ * APIFunction: nullTransform
+ * A null transformation - useful for defining projection aliases when
+ * proj4js is not available:
+ *
+ * (code)
+ * OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857",
+ *     OpenLayers.Layer.SphericalMercator.projectForward);
+ * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:3857",
+ *     OpenLayers.Layer.SphericalMercator.projectInverse);
+ * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
+ *     OpenLayers.Projection.nullTransform);
+ * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
+ *     OpenLayers.Projection.nullTransform);
+ * (end)
  */
-
-/**
- * Class: OpenLayers.Handler.MouseWheel
- * Handler for wheel up/down events.
- * 
- * Inherits from:
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
-    /** 
-     * Property: wheelListener 
-     * {function} 
-     */
-    wheelListener: null,
-
-    /** 
-     * Property: mousePosition
-     * {<OpenLayers.Pixel>} mousePosition is necessary because
-     * evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
-     * value from the last mousemove.
-     */
-    mousePosition: null,
-
-    /**
-     * Property: interval
-     * {Integer} In order to increase server performance, an interval (in 
-     *     milliseconds) can be set to reduce the number of up/down events 
-     *     called. If set, a new up/down event will not be set until the 
-     *     interval has passed. 
-     *     Defaults to 0, meaning no interval. 
-     */
-    interval: 0,
-    
-    /**
-     * Property: delta
-     * {Integer} When interval is set, delta collects the mousewheel z-deltas
-     *     of the events that occur within the interval.
-     *      See also the cumulative option
-     */
-    delta: 0,
-    
-    /**
-     * Property: cumulative
-     * {Boolean} When interval is set: true to collect all the mousewheel 
-     *     z-deltas, false to only record the delta direction (positive or
-     *     negative)
-     */
-    cumulative: true,
-
-    /**
-     * Constructor: OpenLayers.Handler.MouseWheel
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object containing a single function to be
-     *                          called when the drag operation is finished.
-     *                          The callback should expect to recieve a single
-     *                          argument, the point geometry.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        this.wheelListener = OpenLayers.Function.bindAsEventListener(
-            this.onWheelEvent, this
-        );
-    },
-
-    /**
-     * Method: destroy
-     */    
-    destroy: function() {
-        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
-        this.wheelListener = null;
-    },
-
-    /**
-     *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
-     */
-
-    /** 
-     * Method: onWheelEvent
-     * Catch the wheel event and handle it xbrowserly
-     * 
-     * Parameters:
-     * e - {Event} 
-     */
-    onWheelEvent: function(e){
-        
-        // make sure we have a map and check keyboard modifiers
-        if (!this.map || !this.checkModifiers(e)) {
-            return;
-        }
-        
-        // Ride up the element's DOM hierarchy to determine if it or any of 
-        //  its ancestors was: 
-        //   * specifically marked as scrollable
-        //   * one of our layer divs
-        //   * the map div
-        //
-        var overScrollableDiv = false;
-        var overLayerDiv = false;
-        var overMapDiv = false;
-        
-        var elem = OpenLayers.Event.element(e);
-        while((elem != null) && !overMapDiv && !overScrollableDiv) {
-
-            if (!overScrollableDiv) {
-                try {
-                    if (elem.currentStyle) {
-                        overflow = elem.currentStyle["overflow"];
-                    } else {
-                        var style = 
-                            document.defaultView.getComputedStyle(elem, null);
-                        var overflow = style.getPropertyValue("overflow");
-                    }
-                    overScrollableDiv = ( overflow && 
-                        (overflow == "auto") || (overflow == "scroll") );
-                } catch(err) {
-                    //sometimes when scrolling in a popup, this causes 
-                    // obscure browser error
-                }
-            }
-
-            if (!overLayerDiv) {
-                for(var i=0, len=this.map.layers.length; i<len; i++) {
-                    // Are we in the layer div? Note that we have two cases
-                    // here: one is to catch EventPane layers, which have a 
-                    // pane above the layer (layer.pane)
-                    if (elem == this.map.layers[i].div 
-                        || elem == this.map.layers[i].pane) { 
-                        overLayerDiv = true;
-                        break;
-                    }
-                }
-            }
-            overMapDiv = (elem == this.map.div);
-
-            elem = elem.parentNode;
-        }
-        
-        // Logic below is the following:
-        //
-        // If we are over a scrollable div or not over the map div:
-        //  * do nothing (let the browser handle scrolling)
-        //
-        //    otherwise 
-        // 
-        //    If we are over the layer div: 
-        //     * zoom/in out
-        //     then
-        //     * kill event (so as not to also scroll the page after zooming)
-        //
-        //       otherwise
-        //
-        //       Kill the event (dont scroll the page if we wheel over the 
-        //        layerswitcher or the pan/zoom control)
-        //
-        if (!overScrollableDiv && overMapDiv) {
-            if (overLayerDiv) {
-                var delta = 0;
-                if (!e) {
-                    e = window.event;
-                }
-                if (e.wheelDelta) {
-                    delta = e.wheelDelta/120; 
-                    if (window.opera && window.opera.version() < 9.2) {
-                        delta = -delta;
-                    }
-                } else if (e.detail) {
-                    delta = -e.detail / 3;
-                }
-                this.delta = this.delta + delta;
-
-                if(this.interval) {
-                    window.clearTimeout(this._timeoutId);
-                    this._timeoutId = window.setTimeout(
-                        OpenLayers.Function.bind(function(){
-                            this.wheelZoom(e);
-                        }, this),
-                        this.interval
-                    );
-                } else {
-                    this.wheelZoom(e);
-                }
-            }
-            OpenLayers.Event.stop(e);
-        }
-    },
-
-    /**
-     * Method: wheelZoom
-     * Given the wheel event, we carry out the appropriate zooming in or out,
-     *     based on the 'wheelDelta' or 'detail' property of the event.
-     * 
-     * Parameters:
-     * e - {Event}
-     */
-    wheelZoom: function(e) {
-        var delta = this.delta;
-        this.delta = 0;
-        
-        if (delta) {
-            // add the mouse position to the event because mozilla has 
-            // a bug with clientX and clientY (see 
-            // https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
-            // getLonLatFromViewPortPx(e) returns wrong values
-            if (this.mousePosition) {
-                e.xy = this.mousePosition;
-            } 
-            if (!e.xy) {
-                // If the mouse hasn't moved over the map yet, then
-                // we don't have a mouse position (in FF), so we just
-                // act as if the mouse was at the center of the map.
-                // Note that we can tell we are in the map -- and 
-                // this.map is ensured to be true above.
-                e.xy = this.map.getPixelFromLonLat(
-                    this.map.getCenter()
-                );
-            }
-            if (delta < 0) {
-                this.callback("down", [e, this.cumulative ? delta : -1]);
-            } else {
-                this.callback("up", [e, this.cumulative ? delta : 1]);
-            }
-        }
-    },
-    
-    /**
-     * Method: mousemove
-     * Update the stored mousePosition on every move.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousemove: function (evt) {
-        this.mousePosition = evt.xy;
-    },
-
-    /**
-     * Method: activate 
-     */
-    activate: function (evt) {
-        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            //register mousewheel events specifically on the window and document
-            var wheelListener = this.wheelListener;
-            OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
-            OpenLayers.Event.observe(window, "mousewheel", wheelListener);
-            OpenLayers.Event.observe(document, "mousewheel", wheelListener);
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Method: deactivate 
-     */
-    deactivate: function (evt) {
-        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            // unregister mousewheel events specifically on the window and document
-            var wheelListener = this.wheelListener;
-            OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
-            OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
-            OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.MouseWheel"
-});
+OpenLayers.Projection.nullTransform = function(point) {
+    return point;
+};
 /* ======================================================================
     OpenLayers/Layer.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Map.js
  * @requires OpenLayers/Projection.js
  */
@@ -22366,7 +9195,9 @@
 
     /** 
      * APIMethod: display
-     * Hide or show the Layer
+     * Hide or show the Layer. This is designed to be used internally, and 
+     *     is not generally the way to enable or disable the layer. For that,
+     *     use the setVisibility function instead..
      * 
      * Parameters:
      * display - {Boolean}
@@ -22965,2410 +9796,231 @@
     CLASS_NAME: "OpenLayers.Layer"
 });
 /* ======================================================================
-    OpenLayers/Marker/Box.js
+    OpenLayers/Layer/SphericalMercator.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Marker.js
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Projection.js
  */
 
 /**
- * Class: OpenLayers.Marker.Box
+ * Class: OpenLayers.Layer.SphericalMercator
+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate
+ *     conversion for working with commercial APIs which use a spherical
+ *     mercator projection.  Using this layer as a base layer, additional
+ *     layers can be used as overlays if they are in the same projection.
  *
- * Inherits from:
- *  - <OpenLayers.Marker> 
- */
-OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {
-
-    /** 
-     * Property: bounds 
-     * {<OpenLayers.Bounds>} 
-     */
-    bounds: null,
-
-    /** 
-     * Property: div 
-     * {DOMElement} 
-     */
-    div: null,
-    
-    /** 
-     * Constructor: OpenLayers.Marker.Box
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} 
-     * borderColor - {String} 
-     * borderWidth - {int} 
-     */
-    initialize: function(bounds, borderColor, borderWidth) {
-        this.bounds = bounds;
-        this.div    = OpenLayers.Util.createDiv();
-        this.div.style.overflow = 'hidden';
-        this.events = new OpenLayers.Events(this, this.div, null);
-        this.setBorder(borderColor, borderWidth);
-    },
-
-    /**
-     * Method: destroy 
-     */    
-    destroy: function() {
-
-        this.bounds = null;
-        this.div = null;
-
-        OpenLayers.Marker.prototype.destroy.apply(this, arguments);
-    },
-
-    /** 
-     * Method: setBorder
-     * Allow the user to change the box's color and border width
-     * 
-     * Parameters:
-     * color - {String} Default is "red"
-     * width - {int} Default is 2
-     */
-    setBorder: function (color, width) {
-        if (!color) {
-            color = "red";
-        }
-        if (!width) {
-            width = 2;
-        }
-        this.div.style.border = width + "px solid " + color;
-    },
-    
-    /** 
-    * Method: draw
-    * 
-    * Parameters:
-    * px - {<OpenLayers.Pixel>} 
-    * sz - {<OpenLayers.Size>} 
-    * 
-    * Returns: 
-    * {DOMElement} A new DOM Image with this marker´s icon set at the 
-    *         location passed-in
-    */
-    draw: function(px, sz) {
-        OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
-        return this.div;
-    }, 
-
-    /**
-     * Method: onScreen
-     * 
-     * Rreturn:
-     * {Boolean} Whether or not the marker is currently visible on screen.
-     */
-    onScreen:function() {
-        var onScreen = false;
-        if (this.map) {
-            var screenBounds = this.map.getExtent();
-            onScreen = screenBounds.containsBounds(this.bounds, true, true);
-        }    
-        return onScreen;
-    },
-    
-    /**
-     * Method: display
-     * Hide or show the icon
-     * 
-     * Parameters:
-     * display - {Boolean} 
-     */
-    display: function(display) {
-        this.div.style.display = (display) ? "" : "none";
-    },
-
-    CLASS_NAME: "OpenLayers.Marker.Box"
-});
-
-/* ======================================================================
-    OpenLayers/Request/XMLHttpRequest.js
-   ====================================================================== */
-
-// XMLHttpRequest.js Copyright (C) 2010 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,
-        bIE7    = bIE && window.navigator.userAgent.match(/MSIE ([\.0-9]+)/) && RegExp.$1 == 7;
-
-    // Constructor
-    function cXMLHttpRequest() {
-        this._object    = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
-        this._listeners    = [];
-    };
-
-    // 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) {
-        // Delete headers, required when object is reused
-        delete this._headers;
-
-        // When bAsync parameter value is omitted, use true as default
-        if (arguments.length < 3)
-            bAsync    = true;
-
-        // 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,
-            fOnUnload;
-
-        // BUGFIX: IE - memory leak on page unload (inter-page leak)
-        if (bIE && bAsync) {
-            fOnUnload = function() {
-                if (nState != cXMLHttpRequest.DONE) {
-                    fCleanTransport(oRequest);
-                    // Safe to abort here since onreadystatechange handler removed
-                    oRequest.abort();
-                }
-            };
-                window.attachEvent("onunload", fOnUnload);
-        }
-
-        // Add method sniffer
-        if (cXMLHttpRequest.onopen)
-            cXMLHttpRequest.onopen.apply(this, arguments);
-
-        if (arguments.length > 4)
-            this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
-        else
-        if (arguments.length > 3)
-            this._object.open(sMethod, sUrl, bAsync, sUser);
-        else
-            this._object.open(sMethod, sUrl, bAsync);
-
-        if (!bGecko && !bIE) {
-            this.readyState    = cXMLHttpRequest.OPENED;
-            fReadyStateChange(this);
-        }
-
-        this._object.onreadystatechange    = function() {
-            if (bGecko && !bAsync)
-                return;
-
-            // Synchronize state
-            oRequest.readyState        = oRequest._object.readyState;
-
-            //
-            fSynchronizeValues(oRequest);
-
-            // BUGFIX: Firefox fires unnecessary 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
-                    if (sUser) {
-                         if (sPassword)
-                    oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
-                        else
-                            oRequest._object.open(sMethod, sUrl, bAsync, sUser);
-                    }
-                    else
-                        oRequest._object.open(sMethod, sUrl, bAsync);
-                    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 until 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;
-        }
-    };
-    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 - unnecessary 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);
-    };
-
-    // EventTarget interface implementation
-    cXMLHttpRequest.prototype.addEventListener    = function(sName, fHandler, bUseCapture) {
-        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
-            if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
-                return;
-        // Add listener
-        this._listeners.push([sName, fHandler, bUseCapture]);
-    };
-
-    cXMLHttpRequest.prototype.removeEventListener    = function(sName, fHandler, bUseCapture) {
-        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
-            if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
-                break;
-        // Remove listener
-        if (oListener)
-            this._listeners.splice(nIndex, 1);
-    };
-
-    cXMLHttpRequest.prototype.dispatchEvent    = function(oEvent) {
-        var oEventPseudo    = {
-            'type':            oEvent.type,
-            'target':        this,
-            'currentTarget':this,
-            'eventPhase':    2,
-            'bubbles':        oEvent.bubbles,
-            'cancelable':    oEvent.cancelable,
-            'timeStamp':    oEvent.timeStamp,
-            'stopPropagation':    function() {},    // There is no flow
-            'preventDefault':    function() {},    // There is no default action
-            'initEvent':        function() {}    // Original event object should be initialized
-        };
-
-        // Execute onreadystatechange
-        if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
-            (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
-
-        // Execute listeners
-        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
-            if (oListener[0] == oEventPseudo.type && !oListener[2])
-                (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
-    };
-
-    //
-    cXMLHttpRequest.prototype.toString    = function() {
-        return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
-    };
-
-    cXMLHttpRequest.toString    = function() {
-        return '[' + "XMLHttpRequest" + ']';
-    };
-
-    // Helper function
-    function fReadyStateChange(oRequest) {
-        // Sniffing code
-        if (cXMLHttpRequest.onreadystatechange)
-            cXMLHttpRequest.onreadystatechange.apply(oRequest);
-
-        // Fake event
-        oRequest.dispatchEvent({
-            'type':            "readystatechange",
-            'bubbles':        false,
-            'cancelable':    false,
-            'timeStamp':    new Date + 0
-        });
-    };
-
-    function fGetDocument(oRequest) {
-        var oDocument    = oRequest.responseXML,
-            sResponse    = oRequest.responseText;
-        // Try parsing responseText
-        if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
-            oDocument    = new window.ActiveXObject("Microsoft.XMLDOM");
-            oDocument.async                = false;
-            oDocument.validateOnParse    = false;
-            oDocument.loadXML(sResponse);
-        }
-        // Check if there is no error in document
-        if (oDocument)
-            if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (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;
-    };
-
-    // 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-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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
+ * A layer is given properties of this object by setting the sphericalMercator
+ *     property to true.
  *
- *  @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);
+ * More projection information:
+ *  - http://spatialreference.org/ref/user/google-projection/
  *
- * 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
+ * Proj4 Text:
+ *     +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
+ *     +k=1.0 +units=m +nadgrids=@null +no_defs
  *
+ * WKT:
+ *     900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
+ *     DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], 
+ *     PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], 
+ *     AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
+ *     PROJECTION["Mercator_1SP_Google"], 
+ *     PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], 
+ *     PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], 
+ *     PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
+ *     AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
  */
+OpenLayers.Layer.SphericalMercator = {
 
-
-/**
- * Function: OpenLayers.nullHandler
- * @param {} request
- */
-OpenLayers.nullHandler = function(request) {
-    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
-};
-
-/** 
- * APIFunction: OpenLayers.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: OpenLayers.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
+     * Method: getExtent
+     * Get the map's extent.
      *
-     * {Boolean}
+     * Returns:
+     * {<OpenLayers.Bounds>} The map extent.
      */
-    _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);
+    getExtent: function() {
+        var extent = null;
+        if (this.sphericalMercator) {
+            extent = this.map.calculateBounds();
+        } else {
+            extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
         }
-        
-        this.transport = OpenLayers.Ajax.getTransport();
-        this.request(url);
+        return extent;
     },
 
     /**
-     * Method: request
+     * Method: getLonLatFromViewPortPx
+     * Get a map location from a pixel location
      * 
      * 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
+     * viewPortPx - {<OpenLayers.Pixel>}
      *
      * Returns:
-     * {Boolean} - 
+     *  {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
+     *  port OpenLayers.Pixel, translated into lon/lat by map lib
+     *  If the map lib is not loaded or not centered, returns null
      */
-    success: function() {
-        var status = this.getStatus();
-        return !status || (status >=200 && status < 300);
+    getLonLatFromViewPortPx: function (viewPortPx) {
+        return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
     },
     
     /**
-     * Method: getStatus
+     * Method: getViewPortPxFromLonLat
+     * Get a pixel location from a map location
      *
-     * 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
+     * lonlat - {<OpenLayers.LonLat>}
      *
      * Returns:
-     * {?} - response header for the given name
+     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
+     * OpenLayers.LonLat, translated into view port pixels by map lib
+     * If map lib is not loaded or not centered, returns null
      */
-    getHeader: function(name) {
-        try {
-            return this.transport.getResponseHeader(name);
-        } catch (e) {
-            return null;
-        }
+    getViewPortPxFromLonLat: function (lonlat) {
+        return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
     },
 
-    /**
-     * 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 - {?}
+    /** 
+     * Method: initMercatorParameters 
+     * Set up the mercator parameters on the layer: resolutions,
+     *     projection, units.
      */
-    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;
-            }
+    initMercatorParameters: function() {
+        // set up properties for Mercator - assume EPSG:900913
+        this.RESOLUTIONS = [];
+        var maxResolution = 156543.0339;
+        for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
+            this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
         }
-    }
-});
-
-/** 
- * 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;
-        }
+        this.units = "m";
+        this.projection = this.projection || "EPSG:900913";
     },
-    
-    /**
-     * 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
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Handler/Drag.js
- */
-
-/**
- * Class: OpenLayers.Control.DragPan
- * The DragPan control pans the map with a drag of the mouse.
- *
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
-
-    /** 
-     * Property: type
-     * {OpenLayers.Control.TYPES}
-     */
-    type: OpenLayers.Control.TYPE_TOOL,
-    
     /**
-     * Property: panned
-     * {Boolean} The map moved.
-     */
-    panned: false,
-    
-    /**
-     * Property: interval
-     * {Integer} The number of milliseconds that should ellapse before
-     *     panning the map again. Set this to increase dragging performance.
-     *     Defaults to 25 milliseconds.
-     */
-    interval: 25,
-    
-    /**
-     * APIProperty: documentDrag
-     * {Boolean} If set to true, mouse dragging will continue even if the
-     *     mouse cursor leaves the map viewport. Default is false.
-     */
-    documentDrag: false,
-    
-    /**
-     * Method: draw
-     * Creates a Drag handler, using <panMap> and
-     * <panMapDone> as callbacks.
-     */    
-    draw: function() {
-        this.handler = new OpenLayers.Handler.Drag(this, {
-                "move": this.panMap,
-                "done": this.panMapDone
-            }, {
-                interval: this.interval,
-                documentDrag: this.documentDrag
-            }
-        );
-    },
-
-    /**
-    * Method: panMap
-    *
-    * Parameters:
-    * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
-    */
-    panMap: function(xy) {
-        this.panned = true;
-        this.map.pan(
-            this.handler.last.x - xy.x,
-            this.handler.last.y - xy.y,
-            {dragging: this.handler.dragging, animate: false}
-        );
-    },
-    
-    /**
-     * Method: panMapDone
-     * Finish the panning operation.  Only call setCenter (through <panMap>)
-     *     if the map has actually been moved.
+     * APIMethod: forwardMercator
+     * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
      *
      * Parameters:
-     * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
-     */
-    panMapDone: function(xy) {
-        if(this.panned) {
-            this.panMap(xy);
-            this.panned = false;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Control.DragPan"
-});
-/* ======================================================================
-    OpenLayers/Feature/Vector.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-// TRASH THIS
-OpenLayers.State = {
-    /** states */
-    UNKNOWN: 'Unknown',
-    INSERT: 'Insert',
-    UPDATE: 'Update',
-    DELETE: 'Delete'
-};
-
-/**
- * @requires OpenLayers/Feature.js
- * @requires OpenLayers/Util.js
- */
-
-/**
- * Class: OpenLayers.Feature.Vector
- * Vector features use the OpenLayers.Geometry classes as geometry description.
- * They have an 'attributes' property, which is the data object, and a 'style'
- * property, the default values of which are defined in the 
- * <OpenLayers.Feature.Vector.style> objects.
- * 
- * Inherits from:
- *  - <OpenLayers.Feature>
- */
-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
-
-    /** 
-     * Property: fid 
-     * {String} 
-     */
-    fid: null,
-    
-    /** 
-     * APIProperty: geometry 
-     * {<OpenLayers.Geometry>} 
-     */
-    geometry: null,
-
-    /** 
-     * APIProperty: attributes 
-     * {Object} This object holds arbitrary, serializable properties that
-     *     describe the feature.
-     */
-    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} 
-     */
-    state: null,
-    
-    /** 
-     * APIProperty: style 
-     * {Object} 
-     */
-    style: null,
-
-    /**
-     * APIProperty: url
-     * {String} If this property is set it will be taken into account by
-     *     {<OpenLayers.HTTP>} when upadting or deleting the feature.
-     */
-    url: null,
-    
-    /**
-     * Property: renderIntent
-     * {String} rendering intent currently being used
-     */
-    renderIntent: "default",
-
-    /** 
-     * Constructor: OpenLayers.Feature.Vector
-     * Create a vector feature. 
+     * lon - {float} 
+     * lat - {float}
      * 
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} The geometry that this feature
-     *     represents.
-     * attributes - {Object} An optional object that will be mapped to the
-     *     <attributes> property. 
-     * style - {Object} An optional style object.
-     */
-    initialize: function(geometry, attributes, style) {
-        OpenLayers.Feature.prototype.initialize.apply(this,
-                                                      [null, null, attributes]);
-        this.lonlat = null;
-        this.geometry = geometry ? geometry : null;
-        this.state = null;
-        this.attributes = {};
-        if (attributes) {
-            this.attributes = OpenLayers.Util.extend(this.attributes,
-                                                     attributes);
-        }
-        this.style = style ? style : null; 
-    },
-    
-    /** 
-     * Method: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-        if (this.layer) {
-            this.layer.removeFeatures(this);
-            this.layer = null;
-        }
-            
-        this.geometry = null;
-        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
-    },
-    
-    /**
-     * Method: clone
-     * Create a clone of this vector feature.  Does not set any non-standard
-     *     properties.
-     *
      * Returns:
-     * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
+     * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
      */
-    clone: function () {
-        return new OpenLayers.Feature.Vector(
-            this.geometry ? this.geometry.clone() : null,
-            this.attributes,
-            this.style);
+    forwardMercator: function(lon, lat) {
+        var x = lon * 20037508.34 / 180;
+        var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
+
+        y = y * 20037508.34 / 180;
+        
+        return new OpenLayers.LonLat(x, y);
     },
 
     /**
-     * Method: onScreen
-     * Determine whether the feature is within the map viewport.  This method
-     *     tests for an intersection between the geometry and the viewport
-     *     bounds.  If a more effecient but less precise geometry bounds
-     *     intersection is desired, call the method with the boundsOnly
-     *     parameter true.
+     * APIMethod: inverseMercator
+     * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
      *
      * Parameters:
-     * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
-     *     the viewport bounds.  Default is false.  If false, the feature's
-     *     geometry must intersect the viewport for onScreen to return true.
+     * x - {float} A map x in Spherical Mercator.
+     * y - {float} A map y in Spherical Mercator.
      * 
      * Returns:
-     * {Boolean} The feature is currently visible on screen (optionally
-     *     based on its bounds if boundsOnly is true).
+     * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
      */
-    onScreen:function(boundsOnly) {
-        var onScreen = false;
-        if(this.layer && this.layer.map) {
-            var screenBounds = this.layer.map.getExtent();
-            if(boundsOnly) {
-                var featureBounds = this.geometry.getBounds();
-                onScreen = screenBounds.intersectsBounds(featureBounds);
-            } else {
-                var screenPoly = screenBounds.toGeometry();
-                onScreen = screenPoly.intersects(this.geometry);
-            }
-        }    
-        return onScreen;
-    },
+    inverseMercator: function(x, y) {
 
-    /**
-     * Method: getVisibility
-     * Determine whether the feature is displayed or not. It may not displayed
-     *     because:
-     *     - its style display property is set to 'none',
-     *     - it doesn't belong to any layer,
-     *     - the styleMap creates a symbolizer with display property set to 'none'
-     *          for it,
-     *     - the layer which it belongs to is not visible.
-     * 
-     * Returns:
-     * {Boolean} The feature is currently displayed.
-     */
-    getVisibility: function() {
-        return !(this.style && this.style.display == 'none' ||
-                 !this.layer ||
-                 this.layer && this.layer.styleMap &&
-                 this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
-                 this.layer && !this.layer.getVisibility());
-    },
-    
-    /**
-     * Method: createMarker
-     * HACK - we need to decide if all vector features should be able to
-     *     create markers
-     * 
-     * Returns:
-     * {<OpenLayers.Marker>} For now just returns null
-     */
-    createMarker: function() {
-        return null;
-    },
+        var lon = (x / 20037508.34) * 180;
+        var lat = (y / 20037508.34) * 180;
 
-    /**
-     * Method: destroyMarker
-     * HACK - we need to decide if all vector features should be able to
-     *     delete markers
-     * 
-     * If user overrides the createMarker() function, s/he should be able
-     *   to also specify an alternative function for destroying it
-     */
-    destroyMarker: function() {
-        // pass
+        lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
+        
+        return new OpenLayers.LonLat(lon, lat);
     },
 
     /**
-     * Method: createPopup
-     * HACK - we need to decide if all vector features should be able to
-     *     create popups
+     * Method: projectForward 
+     * Given an object with x and y properties in EPSG:4326, modify the x,y
+     * properties on the object to be the Spherical Mercator projected
+     * coordinates.
+     *
+     * Parameters:
+     * point - {Object} An object with x and y properties. 
      * 
      * Returns:
-     * {<OpenLayers.Popup>} For now just returns null
+     * {Object} The point, with the x and y properties transformed to spherical
+     * mercator.
      */
-    createPopup: function() {
-        return null;
+    projectForward: function(point) {
+        var lonlat = OpenLayers.Layer.SphericalMercator.forwardMercator(point.x, point.y);
+        point.x = lonlat.lon;
+        point.y = lonlat.lat;
+        return point;
     },
-
-    /**
-     * Method: atPoint
-     * Determins whether the feature intersects with the specified location.
-     * 
-     * Parameters: 
-     * lonlat - {<OpenLayers.LonLat>} 
-     * toleranceLon - {float} Optional tolerance in Geometric Coords
-     * toleranceLat - {float} Optional tolerance in Geographic Coords
-     * 
-     * Returns:
-     * {Boolean} Whether or not the feature is at the specified location
-     */
-    atPoint: function(lonlat, toleranceLon, toleranceLat) {
-        var atPoint = false;
-        if(this.geometry) {
-            atPoint = this.geometry.atPoint(lonlat, toleranceLon, 
-                                                    toleranceLat);
-        }
-        return atPoint;
-    },
-
-    /**
-     * Method: destroyPopup
-     * HACK - we need to decide if all vector features should be able to
-     * delete popups
-     */
-    destroyPopup: function() {
-        // pass
-    },
-
-    /**
-     * Method: move
-     * Moves the feature and redraws it at its new location
-     *
-     * Parameters:
-     * state - {OpenLayers.LonLat or OpenLayers.Pixel} the
-     *         location to which to move the feature.
-     */
-    move: function(location) {
-
-        if(!this.layer || !this.geometry.move){
-            //do nothing if no layer or immoveable geometry
-            return;
-        }
-
-        var pixel;
-        if (location.CLASS_NAME == "OpenLayers.LonLat") {
-            pixel = this.layer.getViewPortPxFromLonLat(location);
-        } else {
-            pixel = location;
-        }
-        
-        var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
-        var res = this.layer.map.getResolution();
-        this.geometry.move(res * (pixel.x - lastPixel.x),
-                           res * (lastPixel.y - pixel.y));
-        this.layer.drawFeature(this);
-        return lastPixel;
-    },
     
     /**
-     * Method: toState
-     * Sets the new state
+     * Method: projectInverse
+     * Given an object with x and y properties in Spherical Mercator, modify
+     * the x,y properties on the object to be the unprojected coordinates.
      *
      * Parameters:
-     * state - {String} 
-     */
-    toState: function(state) {
-        if (state == OpenLayers.State.UPDATE) {
-            switch (this.state) {
-                case OpenLayers.State.UNKNOWN:
-                case OpenLayers.State.DELETE:
-                    this.state = state;
-                    break;
-                case OpenLayers.State.UPDATE:
-                case OpenLayers.State.INSERT:
-                    break;
-            }
-        } else if (state == OpenLayers.State.INSERT) {
-            switch (this.state) {
-                case OpenLayers.State.UNKNOWN:
-                    break;
-                default:
-                    this.state = state;
-                    break;
-            }
-        } else if (state == OpenLayers.State.DELETE) {
-            switch (this.state) {
-                case OpenLayers.State.INSERT:
-                    // the feature should be destroyed
-                    break;
-                case OpenLayers.State.DELETE:
-                    break;
-                case OpenLayers.State.UNKNOWN:
-                case OpenLayers.State.UPDATE:
-                    this.state = state;
-                    break;
-            }
-        } else if (state == OpenLayers.State.UNKNOWN) {
-            this.state = state;
-        }
-    },
-    
-    CLASS_NAME: "OpenLayers.Feature.Vector"
-});
-
-
-/**
- * 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. 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
- *
- * Symbolizer properties:
- * fill - {Boolean} Set to false if no fill is desired.
- * fillColor - {String} Hex fill color.  Default is "#ee9900".
- * fillOpacity - {Number} Fill opacity (0-1).  Default is 0.4 
- * stroke - {Boolean} Set to false if no stroke is desired.
- * 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]
- * graphic - {Boolean} Set to false if no graphic is desired.
- * 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.
- * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
- * 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.
- * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
- *     fillText or mozDrawText to be available.
- * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
- *     composed of two characters. The first character is for the horizontal alignment, the second for the vertical
- *     alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
- *     alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". The canvas renderer does not
- *     support vertical alignment, it will always use "b".
- * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label.
- * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label.
- * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
- *     Default is false.
- * fontColor - {String} The font color for the label, to be provided like CSS.
- * fontOpacity - {Number} Opacity (0-1) for the label
- * fontFamily - {String} The font family for the label, to be provided like in CSS.
- * fontSize - {String} The font size for the label, to be provided like in CSS.
- * fontWeight - {String} The font weight for the label, to be provided like in CSS.
- * display - {String} Symbolizers will have no effect if display is set to "none".  All other values have no effect.
- */ 
-OpenLayers.Feature.Vector.style = {
-    'default': {
-        fillColor: "#ee9900",
-        fillOpacity: 0.4, 
-        hoverFillColor: "white",
-        hoverFillOpacity: 0.8,
-        strokeColor: "#ee9900",
-        strokeOpacity: 1,
-        strokeWidth: 1,
-        strokeLinecap: "round",
-        strokeDashstyle: "solid",
-        hoverStrokeColor: "red",
-        hoverStrokeOpacity: 1,
-        hoverStrokeWidth: 0.2,
-        pointRadius: 6,
-        hoverPointRadius: 1,
-        hoverPointUnit: "%",
-        pointerEvents: "visiblePainted",
-        cursor: "inherit"
-    },
-    'select': {
-        fillColor: "blue",
-        fillOpacity: 0.4, 
-        hoverFillColor: "white",
-        hoverFillOpacity: 0.8,
-        strokeColor: "blue",
-        strokeOpacity: 1,
-        strokeWidth: 2,
-        strokeLinecap: "round",
-        strokeDashstyle: "solid",
-        hoverStrokeColor: "red",
-        hoverStrokeOpacity: 1,
-        hoverStrokeWidth: 0.2,
-        pointRadius: 6,
-        hoverPointRadius: 1,
-        hoverPointUnit: "%",
-        pointerEvents: "visiblePainted",
-        cursor: "pointer"
-    },
-    'temporary': {
-        fillColor: "#66cccc",
-        fillOpacity: 0.2, 
-        hoverFillColor: "white",
-        hoverFillOpacity: 0.8,
-        strokeColor: "#66cccc",
-        strokeOpacity: 1,
-        strokeLinecap: "round",
-        strokeWidth: 2,
-        strokeDashstyle: "solid",
-        hoverStrokeColor: "red",
-        hoverStrokeOpacity: 1,
-        hoverStrokeWidth: 0.2,
-        pointRadius: 6,
-        hoverPointRadius: 1,
-        hoverPointUnit: "%",
-        pointerEvents: "visiblePainted",
-        cursor: "inherit"
-    },
-    'delete': {
-        display: "none"
-    }
-};    
-/* ======================================================================
-    OpenLayers/Handler/Box.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Handler.js
- * @requires OpenLayers/Handler/Drag.js
- */
-
-/**
- * Class: OpenLayers.Handler.Box
- * Handler for dragging a rectangle across the map.  Box is displayed 
- * on mouse down, moves on mouse move, and is finished on mouse up.
- *
- * Inherits from:
- *  - <OpenLayers.Handler> 
- */
-OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
-
-    /** 
-     * Property: dragHandler 
-     * {<OpenLayers.Handler.Drag>} 
-     */
-    dragHandler: null,
-
-    /**
-     * APIProperty: boxDivClassName
-     * {String} The CSS class to use for drawing the box. Default is
-     *     olHandlerBoxZoomBox
-     */
-    boxDivClassName: 'olHandlerBoxZoomBox',
-    
-    /**
-     * Property: boxCharacteristics
-     * {Object} Caches some box characteristics from css. This is used
-     *     by the getBoxCharacteristics method.
-     */
-    boxCharacteristics: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Box
-     *
-     * Parameters:
-     * control - {<OpenLayers.Control>} 
-     * callbacks - {Object} An object containing a single function to be
-     *                          called when the drag operation is finished.
-     *                          The callback should expect to recieve a single
-     *                          argument, the point geometry.
-     * options - {Object} 
-     */
-    initialize: function(control, callbacks, options) {
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        var callbacks = {
-            "down": this.startBox, 
-            "move": this.moveBox, 
-            "out":  this.removeBox,
-            "up":   this.endBox
-        };
-        this.dragHandler = new OpenLayers.Handler.Drag(
-                                this, callbacks, {keyMask: this.keyMask});
-    },
-
-    /**
-     * Method: destroy
-     */
-    destroy: function() {
-        if (this.dragHandler) {
-            this.dragHandler.destroy();
-            this.dragHandler = null;
-        }            
-        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
-    },
-
-    /**
-     * Method: setMap
-     */
-    setMap: function (map) {
-        OpenLayers.Handler.prototype.setMap.apply(this, arguments);
-        if (this.dragHandler) {
-            this.dragHandler.setMap(map);
-        }
-    },
-
-    /**
-    * Method: startBox
-    *
-    * Parameters:
-    * evt - {Event} 
-    */
-    startBox: function (xy) {
-        this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
-                                                 this.dragHandler.start);
-        this.zoomBox.className = this.boxDivClassName;                                         
-        this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
-        this.map.viewPortDiv.appendChild(this.zoomBox);
-
-        OpenLayers.Element.addClass(
-            this.map.viewPortDiv, "olDrawBox"
-        );
-    },
-
-    /**
-    * Method: moveBox
-    */
-    moveBox: function (xy) {
-        var startX = this.dragHandler.start.x;
-        var startY = this.dragHandler.start.y;
-        var deltaX = Math.abs(startX - xy.x);
-        var deltaY = Math.abs(startY - xy.y);
-        this.zoomBox.style.width = Math.max(1, deltaX) + "px";
-        this.zoomBox.style.height = Math.max(1, deltaY) + "px";
-        this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
-        this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
-
-        // depending on the box model, modify width and height to take borders
-        // of the box into account
-        var box = this.getBoxCharacteristics();
-        if (box.newBoxModel) {
-            if (xy.x > startX) {
-                this.zoomBox.style.width =
-                    Math.max(1, deltaX - box.xOffset) + "px";
-            }
-            if (xy.y > startY) {
-                this.zoomBox.style.height =
-                    Math.max(1, deltaY - box.yOffset) + "px";
-            }
-        }
-    },
-
-    /**
-    * Method: endBox
-    */
-    endBox: function(end) {
-        var result;
-        if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||    
-            Math.abs(this.dragHandler.start.y - end.y) > 5) {   
-            var start = this.dragHandler.start;
-            var top = Math.min(start.y, end.y);
-            var bottom = Math.max(start.y, end.y);
-            var left = Math.min(start.x, end.x);
-            var right = Math.max(start.x, end.x);
-            result = new OpenLayers.Bounds(left, bottom, right, top);
-        } else {
-            result = this.dragHandler.start.clone(); // i.e. OL.Pixel
-        } 
-        this.removeBox();
-
-        this.callback("done", [result]);
-    },
-
-    /**
-     * Method: removeBox
-     * Remove the zoombox from the screen and nullify our reference to it.
-     */
-    removeBox: function() {
-        this.map.viewPortDiv.removeChild(this.zoomBox);
-        this.zoomBox = null;
-        this.boxCharacteristics = null;
-        OpenLayers.Element.removeClass(
-            this.map.viewPortDiv, "olDrawBox"
-        );
-
-    },
-
-    /**
-     * Method: activate
-     */
-    activate: function () {
-        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            this.dragHandler.activate();
-            return true;
-        } else {
-            return false;
-        }
-    },
-
-    /**
-     * Method: deactivate
-     */
-    deactivate: function () {
-        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.dragHandler.deactivate();
-            return true;
-        } else {
-            return false;
-        }
-    },
-    
-    /**
-     * Method: getCharacteristics
-     * Determines offset and box model for a box.
+     * point - {Object} An object with x and y properties. 
      * 
      * 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
+     * {Object} The point, with the x and y properties transformed from
+     * spherical mercator to unprojected coordinates..
      */
-    getBoxCharacteristics: function() {
-        if (!this.boxCharacteristics) {
-            var xOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
-                "border-left-width")) + parseInt(OpenLayers.Element.getStyle(
-                this.zoomBox, "border-right-width")) + 1;
-            var yOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
-                "border-top-width")) + parseInt(OpenLayers.Element.getStyle(
-                this.zoomBox, "border-bottom-width")) + 1;
-            // all browsers use the new box model, except IE in quirks mode
-            var newBoxModel = OpenLayers.Util.getBrowserName() == "msie" ?
-                document.compatMode != "BackCompat" : true;
-            this.boxCharacteristics = {
-                xOffset: xOffset,
-                yOffset: yOffset,
-                newBoxModel: newBoxModel
-            };
-        }
-        return this.boxCharacteristics;
-    },
+    projectInverse: function(point) {
+        var lonlat = OpenLayers.Layer.SphericalMercator.inverseMercator(point.x, point.y);
+        point.x = lonlat.lon;
+        point.y = lonlat.lat;
+        return point;
+    }
 
-    CLASS_NAME: "OpenLayers.Handler.Box"
-});
-/* ======================================================================
-    OpenLayers/Handler/RegularPolygon.js
-   ====================================================================== */
+};
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
 /**
- * @requires OpenLayers/Handler/Drag.js
+ * Note: Transforms for web mercator <-> EPSG:4326
+ * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
+ * OpenLayers originally started referring to EPSG:900913 as web mercator.
+ * The EPSG has declared EPSG:3857 to be web mercator.  
+ * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as 
+ * equivalent.  See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084
  */
-
-/**
- * Class: OpenLayers.Handler.RegularPolygon
- * Handler to draw a regular polygon on the map.  Polygon is displayed on mouse
- *     down, moves or is modified 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.RegularPolygon> constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {
+(function() {
     
-    /**
-     * APIProperty: sides
-     * {Integer} Number of sides for the regular polygon.  Needs to be greater
-     *     than 2.  Defaults to 4.
-     */
-    sides: 4,
-
-    /**
-     * APIProperty: radius
-     * {Float} Optional radius in map units of the regular polygon.  If this is
-     *     set to some non-zero value, a polygon with a fixed radius will be
-     *     drawn and dragged with mose movements.  If this property is not
-     *     set, dragging changes the radius of the polygon.  Set to null by
-     *     default.
-     */
-    radius: null,
+    // list of equivalent codes for web mercator
+    var codes = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"];
     
-    /**
-     * APIProperty: snapAngle
-     * {Float} If set to a non-zero value, the handler will snap the polygon
-     *     rotation to multiples of the snapAngle.  Value is an angle measured
-     *     in degrees counterclockwise from the positive x-axis.  
-     */
-    snapAngle: null,
+    var add = OpenLayers.Projection.addTransform;
+    var merc = OpenLayers.Layer.SphericalMercator;
+    var same = OpenLayers.Projection.nullTransform;
     
-    /**
-     * APIProperty: snapToggle
-     * {String} If set, snapToggle is checked on mouse events and will set
-     *     the snap mode to the opposite of what it currently is.  To disallow
-     *     toggling between snap and non-snap mode, set freehandToggle to
-     *     null.  Acceptable toggle values are 'shiftKey', 'ctrlKey', and
-     *     'altKey'. Snap mode is only possible if this.snapAngle is set to a
-     *     non-zero value.
-     */
-    snapToggle: 'shiftKey',
-    
-    /**
-     * Property: layerOptions
-     * {Object} Any optional properties to be set on the sketch layer.
-     */
-    layerOptions: null,
-
-    /**
-     * APIProperty: persist
-     * {Boolean} Leave the feature rendered until clear is called.  Default
-     *     is false.  If set to true, the feature remains rendered until
-     *     clear is called, typically by deactivating the handler or starting
-     *     another drawing.
-     */
-    persist: false,
-
-    /**
-     * APIProperty: irregular
-     * {Boolean} Draw an irregular polygon instead of a regular polygon.
-     *     Default is false.  If true, the initial mouse down will represent
-     *     one corner of the polygon bounds and with each mouse movement, the
-     *     polygon will be stretched so the opposite corner of its bounds
-     *     follows the mouse position.  This property takes precedence over
-     *     the radius property.  If set to true, the radius property will
-     *     be ignored.
-     */
-    irregular: false,
-
-    /**
-     * Property: angle
-     * {Float} The angle from the origin (mouse down) to the current mouse
-     *     position, in radians.  This is measured counterclockwise from the
-     *     positive x-axis.
-     */
-    angle: null,
-
-    /**
-     * Property: fixedRadius
-     * {Boolean} The polygon has a fixed radius.  True if a radius is set before
-     *     drawing begins.  False otherwise.
-     */
-    fixedRadius: false,
-
-    /**
-     * Property: feature
-     * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature
-     */
-    feature: null,
-
-    /**
-     * Property: layer
-     * {<OpenLayers.Layer.Vector>} The temporary drawing layer
-     */
-    layer: null,
-
-    /**
-     * Property: origin
-     * {<OpenLayers.Geometry.Point>} Location of the first mouse down
-     */
-    origin: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.RegularPolygon
-     * Create a new regular polygon handler.
-     *
-     * Parameters:
-     * 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 object with properties to be set on the handler.
-     *     If the options.sides property is not specified, the number of sides
-     *     will default to 4.
-     *
-     * Named callbacks:
-     * create - Called when a sketch is first created.  Callback called with
-     *     the creation point geometry and sketch feature.
-     * done - Called when the sketch drawing is finished.  The callback will
-     *     recieve a single argument, the sketch geometry.
-     * cancel - Called when the handler is deactivated while drawing.  The
-     *     cancel callback will receive a geometry.
-     */
-    initialize: function(control, callbacks, options) {
-        if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
-            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+    var i, len, code, other, j;
+    for (i=0, len=codes.length; i<len; ++i) {
+        code = codes[i];
+        add("EPSG:4326", code, merc.projectForward);
+        add(code, "EPSG:4326", merc.projectInverse);
+        for (j=i+1; j<len; ++j) {
+            other = codes[j];
+            add(code, other, same);
+            add(other, code, same);
         }
-
-        OpenLayers.Handler.prototype.initialize.apply(this,
-                                                [control, callbacks, options]);
-        this.options = (options) ? options : {};
-    },
+    }
     
-    /**
-     * APIMethod: setOptions
-     * 
-     * Parameters:
-     * newOptions - {Object} 
-     */
-    setOptions: function (newOptions) {
-        OpenLayers.Util.extend(this.options, newOptions);
-        OpenLayers.Util.extend(this, newOptions);
-    },
-    
-    /**
-     * APIMethod: activate
-     * Turn on the handler.
-     *
-     * Return:
-     * {Boolean} The handler was successfully activated
-     */
-    activate: function() {
-        var activated = false;
-        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            // create temporary vector layer for rendering geometry sketch
-            var options = OpenLayers.Util.extend({
-                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: OpenLayers.Function.True
-            }, this.layerOptions);
-            this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
-            this.map.addLayer(this.layer);
-            activated = true;
-        }
-        return activated;
-    },
-
-    /**
-     * APIMethod: deactivate
-     * Turn off the handler.
-     *
-     * Return:
-     * {Boolean} The handler was successfully deactivated
-     */
-    deactivate: function() {
-        var deactivated = false;
-        if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {
-            // call the cancel callback if mid-drawing
-            if(this.dragging) {
-                this.cancel();
-            }
-            // If a layer's map property is set to null, it means that that
-            // layer isn't added to the map. Since we ourself added the layer
-            // to the map in activate(), we can assume that if this.layer.map
-            // is null it means that the layer has been destroyed (as a result
-            // of map.destroy() for example.
-            if (this.layer.map != null) {
-                this.layer.destroy(false);
-                if (this.feature) {
-                    this.feature.destroy();
-                }
-            }
-            this.layer = null;
-            this.feature = null;
-            deactivated = true;
-        }
-        return deactivated;
-    },
-    
-    /**
-     * Method: down
-     * Start drawing a new feature
-     *
-     * Parameters:
-     * evt - {Event} The drag start event
-     */
-    down: function(evt) {
-        this.fixedRadius = !!(this.radius);
-        var maploc = this.map.getLonLatFromPixel(evt.xy);
-        this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
-        // create the new polygon
-        if(!this.fixedRadius || this.irregular) {
-            // smallest radius should not be less one pixel in map units
-            // VML doesn't behave well with smaller
-            this.radius = this.map.getResolution();
-        }
-        if(this.persist) {
-            this.clear();
-        }
-        this.feature = new OpenLayers.Feature.Vector();
-        this.createGeometry();
-        this.callback("create", [this.origin, this.feature]);
-        this.layer.addFeatures([this.feature], {silent: true});
-        this.layer.drawFeature(this.feature, this.style);
-    },
-    
-    /**
-     * Method: move
-     * Respond to drag move events
-     *
-     * Parameters:
-     * evt - {Evt} The move event
-     */
-    move: function(evt) {
-        var maploc = this.map.getLonLatFromPixel(evt.xy);
-        var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
-        if(this.irregular) {
-            var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;
-            this.radius = Math.max(this.map.getResolution() / 2, ry);
-        } else if(this.fixedRadius) {
-            this.origin = point;
-        } else {
-            this.calculateAngle(point, evt);
-            this.radius = Math.max(this.map.getResolution() / 2,
-                                   point.distanceTo(this.origin));
-        }
-        this.modifyGeometry();
-        if(this.irregular) {
-            var dx = point.x - this.origin.x;
-            var dy = point.y - this.origin.y;
-            var ratio;
-            if(dy == 0) {
-                ratio = dx / (this.radius * Math.sqrt(2));
-            } else {
-                ratio = dx / dy;
-            }
-            this.feature.geometry.resize(1, this.origin, ratio);
-            this.feature.geometry.move(dx / 2, dy / 2);
-        }
-        this.layer.drawFeature(this.feature, this.style);
-    },
-
-    /**
-     * Method: up
-     * Finish drawing the feature
-     *
-     * Parameters:
-     * evt - {Event} The mouse up event
-     */
-    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]);
-        }
-    },
-
-    /**
-     * Method: out
-     * Finish drawing the feature.
-     *
-     * Parameters:
-     * evt - {Event} The mouse out event
-     */
-    out: function(evt) {
-        this.finalize();
-    },
-
-    /**
-     * Method: createGeometry
-     * Create the new polygon geometry.  This is called at the start of the
-     *     drag and at any point during the drag if the number of sides
-     *     changes.
-     */
-    createGeometry: function() {
-        this.angle = Math.PI * ((1/this.sides) - (1/2));
-        if(this.snapAngle) {
-            this.angle += this.snapAngle * (Math.PI / 180);
-        }
-        this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
-            this.origin, this.radius, this.sides, this.snapAngle
-        );
-    },
-    
-    /**
-     * Method: modifyGeometry
-     * Modify the polygon geometry in place.
-     */
-    modifyGeometry: function() {
-        var angle, point;
-        var ring = this.feature.geometry.components[0];
-        // if the number of sides ever changes, create a new geometry
-        if(ring.components.length != (this.sides + 1)) {
-            this.createGeometry();
-            ring = this.feature.geometry.components[0];
-        }
-        for(var i=0; i<this.sides; ++i) {
-            point = ring.components[i];
-            angle = this.angle + (i * 2 * Math.PI / this.sides);
-            point.x = this.origin.x + (this.radius * Math.cos(angle));
-            point.y = this.origin.y + (this.radius * Math.sin(angle));
-            point.clearBounds();
-        }
-    },
-    
-    /**
-     * Method: calculateAngle
-     * Calculate the angle based on settings.
-     *
-     * Parameters:
-     * point - {<OpenLayers.Geometry.Point>}
-     * evt - {Event}
-     */
-    calculateAngle: function(point, evt) {
-        var alpha = Math.atan2(point.y - this.origin.y,
-                               point.x - this.origin.x);
-        if(this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {
-            var snapAngleRad = (Math.PI / 180) * this.snapAngle;
-            this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;
-        } else {
-            this.angle = alpha;
-        }
-    },
-
-    /**
-     * APIMethod: cancel
-     * Finish the geometry and call the "cancel" callback.
-     */
-    cancel: function() {
-        // the polygon geometry gets cloned in the callback method
-        this.callback("cancel", null);
-        this.finalize();
-    },
-
-    /**
-     * Method: finalize
-     * Finish the geometry and call the "done" callback.
-     */
-    finalize: function() {
-        this.origin = null;
-        this.radius = this.options.radius;
-    },
-
-    /**
-     * APIMethod: clear
-     * Clear any rendered features on the temporary layer.  This is called
-     *     when the handler is deactivated, canceled, or done (unless persist
-     *     is true).
-     */
-    clear: function() {
-        if (this.layer) {
-            this.layer.renderer.clear();
-            this.layer.destroyFeatures();
-        }
-    },
-    
-    /**
-     * Method: callback
-     * Trigger the control's named callback with the given arguments
-     *
-     * Parameters:
-     * name - {String} The key for the callback that is one of the properties
-     *     of the handler's callbacks object.
-     * args - {Array} An array of arguments with which to call the callback
-     *     (defined by the control).
-     */
-    callback: function (name, args) {
-        // override the callback method to always send the polygon geometry
-        if (this.callbacks[name]) {
-            this.callbacks[name].apply(this.control,
-                                       [this.feature.geometry.clone()]);
-        }
-        // since sketch features are added to the temporary layer
-        // they must be cleared here if done or cancel
-        if(!this.persist && (name == "done" || name == "cancel")) {
-            this.clear();
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
-});
+})();
 /* ======================================================================
     OpenLayers/Layer/EventPane.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -25472,7 +10124,7 @@
         this.pane.style.display = this.div.style.display;
         this.pane.style.width="100%";
         this.pane.style.height="100%";
-        if (OpenLayers.Util.getBrowserName() == "msie") {
+        if (OpenLayers.BROWSER_NAME == "msie") {
             this.pane.style.background = 
                 "url(" + OpenLayers.Util.getImagesLocation() + "blank.gif)";
         }
@@ -25790,7 +10442,7 @@
     OpenLayers/Layer/FixedZoomLevels.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -26106,1551 +10758,383 @@
 });
 
 /* ======================================================================
-    OpenLayers/Layer/HTTPRequest.js
+    OpenLayers/Layer/VirtualEarth.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 
 /**
- * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Layer/SphericalMercator.js
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
- * Class: OpenLayers.Layer.HTTPRequest
+ * Class: OpenLayers.Layer.VirtualEarth
  * 
- * Inherits from: 
- *  - <OpenLayers.Layer>
+ * Inherits from:
+ *  - <OpenLayers.Layer.EventPane>
+ *  - <OpenLayers.Layer.FixedZoomLevels>
  */
-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
-
+OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
+    OpenLayers.Layer.EventPane,
+    OpenLayers.Layer.FixedZoomLevels, {
+    
     /** 
-     * Constant: URL_HASH_FACTOR
-     * {Float} Used to hash URL param strings for multi-WMS server selection.
-     *         Set to the Golden Ratio per Knuth's recommendation.
+     * Constant: MIN_ZOOM_LEVEL
+     * {Integer} 1 
      */
-    URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
-
+    MIN_ZOOM_LEVEL: 1,
+    
     /** 
-     * Property: url
-     * {Array(String) or String} This is either an array of url strings or 
-     *                           a single url string. 
+     * Constant: MAX_ZOOM_LEVEL
+     * {Integer} 19
      */
-    url: null,
+    MAX_ZOOM_LEVEL: 19,
 
     /** 
-     * Property: params
-     * {Object} Hashtable of key/value parameters
+     * Constant: RESOLUTIONS
+     * {Array(Float)} Hardcode these resolutions so that they are more closely
+     *                tied with the standard wms projection
      */
-    params: null,
-    
-    /** 
-     * APIProperty: reproject
-     * *Deprecated*. See http://trac.openlayers.org/wiki/SpatialMercator
-     * for information on the replacement for this functionality. 
-     * {Boolean} Whether layer should reproject itself based on base layer 
-     *           locations. This allows reprojection onto commercial layers. 
-     *           Default is false: Most layers can't reproject, but layers 
-     *           which can create non-square geographic pixels can, like WMS.
-     *           
-     */
-    reproject: false,
+    RESOLUTIONS: [
+        1.40625, 
+        0.703125, 
+        0.3515625, 
+        0.17578125, 
+        0.087890625, 
+        0.0439453125,
+        0.02197265625, 
+        0.010986328125, 
+        0.0054931640625, 
+        0.00274658203125,
+        0.001373291015625, 
+        0.0006866455078125, 
+        0.00034332275390625, 
+        0.000171661376953125, 
+        0.0000858306884765625, 
+        0.00004291534423828125,
+        0.00002145767211914062, 
+        0.00001072883605957031,
+        0.00000536441802978515
+    ],
 
     /**
-     * Constructor: OpenLayers.Layer.HTTPRequest
-     * 
-     * Parameters:
-     * name - {String}
-     * url - {Array(String) or String}
-     * params - {Object}
-     * options - {Object} Hashtable of extra options to tag onto the layer
+     * APIProperty: type
+     * {VEMapType}
      */
-    initialize: function(name, url, params, options) {
-        var newArguments = arguments;
-        newArguments = [name, options];
-        OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
-        this.url = url;
-        this.params = OpenLayers.Util.extend( {}, params);
-    },
+    type: null,
 
     /**
-     * APIMethod: destroy
+     * APIProperty: wrapDateLine
+     * {Boolean} Allow user to pan forever east/west.  Default is true.  
+     *     Setting this to false only restricts panning if 
+     *     <sphericalMercator> is true. 
      */
-    destroy: function() {
-        this.url = null;
-        this.params = null;
-        OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
-    },
-    
-    /**
-     * APIMethod: clone
-     * 
-     * Parameters:
-     * obj - {Object}
-     * 
-     * Returns:
-     * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this 
-     *                                  <OpenLayers.Layer.HTTPRequest>
-     */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.HTTPRequest(this.name,
-                                                   this.url,
-                                                   this.params,
-                                                   this.getOptions());
-        }
-        
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+    wrapDateLine: true,
 
-        // copy/set any non-init, non-simple values here
-        
-        return obj;
-    },
-
-    /** 
-     * APIMethod: setUrl
-     * 
-     * Parameters:
-     * newUrl - {String}
-     */
-    setUrl: function(newUrl) {
-        this.url = newUrl;
-    },
-
     /**
-     * APIMethod: mergeNewParams
-     * 
-     * Parameters:
-     * newParams - {Object}
-     *
-     * Returns:
-     * redrawn: {Boolean} whether the layer was actually redrawn.
+     * APIProperty: sphericalMercator
+     * {Boolean} Should the map act as a mercator-projected map? This will
+     *     cause all interactions with the map to be in the actual map
+     *     projection, which allows support for vector drawing, overlaying
+     *     other maps, etc. 
      */
-    mergeNewParams:function(newParams) {
-        this.params = OpenLayers.Util.extend(this.params, newParams);
-        var ret = this.redraw();
-        if(this.map != null) {
-            this.map.events.triggerEvent("changelayer", {
-                layer: this,
-                property: "params"
-            });
-        }
-        return ret;
-    },
-
-    /**
-     * APIMethod: redraw
-     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
-     *
-     * Parameters:
-     * force - {Boolean} Force redraw by adding random parameter.
-     *
-     * Returns:
-     * {Boolean} The layer was redrawn.
-     */
-    redraw: function(force) { 
-        if (force) {
-            return this.mergeNewParams({"_olSalt": Math.random()});
-        } else {
-            return OpenLayers.Layer.prototype.redraw.apply(this, []);
-        }
-    },
+    sphericalMercator: false,
     
     /**
-     * Method: selectUrl
-     * selectUrl() implements the standard floating-point multiplicative
-     *     hash function described by Knuth, and hashes the contents of the 
-     *     given param string into a float between 0 and 1. This float is then
-     *     scaled to the size of the provided urls array, and used to select
-     *     a URL.
-     *
-     * Parameters:
-     * paramString - {String}
-     * urls - {Array(String)}
-     * 
-     * Returns:
-     * {String} An entry from the urls array, deterministically selected based
-     *          on the paramString.
+     * APIProperty: animationEnabled
+     * {Boolean} If set to true, the transition between zoom levels will be
+     *     animated. Set to false to match the zooming experience of other
+     *     layer types. Default is true.
      */
-    selectUrl: function(paramString, urls) {
-        var product = 1;
-        for (var i=0, len=paramString.length; i<len; i++) { 
-            product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; 
-            product -= Math.floor(product); 
-        }
-        return urls[Math.floor(product * urls.length)];
-    },
+    animationEnabled: true, 
 
     /** 
-     * Method: getFullRequestString
-     * Combine url with layer's params and these newParams. 
-     *   
-     *    does checking on the serverPath variable, allowing for cases when it 
-     *     is supplied with trailing ? or &, as well as cases where not. 
-     *
-     *    return in formatted string like this:
-     *        "server?key1=value1&key2=value2&key3=value3"
+     * Constructor: OpenLayers.Layer.VirtualEarth
      * 
-     * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
-     *
      * Parameters:
-     * newParams - {Object}
-     * altUrl - {String} Use this as the url instead of the layer's url
-     *   
-     * Returns: 
-     * {String}
+     * name - {String}
+     * options - {Object}
      */
-    getFullRequestString:function(newParams, altUrl) {
-
-        // if not altUrl passed in, use layer's url
-        var url = altUrl || this.url;
-        
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // if url is not a string, it should be an array of strings, 
-        // in which case we will deterministically select one of them in 
-        // order to evenly distribute requests to different urls.
-        //
-        if (url instanceof Array) {
-            url = this.selectUrl(paramsString, url);
-        }   
- 
-        // ignore parameters that are already in the url search string
-        var urlParams = 
-            OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
-            }
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+        if(this.sphericalMercator) {
+            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+            this.initMercatorParameters();
         }
-        paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        return OpenLayers.Util.urlAppend(url, paramsString);
     },
-
-    CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
-});
-/* ======================================================================
-    OpenLayers/Layer/Markers.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer.js
- */
-
-/**
- * Class: OpenLayers.Layer.Markers
- * 
- * Inherits from:
- *  - <OpenLayers.Layer> 
- */
-OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
     
-    /** 
-     * APIProperty: isBaseLayer 
-     * {Boolean} Markers layer is never a base layer.  
+    /**
+     * Method: loadMapObject
      */
-    isBaseLayer: false,
-    
-    /** 
-     * APIProperty: markers 
-     * {Array(<OpenLayers.Marker>)} internal marker list 
-     */
-    markers: null,
+    loadMapObject:function() {
 
+        // create div and set to same size as map
+        var veDiv = OpenLayers.Util.createDiv(this.name);
+        var sz = this.map.getSize();
+        veDiv.style.width = sz.w + "px";
+        veDiv.style.height = sz.h + "px";
+        this.div.appendChild(veDiv);
 
-    /** 
-     * Property: drawn 
-     * {Boolean} internal state of drawing. This is a workaround for the fact
-     * that the map does not call moveTo with a zoomChanged when the map is
-     * first starting up. This lets us catch the case where we have *never*
-     * drawn the layer, and draw it even if the zoom hasn't changed.
-     */
-    drawn: false,
-    
-    /**
-     * Constructor: OpenLayers.Layer.Markers 
-     * Create a Markers layer.
-     *
-     * Parameters:
-     * name - {String} 
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, options) {
-        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
-        this.markers = [];
-    },
-    
-    /**
-     * APIMethod: destroy 
-     */
-    destroy: function() {
-        this.clearMarkers();
-        this.markers = null;
-        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
-    },
+        try { // crash prevention
+            this.mapObject = new VEMap(this.name);
+        } catch (e) { }
 
-    /**
-     * APIMethod: setOpacity
-     * Sets the opacity for all the markers.
-     * 
-     * Parameter:
-     * opacity - {Float}
-     */
-    setOpacity: function(opacity) {
-        if (opacity != this.opacity) {
-            this.opacity = opacity;
-            for (var i=0, len=this.markers.length; i<len; i++) {
-                this.markers[i].setOpacity(this.opacity);
-            }
-        }
-    },
+        if (this.mapObject != null) {
+            try { // this is to catch a Mozilla bug without falling apart
 
-    /** 
-     * Method: moveTo
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} 
-     * zoomChanged - {Boolean} 
-     * dragging - {Boolean} 
-     */
-    moveTo:function(bounds, zoomChanged, dragging) {
-        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+                // The fourth argument is whether the map is 'fixed' -- not 
+                // draggable. See: 
+                // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
+                //
+                this.mapObject.LoadMap(null, null, this.type, true);
+                this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
 
-        if (zoomChanged || !this.drawn) {
-            for(var i=0, len=this.markers.length; i<len; i++) {
-                this.drawMarker(this.markers[i]);
+            } catch (e) { }
+            this.mapObject.HideDashboard();
+            if(typeof this.mapObject.SetAnimationEnabled == "function") {
+                this.mapObject.SetAnimationEnabled(this.animationEnabled);
             }
-            this.drawn = true;
         }
-    },
 
-    /**
-     * APIMethod: addMarker
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
-     */
-    addMarker: function(marker) {
-        this.markers.push(marker);
+        //can we do smooth panning? this is an unpublished method, so we need 
+        // to be careful
+        if ( !this.mapObject ||
+             !this.mapObject.vemapcontrol ||
+             !this.mapObject.vemapcontrol.PanMap ||
+             (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
 
-        if (this.opacity != null) {
-            marker.setOpacity(this.opacity);
+            this.dragPanMapObject = null;
         }
 
-        if (this.map && this.map.getExtent()) {
-            marker.map = this.map;
-            this.drawMarker(marker);
-        }
     },
 
     /**
-     * APIMethod: removeMarker
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
+     * Method: onMapResize
      */
-    removeMarker: function(marker) {
-        if (this.markers && this.markers.length) {
-            OpenLayers.Util.removeItem(this.markers, marker);
-            marker.erase();
-        }
+    onMapResize: function() {
+        this.mapObject.Resize(this.map.size.w, this.map.size.h);
     },
 
-    /**
-     * Method: clearMarkers
-     * This method removes all markers from a layer. The markers are not
-     * destroyed by this function, but are removed from the list of markers.
-     */
-    clearMarkers: function() {
-        if (this.markers != null) {
-            while(this.markers.length > 0) {
-                this.removeMarker(this.markers[0]);
-            }
-        }
-    },
-
     /** 
-     * Method: drawMarker
-     * Calculate the pixel location for the marker, create it, and 
-     *    add it to the layer's div
-     *
-     * Parameters:
-     * marker - {<OpenLayers.Marker>} 
-     */
-    drawMarker: function(marker) {
-        var px = this.map.getLayerPxFromLonLat(marker.lonlat);
-        if (px == null) {
-            marker.display(false);
-        } else {
-            if (!marker.isDrawn()) {
-                var markerImg = marker.draw(px);
-                this.div.appendChild(markerImg);
-            } else if(marker.icon) {
-                marker.icon.moveTo(px);
-            }
-        }
-    },
-    
-    /** 
-     * APIMethod: getDataExtent
-     * Calculates the max extent which includes all of the markers.
+     * APIMethod: getWarningHTML
      * 
-     * Returns:
-     * {<OpenLayers.Bounds>}
+     * Returns: 
+     * {String} String with information on why layer is broken, how to get
+     *          it working.
      */
-    getDataExtent: function () {
-        var maxExtent = null;
-        
-        if ( this.markers && (this.markers.length > 0)) {
-            var maxExtent = new OpenLayers.Bounds();
-            for(var i=0, len=this.markers.length; i<len; i++) {
-                var marker = this.markers[i];
-                maxExtent.extend(marker.lonlat);
-            }
-        }
-
-        return maxExtent;
+    getWarningHTML:function() {
+        return OpenLayers.i18n(
+            "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
+        );
     },
 
-    CLASS_NAME: "OpenLayers.Layer.Markers"
-});
-/* ======================================================================
-    OpenLayers/Layer/SphericalMercator.js
-   ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
 
-/**
- * @requires OpenLayers/Layer.js
- * @requires OpenLayers/Projection.js
- */
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
 
-/**
- * Class: OpenLayers.Layer.SphericalMercator
- * A mixin for layers that wraps up the pieces neccesary to have a coordinate
- *     conversion for working with commercial APIs which use a spherical
- *     mercator projection.  Using this layer as a base layer, additional
- *     layers can be used as overlays if they are in the same projection.
- *
- * A layer is given properties of this object by setting the sphericalMercator
- *     property to true.
- *
- * More projection information:
- *  - http://spatialreference.org/ref/user/google-projection/
- *
- * Proj4 Text:
- *     +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
- *     +k=1.0 +units=m +nadgrids=@null +no_defs
- *
- * WKT:
- *     900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
- *     DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], 
- *     PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], 
- *     AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
- *     PROJECTION["Mercator_1SP_Google"], 
- *     PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], 
- *     PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], 
- *     PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
- *     AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
- */
-OpenLayers.Layer.SphericalMercator = {
 
-    /**
-     * Method: getExtent
-     * Get the map's extent.
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} The map extent.
+  // Get&Set Center, Zoom
+
+    /** 
+     * APIMethod: setMapObjectCenter
+     * Set the mapObject to the specified center and zoom
+     * 
+     * Parameters:
+     * center - {Object} MapObject LonLat format
+     * zoom - {int} MapObject zoom format
      */
-    getExtent: function() {
-        var extent = null;
-        if (this.sphericalMercator) {
-            extent = this.map.calculateBounds();
-        } else {
-            extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
-        }
-        return extent;
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.SetCenterAndZoom(center, zoom); 
     },
-
+   
     /**
-     * Method: getLonLatFromViewPortPx
-     * Get a map location from a pixel location
+     * APIMethod: getMapObjectCenter
      * 
-     * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     *  {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
-     *  port OpenLayers.Pixel, translated into lon/lat by map lib
-     *  If the map lib is not loaded or not centered, returns null
+     * Returns: 
+     * {Object} The mapObject's current center in Map Object format
      */
-    getLonLatFromViewPortPx: function (viewPortPx) {
-        return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
+    getMapObjectCenter: function() {
+        return this.mapObject.GetCenter();
     },
-    
+
     /**
-     * Method: getViewPortPxFromLonLat
-     * Get a pixel location from a map location
-     *
+     * APIMethod: dragPanMapObject
+     * 
      * Parameters:
-     * lonlat - {<OpenLayers.LonLat>}
-     *
-     * Returns:
-     * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
-     * OpenLayers.LonLat, translated into view port pixels by map lib
-     * If map lib is not loaded or not centered, returns null
+     * dX - {Integer}
+     * dY - {Integer}
      */
-    getViewPortPxFromLonLat: function (lonlat) {
-        return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
+    dragPanMapObject: function(dX, dY) {
+        this.mapObject.vemapcontrol.PanMap(dX, -dY);
     },
 
     /** 
-     * Method: initMercatorParameters 
-     * Set up the mercator parameters on the layer: resolutions,
-     *     projection, units.
-     */
-    initMercatorParameters: function() {
-        // set up properties for Mercator - assume EPSG:900913
-        this.RESOLUTIONS = [];
-        var maxResolution = 156543.0339;
-        for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
-            this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
-        }
-        this.units = "m";
-        this.projection = this.projection || "EPSG:900913";
-    },
-
-    /**
-     * APIMethod: forwardMercator
-     * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
-     *
-     * Parameters:
-     * lon - {float} 
-     * lat - {float}
+     * APIMethod: getMapObjectZoom
      * 
      * Returns:
-     * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
+     * {Integer} The mapObject's current zoom, in Map Object format
      */
-    forwardMercator: function(lon, lat) {
-        var x = lon * 20037508.34 / 180;
-        var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
-
-        y = y * 20037508.34 / 180;
-        
-        return new OpenLayers.LonLat(x, y);
+    getMapObjectZoom: function() {
+        return this.mapObject.GetZoomLevel();
     },
 
+
+  // LonLat - Pixel Translation
+  
     /**
-     * APIMethod: inverseMercator
-     * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
-     *
+     * APIMethod: getMapObjectLonLatFromMapObjectPixel
+     * 
      * Parameters:
-     * x - {float} A map x in Spherical Mercator.
-     * y - {float} A map y in Spherical Mercator.
+     * moPixel - {Object} MapObject Pixel format
      * 
      * Returns:
-     * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
+     * {Object} MapObject LonLat translated from MapObject Pixel
      */
-    inverseMercator: function(x, y) {
-
-        var lon = (x / 20037508.34) * 180;
-        var lat = (y / 20037508.34) * 180;
-
-        lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
-        
-        return new OpenLayers.LonLat(lon, lat);
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        //the conditional here is to test if we are running the v6 of VE
+        return (typeof VEPixel != 'undefined') 
+            ? this.mapObject.PixelToLatLong(moPixel)
+            : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
     },
 
     /**
-     * Method: projectForward 
-     * Given an object with x and y properties in EPSG:4326, modify the x,y
-     * properties on the object to be the Spherical Mercator projected
-     * coordinates.
-     *
+     * APIMethod: getMapObjectPixelFromMapObjectLonLat
+     * 
      * Parameters:
-     * point - {Object} An object with x and y properties. 
+     * moLonLat - {Object} MapObject LonLat format
      * 
      * Returns:
-     * {Object} The point, with the x and y properties transformed to spherical
-     * mercator.
+     * {Object} MapObject Pixel transtlated from MapObject LonLat
      */
-    projectForward: function(point) {
-        var lonlat = OpenLayers.Layer.SphericalMercator.forwardMercator(point.x, point.y);
-        point.x = lonlat.lon;
-        point.y = lonlat.lat;
-        return point;
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.LatLongToPixel(moLonLat);
     },
-    
-    /**
-     * Method: projectInverse
-     * Given an object with x and y properties in Spherical Mercator, modify
-     * the x,y properties on the object to be the unprojected coordinates.
-     *
-     * Parameters:
-     * point - {Object} An object with x and y properties. 
-     * 
-     * Returns:
-     * {Object} The point, with the x and y properties transformed from
-     * spherical mercator to unprojected coordinates..
-     */
-    projectInverse: function(point) {
-        var lonlat = OpenLayers.Layer.SphericalMercator.inverseMercator(point.x, point.y);
-        point.x = lonlat.lon;
-        point.y = lonlat.lat;
-        return point;
-    }
 
-};
 
-/**
- * Note: Two transforms declared
- * Transforms from EPSG:4326 to EPSG:900913 and from EPSG:900913 to EPSG:4326
- *     are set by this class.
- */
-OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:900913",
-    OpenLayers.Layer.SphericalMercator.projectForward);
-OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:4326",
-    OpenLayers.Layer.SphericalMercator.projectInverse);
-/* ======================================================================
-    OpenLayers/Control/DrawFeature.js
-   ====================================================================== */
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
 
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Feature/Vector.js
- */
-
-/**
- * Class: OpenLayers.Control.DrawFeature
- * The DrawFeature control draws point, line or polygon features on a vector
- * layer when active.
- *
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
+  // LonLat
     
     /**
-     * Property: layer
-     * {<OpenLayers.Layer.Vector>}
-     */
-    layer: null,
-
-    /**
-     * Property: callbacks
-     * {Object} The functions that are sent to the handler for callback
-     */
-    callbacks: null,
-    
-    /**
-     * Constant: EVENT_TYPES
-     *
-     * Supported event types:
-     * featureadded - Triggered when a feature is added
-     */
-    EVENT_TYPES: ["featureadded"],
-    
-    /**
-     * APIProperty: multi
-     * {Boolean} Cast features to multi-part geometries before passing to the
-     *     layer.  Default is false.
-     */
-    multi: false,
-
-    /**
-     * APIProperty: featureAdded
-     * {Function} Called after each feature is added
-     */
-    featureAdded: function() {},
-
-    /**
-     * APIProperty: handlerOptions
-     * {Object} Used to set non-default properties on the control's handler
-     */
-    handlerOptions: null,
-    
-    /**
-     * Constructor: OpenLayers.Control.DrawFeature
+     * APIMethod: getLongitudeFromMapObjectLonLat
      * 
      * Parameters:
-     * layer - {<OpenLayers.Layer.Vector>} 
-     * handler - {<OpenLayers.Handler>} 
-     * options - {Object} 
+     * moLonLat - {Object} MapObject LonLat format
+     * 
+     * Returns:
+     * {Float} Longitude of the given MapObject LonLat
      */
-    initialize: function(layer, handler, options) {
-        
-        // concatenate events specific to vector with those from the base
-        this.EVENT_TYPES =
-            OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(
-            OpenLayers.Control.prototype.EVENT_TYPES
-        );
-        
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);
-        this.callbacks = OpenLayers.Util.extend(
-            {
-                done: this.drawFeature,
-                modify: function(vertex, feature) {
-                    this.layer.events.triggerEvent(
-                        "sketchmodified", {vertex: vertex, feature: feature}
-                    );
-                },
-                create: function(vertex, feature) {
-                    this.layer.events.triggerEvent(
-                        "sketchstarted", {vertex: vertex, feature: feature}
-                    );
-                }
-            },
-            this.callbacks
-        );
-        this.layer = layer;
-        this.handlerOptions = this.handlerOptions || {};
-        if (!("multi" in this.handlerOptions)) {
-            this.handlerOptions.multi = this.multi;
-        }
-        var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
-        if(sketchStyle) {
-            this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
-                this.handlerOptions.layerOptions,
-                {styleMap: new OpenLayers.StyleMap({"default": sketchStyle})}
-            );
-        }
-        this.handler = new handler(this, this.callbacks, this.handlerOptions);
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return this.sphericalMercator ? 
+            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
+            moLonLat.Longitude;
     },
 
     /**
-     * Method: drawFeature
-     */
-    drawFeature: function(geometry) {
-        var feature = new OpenLayers.Feature.Vector(geometry);
-        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"
-});
-/* ======================================================================
-    OpenLayers/Control/Measure.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Feature/Vector.js
- */
-
-/**
- * Class: OpenLayers.Control.Measure
- * Allows for drawing of features for measurements.
- *
- * Inherits from:
- *  - <OpenLayers.Control>
- */
-OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
-
-    /**
-     * 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.
-     *
-     * Supported control event types (in addition to those from <OpenLayers.Control>):
-     * 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
-     *      measurement sketch.  Listeners receive an event with measure,
-     *      units, order, and geometry.
-     */
-    EVENT_TYPES: ['measure', 'measurepartial'],
-
-    /**
-     * APIProperty: handlerOptions
-     * {Object} Used to set non-default properties on the control's handler
-     */
-    handlerOptions: null,
-    
-    /**
-     * Property: callbacks
-     * {Object} The functions that are sent to the handler for callback
-     */
-    callbacks: null,
-    
-    /**
-     * Property: displaySystem
-     * {String} Display system for output measurements.  Supported values
-     *     are 'english', 'metric', and 'geographic'.  Default is 'metric'.
-     */
-    displaySystem: 'metric',
-    
-    /**
-     * Property: geodesic
-     * {Boolean} Calculate geodesic metrics instead of planar metrics.  This
-     *     requires that geometries can be transformed into Geographic/WGS84
-     *     (if that is not already the map projection).  Default is false.
-     */
-    geodesic: false,
-    
-    /**
-     * Property: displaySystemUnits
-     * {Object} Units for various measurement systems.  Values are arrays
-     *     of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
-     *     order of length.
-     */
-    displaySystemUnits: {
-        geographic: ['dd'],
-        english: ['mi', 'ft', 'in'],
-        metric: ['km', 'm']
-    },
-
-    /**
-     * Property: delay
-     * {Number} Number of milliseconds between clicks before the event is
-     *     considered a double-click.  The "measurepartial" event will not
-     *     be triggered if the sketch is completed within this time.  This
-     *     is required for IE where creating a browser reflow (if a listener
-     *     is modifying the DOM by displaying the measurement values) messes
-     *     with the dblclick listener in the sketch handler.
-     */
-    partialDelay: 300,
-
-    /**
-     * Property: delayedTrigger
-     * {Number} Timeout id of trigger for measurepartial.
-     */
-    delayedTrigger: null,
-    
-    /**
-     * APIProperty: persist
-     * {Boolean} Keep the temporary measurement sketch drawn after the
-     *     measurement is complete.  The geometry will persist until a new
-     *     measurement is started, the control is deactivated, or <cancel> is
-     *     called.
-     */
-    persist: false,
-
-    /**
-     * Constructor: OpenLayers.Control.Measure
+     * APIMethod: getLatitudeFromMapObjectLonLat
      * 
      * Parameters:
-     * handler - {<OpenLayers.Handler>} 
-     * options - {Object} 
-     */
-    initialize: function(handler, options) {
-        // concatenate events specific to measure with those from the base
-        this.EVENT_TYPES =
-            OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(
-            OpenLayers.Control.prototype.EVENT_TYPES
-        );
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);
-        this.callbacks = OpenLayers.Util.extend(
-            {done: this.measureComplete, point: this.measurePartial},
-            this.callbacks
-        );
-
-        // let the handler options override, so old code that passes 'persist' 
-        // directly to the handler does not need an update
-        this.handlerOptions = OpenLayers.Util.extend(
-            {persist: this.persist}, this.handlerOptions
-        );
-        this.handler = new handler(this, this.callbacks, this.handlerOptions);
-    },
-    
-    /**
-     * APIMethod: cancel
-     * Stop the control from measuring.  If <persist> is true, the temporary
-     *     sketch will be erased.
-     */
-    cancel: function() {
-        this.handler.cancel();
-    },
-    
-    /**
-     * Method: updateHandler
-     *
-     * Parameters:
-     * handler - {Function} One of the sketch handler constructors.
-     * options - {Object} Options for the handler.
-     */
-    updateHandler: function(handler, options) {
-        var active = this.active;
-        if(active) {
-            this.deactivate();
-        }
-        this.handler = new handler(this, this.callbacks, options);
-        if(active) {
-            this.activate();
-        }
-    },
-
-    /**
-     * Method: measureComplete
-     * Called when the measurement sketch is done.
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     */
-    measureComplete: function(geometry) {
-        if(this.delayedTrigger) {
-            window.clearTimeout(this.delayedTrigger);
-        }
-        this.measure(geometry, "measure");
-    },
-    
-    /**
-     * Method: measurePartial
-     * Called each time a new point is added to the measurement sketch.
-     *
-     * Parameters:
-     * point - {<OpenLayers.Geometry.Point>} The last point added.
-     * geometry - {<OpenLayers.Geometry>} The sketch geometry.
-     */
-    measurePartial: function(point, geometry) {
-        if (geometry.getLength() > 0) {
-            geometry = geometry.clone();
-            this.delayedTrigger = window.setTimeout(
-                OpenLayers.Function.bind(function() {
-                    this.measure(geometry, "measurepartial");
-                }, this),
-                this.partialDelay
-            );
-        }
-    },
-
-    /**
-     * Method: measure
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     * eventType - {String}
-     */
-    measure: function(geometry, eventType) {
-        var stat, order;
-        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
-            stat = this.getBestLength(geometry);
-            order = 1;
-        } else {
-            stat = this.getBestArea(geometry);
-            order = 2;
-        }
-        this.events.triggerEvent(eventType, {
-            measure: stat[0],
-            units: stat[1],
-            order: order,
-            geometry: geometry
-        });
-    },
-    
-    /**
-     * Method: getBestArea
-     * Based on the <displaySystem> returns the area of a geometry.
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     *
+     * moLonLat - {Object} MapObject LonLat format
+     * 
      * Returns:
-     * {Array([Float, String])}  Returns a two item array containing the
-     *     area and the units abbreviation.
+     * {Float} Latitude of the given MapObject LonLat
      */
-    getBestArea: function(geometry) {
-        var units = this.displaySystemUnits[this.displaySystem];
-        var unit, area;
-        for(var i=0, len=units.length; i<len; ++i) {
-            unit = units[i];
-            area = this.getArea(geometry, unit);
-            if(area > 1) {
-                break;
-            }
-        }
-        return [area, unit];
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return this.sphericalMercator ? 
+            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
+            moLonLat.Latitude;
     },
-    
-    /**
-     * Method: getArea
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     * units - {String} Unit abbreviation
-     *
-     * Returns:
-     * {Float} The geometry area in the given units.
-     */
-    getArea: function(geometry, units) {
-        var area, geomUnits;
-        if(this.geodesic) {
-            area = geometry.getGeodesicArea(this.map.getProjectionObject());
-            geomUnits = "m";
-        } else {
-            area = geometry.getArea();
-            geomUnits = this.map.getUnits();
-        }
-        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
-        if(inPerDisplayUnit) {
-            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
-            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
-        }
-        return area;
-    },
-    
-    /**
-     * Method: getBestLength
-     * Based on the <displaySystem> returns the length of a geometry.
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     *
-     * Returns:
-     * {Array([Float, String])}  Returns a two item array containing the
-     *     length and the units abbreviation.
-     */
-    getBestLength: function(geometry) {
-        var units = this.displaySystemUnits[this.displaySystem];
-        var unit, length;
-        for(var i=0, len=units.length; i<len; ++i) {
-            unit = units[i];
-            length = this.getLength(geometry, unit);
-            if(length > 1) {
-                break;
-            }
-        }
-        return [length, unit];
-    },
 
     /**
-     * Method: getLength
-     *
+     * APIMethod: getMapObjectLonLatFromLonLat
+     * 
      * Parameters:
-     * geometry - {<OpenLayers.Geometry>}
-     * units - {String} Unit abbreviation
-     *
+     * lon - {Float}
+     * lat - {Float}
+     * 
      * Returns:
-     * {Float} The geometry length in the given units.
+     * {Object} MapObject LonLat built from lon and lat params
      */
-    getLength: function(geometry, units) {
-        var length, geomUnits;
-        if(this.geodesic) {
-            length = geometry.getGeodesicLength(this.map.getProjectionObject());
-            geomUnits = "m";
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        var veLatLong;
+        if(this.sphericalMercator) {
+            var lonlat = this.inverseMercator(lon, lat);
+            veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
         } else {
-            length = geometry.getLength();
-            geomUnits = this.map.getUnits();
+            veLatLong = new VELatLong(lat, lon);
         }
-        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
-        if(inPerDisplayUnit) {
-            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
-            length *= (inPerMapUnit / inPerDisplayUnit);
-        }
-        return length;
+        return veLatLong;
     },
 
-    CLASS_NAME: "OpenLayers.Control.Measure"
-});
-/* ======================================================================
-    OpenLayers/Control/ZoomBox.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Control.js
- * @requires OpenLayers/Handler/Box.js
- */
-
-/**
- * Class: OpenLayers.Control.ZoomBox
- * 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>
- */
-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
-    /**
-     * Property: type
-     * {OpenLayers.Control.TYPE}
-     */
-    type: OpenLayers.Control.TYPE_TOOL,
-
-    /**
-     * Property: out
-     * {Boolean} Should the control be used for zooming out?
-     */
-    out: false,
-
-    /**
-     * Property: alwaysZoom
-     * {Boolean} Always zoom in/out, when box drawed 
-     */
-    alwaysZoom: false,
-
-    /**
-     * Method: draw
-     */    
-    draw: function() {
-        this.handler = new OpenLayers.Handler.Box( this,
-                            {done: this.zoomBox}, {keyMask: this.keyMask} );
-    },
-
-    /**
-     * Method: zoomBox
-     *
-     * Parameters:
-     * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
-     */
-    zoomBox: function (position) {
-        if (position instanceof OpenLayers.Bounds) {
-            var bounds;
-            if (!this.out) {
-                var minXY = this.map.getLonLatFromPixel(
-                            new OpenLayers.Pixel(position.left, position.bottom));
-                var maxXY = this.map.getLonLatFromPixel(
-                            new OpenLayers.Pixel(position.right, position.top));
-                bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
-                                               maxXY.lon, maxXY.lat);
-            } else {
-                var pixWidth = Math.abs(position.right-position.left);
-                var pixHeight = Math.abs(position.top-position.bottom);
-                var zoomFactor = Math.min((this.map.size.h / pixHeight),
-                    (this.map.size.w / pixWidth));
-                var extent = this.map.getExtent();
-                var center = this.map.getLonLatFromPixel(
-                    position.getCenterPixel());
-                var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
-                var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
-                var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
-                var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
-                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),
-                               this.map.getZoom() + 1);
-            } else {
-                this.map.setCenter(this.map.getLonLatFromPixel(position),
-                               this.map.getZoom() - 1);
-            }
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Control.ZoomBox"
-});
-/* ======================================================================
-    OpenLayers/Format/WKT.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Format.js
- * @requires OpenLayers/Feature/Vector.js
- */
-
-/**
- * Class: OpenLayers.Format.WKT
- * Class for reading and writing Well-Known Text.  Create a new instance
- * with the <OpenLayers.Format.WKT> constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Format>
- */
-OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
+  // Pixel
     
     /**
-     * Constructor: OpenLayers.Format.WKT
-     * Create a new parser for WKT
-     *
+     * APIMethod: getXFromMapObjectPixel
+     * 
      * Parameters:
-     * options - {Object} An optional object whose properties will be set on
-     *           this instance
-     *
+     * moPixel - {Object} MapObject Pixel format
+     * 
      * Returns:
-     * {<OpenLayers.Format.WKT>} A new WKT parser.
+     * {Integer} X value of the MapObject Pixel
      */
-    initialize: function(options) {
-        this.regExes = {
-            'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
-            'spaces': /\s+/,
-            'parenComma': /\)\s*,\s*\(/,
-            'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,  // can't use {2} here
-            'trimParens': /^\s*\(?(.*?)\)?\s*$/
-        };
-        OpenLayers.Format.prototype.initialize.apply(this, [options]);
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
     },
 
     /**
-     * Method: read
-     * Deserialize a WKT string and return a vector feature or an
-     * array of vector features.  Supports WKT for POINT, MULTIPOINT,
-     * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and
-     * GEOMETRYCOLLECTION.
-     *
+     * APIMethod: getYFromMapObjectPixel
+     * 
      * Parameters:
-     * wkt - {String} A WKT string
-     *
+     * moPixel - {Object} MapObject Pixel format
+     * 
      * Returns:
-     * {<OpenLayers.Feature.Vector>|Array} A feature or array of features for
-     * GEOMETRYCOLLECTION WKT.
+     * {Integer} Y value of the MapObject Pixel
      */
-    read: function(wkt) {
-        var features, type, str;
-        var matches = this.regExes.typeStr.exec(wkt);
-        if(matches) {
-            type = matches[1].toLowerCase();
-            str = matches[2];
-            if(this.parse[type]) {
-                features = this.parse[type].apply(this, [str]);
-            }
-            if (this.internalProjection && this.externalProjection) {
-                if (features && 
-                    features.CLASS_NAME == "OpenLayers.Feature.Vector") {
-                    features.geometry.transform(this.externalProjection,
-                                                this.internalProjection);
-                } else if (features &&
-                           type != "geometrycollection" &&
-                           typeof features == "object") {
-                    for (var i=0, len=features.length; i<len; i++) {
-                        var component = features[i];
-                        component.geometry.transform(this.externalProjection,
-                                                     this.internalProjection);
-                    }
-                }
-            }
-        }    
-        return features;
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
     },
 
     /**
-     * Method: write
-     * Serialize a feature or array of features into a WKT string.
-     *
+     * APIMethod: getMapObjectPixelFromXY
+     * 
      * Parameters:
-     * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
-     *            features
-     *
+     * x - {Integer}
+     * y - {Integer}
+     * 
      * Returns:
-     * {String} The WKT string representation of the input geometries
+     * {Object} MapObject Pixel from x and y parameters
      */
-    write: function(features) {
-        var collection, geometry, type, data, isCollection;
-        if(features.constructor == Array) {
-            collection = features;
-            isCollection = true;
-        } else {
-            collection = [features];
-            isCollection = false;
-        }
-        var pieces = [];
-        if(isCollection) {
-            pieces.push('GEOMETRYCOLLECTION(');
-        }
-        for(var i=0, len=collection.length; i<len; ++i) {
-            if(isCollection && i>0) {
-                pieces.push(',');
-            }
-            geometry = collection[i].geometry;
-            type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
-            if(!this.extract[type]) {
-                return null;
-            }
-            if (this.internalProjection && this.externalProjection) {
-                geometry = geometry.clone();
-                geometry.transform(this.internalProjection, 
-                                   this.externalProjection);
-            }                       
-            data = this.extract[type].apply(this, [geometry]);
-            pieces.push(type.toUpperCase() + '(' + data + ')');
-        }
-        if(isCollection) {
-            pieces.push(')');
-        }
-        return pieces.join('');
+    getMapObjectPixelFromXY: function(x, y) {
+        //the conditional here is to test if we are running the v6 of VE
+        return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
+                         : new Msn.VE.Pixel(x, y);
     },
-    
-    /**
-     * Object with properties corresponding to the geometry types.
-     * Property values are functions that do the actual data extraction.
-     */
-    extract: {
-        /**
-         * Return a space delimited string of point coordinates.
-         * @param {<OpenLayers.Geometry.Point>} point
-         * @returns {String} A string of coordinates representing the point
-         */
-        'point': function(point) {
-            return point.x + ' ' + point.y;
-        },
 
-        /**
-         * Return a comma delimited string of point coordinates from a multipoint.
-         * @param {<OpenLayers.Geometry.MultiPoint>} multipoint
-         * @returns {String} A string of point coordinate strings representing
-         *                  the multipoint
-         */
-        'multipoint': function(multipoint) {
-            var array = [];
-            for(var i=0, len=multipoint.components.length; i<len; ++i) {
-                array.push('(' +
-                           this.extract.point.apply(this, [multipoint.components[i]]) +
-                           ')');
-            }
-            return array.join(',');
-        },
-        
-        /**
-         * Return a comma delimited string of point coordinates from a line.
-         * @param {<OpenLayers.Geometry.LineString>} linestring
-         * @returns {String} A string of point coordinate strings representing
-         *                  the linestring
-         */
-        'linestring': function(linestring) {
-            var array = [];
-            for(var i=0, len=linestring.components.length; i<len; ++i) {
-                array.push(this.extract.point.apply(this, [linestring.components[i]]));
-            }
-            return array.join(',');
-        },
-
-        /**
-         * Return a comma delimited string of linestring strings from a multilinestring.
-         * @param {<OpenLayers.Geometry.MultiLineString>} multilinestring
-         * @returns {String} A string of of linestring strings representing
-         *                  the multilinestring
-         */
-        'multilinestring': function(multilinestring) {
-            var array = [];
-            for(var i=0, len=multilinestring.components.length; i<len; ++i) {
-                array.push('(' +
-                           this.extract.linestring.apply(this, [multilinestring.components[i]]) +
-                           ')');
-            }
-            return array.join(',');
-        },
-        
-        /**
-         * Return a comma delimited string of linear ring arrays from a polygon.
-         * @param {<OpenLayers.Geometry.Polygon>} polygon
-         * @returns {String} An array of linear ring arrays representing the polygon
-         */
-        'polygon': function(polygon) {
-            var array = [];
-            for(var i=0, len=polygon.components.length; i<len; ++i) {
-                array.push('(' +
-                           this.extract.linestring.apply(this, [polygon.components[i]]) +
-                           ')');
-            }
-            return array.join(',');
-        },
-
-        /**
-         * Return an array of polygon arrays from a multipolygon.
-         * @param {<OpenLayers.Geometry.MultiPolygon>} multipolygon
-         * @returns {Array} An array of polygon arrays representing
-         *                  the multipolygon
-         */
-        'multipolygon': function(multipolygon) {
-            var array = [];
-            for(var i=0, len=multipolygon.components.length; i<len; ++i) {
-                array.push('(' +
-                           this.extract.polygon.apply(this, [multipolygon.components[i]]) +
-                           ')');
-            }
-            return array.join(',');
-        }
-
-    },
-
-    /**
-     * Object with properties corresponding to the geometry types.
-     * Property values are functions that do the actual parsing.
-     */
-    parse: {
-        /**
-         * Return point feature given a point WKT fragment.
-         * @param {String} str A WKT fragment representing the point
-         * @returns {<OpenLayers.Feature.Vector>} A point feature
-         * @private
-         */
-        'point': function(str) {
-            var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.Point(coords[0], coords[1])
-            );
-        },
-
-        /**
-         * Return a multipoint feature given a multipoint WKT fragment.
-         * @param {String} A WKT fragment representing the multipoint
-         * @returns {<OpenLayers.Feature.Vector>} A multipoint feature
-         * @private
-         */
-        'multipoint': function(str) {
-            var point;
-            var points = OpenLayers.String.trim(str).split(this.regExes.parenComma);
-            var components = [];
-            for(var i=0, len=points.length; i<len; ++i) {
-                point = points[i].replace(this.regExes.trimParens, '$1');
-                components.push(this.parse.point.apply(this, [point]).geometry);
-            }
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.MultiPoint(components)
-            );
-        },
-        
-        /**
-         * Return a linestring feature given a linestring WKT fragment.
-         * @param {String} A WKT fragment representing the linestring
-         * @returns {<OpenLayers.Feature.Vector>} A linestring feature
-         * @private
-         */
-        'linestring': function(str) {
-            var points = OpenLayers.String.trim(str).split(',');
-            var components = [];
-            for(var i=0, len=points.length; i<len; ++i) {
-                components.push(this.parse.point.apply(this, [points[i]]).geometry);
-            }
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.LineString(components)
-            );
-        },
-
-        /**
-         * Return a multilinestring feature given a multilinestring WKT fragment.
-         * @param {String} A WKT fragment representing the multilinestring
-         * @returns {<OpenLayers.Feature.Vector>} A multilinestring feature
-         * @private
-         */
-        'multilinestring': function(str) {
-            var line;
-            var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
-            var components = [];
-            for(var i=0, len=lines.length; i<len; ++i) {
-                line = lines[i].replace(this.regExes.trimParens, '$1');
-                components.push(this.parse.linestring.apply(this, [line]).geometry);
-            }
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.MultiLineString(components)
-            );
-        },
-        
-        /**
-         * Return a polygon feature given a polygon WKT fragment.
-         * @param {String} A WKT fragment representing the polygon
-         * @returns {<OpenLayers.Feature.Vector>} A polygon feature
-         * @private
-         */
-        'polygon': function(str) {
-            var ring, linestring, linearring;
-            var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
-            var components = [];
-            for(var i=0, len=rings.length; i<len; ++i) {
-                ring = rings[i].replace(this.regExes.trimParens, '$1');
-                linestring = this.parse.linestring.apply(this, [ring]).geometry;
-                linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
-                components.push(linearring);
-            }
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.Polygon(components)
-            );
-        },
-
-        /**
-         * Return a multipolygon feature given a multipolygon WKT fragment.
-         * @param {String} A WKT fragment representing the multipolygon
-         * @returns {<OpenLayers.Feature.Vector>} A multipolygon feature
-         * @private
-         */
-        'multipolygon': function(str) {
-            var polygon;
-            var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
-            var components = [];
-            for(var i=0, len=polygons.length; i<len; ++i) {
-                polygon = polygons[i].replace(this.regExes.trimParens, '$1');
-                components.push(this.parse.polygon.apply(this, [polygon]).geometry);
-            }
-            return new OpenLayers.Feature.Vector(
-                new OpenLayers.Geometry.MultiPolygon(components)
-            );
-        },
-
-        /**
-         * Return an array of features given a geometrycollection WKT fragment.
-         * @param {String} A WKT fragment representing the geometrycollection
-         * @returns {Array} An array of OpenLayers.Feature.Vector
-         * @private
-         */
-        'geometrycollection': function(str) {
-            // separate components of the collection with |
-            str = str.replace(/,\s*([A-Za-z])/g, '|$1');
-            var wktArray = OpenLayers.String.trim(str).split('|');
-            var components = [];
-            for(var i=0, len=wktArray.length; i<len; ++i) {
-                components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
-            }
-            return components;
-        }
-
-    },
-
-    CLASS_NAME: "OpenLayers.Format.WKT" 
-});     
+    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
+});
 /* ======================================================================
     OpenLayers/Layer/Google.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -27660,6 +11144,7 @@
  * @requires OpenLayers/Layer/SphericalMercator.js
  * @requires OpenLayers/Layer/EventPane.js
  * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -28446,1581 +11931,4663 @@
     
 };
 /* ======================================================================
-    OpenLayers/Layer/Grid.js
+    OpenLayers/Format.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Layer/HTTPRequest.js
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
- * Class: OpenLayers.Layer.Grid
- * Base class for layers that use a lattice of tiles.  Create a new grid
- * layer with the <OpenLayers.Layer.Grid> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.HTTPRequest>
+ * Class: OpenLayers.Format
+ * Base class for format reading/writing a variety of formats.  Subclasses
+ *     of OpenLayers.Format are expected to have read and write methods.
  */
-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
+OpenLayers.Format = OpenLayers.Class({
     
     /**
-     * APIProperty: tileSize
-     * {<OpenLayers.Size>}
+     * Property: options
+     * {Object} A reference to options passed to the constructor.
      */
-    tileSize: null,
+    options: null,
     
     /**
-     * Property: grid
-     * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is 
-     *     an array of tiles.
+     * APIProperty: externalProjection
+     * {<OpenLayers.Projection>} When passed a externalProjection and
+     *     internalProjection, the format will reproject the geometries it
+     *     reads or writes. The externalProjection is the projection used by
+     *     the content which is passed into read or which comes out of write.
+     *     In order to reproject, a projection transformation function for the
+     *     specified projections must be available. This support may be 
+     *     provided via proj4js or via a custom transformation function. See
+     *     {<OpenLayers.Projection.addTransform>} for more information on
+     *     custom transformations.
      */
-    grid: null,
+    externalProjection: null,
 
     /**
-     * APIProperty: singleTile
-     * {Boolean} Moves the layer into single-tile mode, meaning that one tile 
-     *     will be loaded. The tile's size will be determined by the 'ratio'
-     *     property. When the tile is dragged such that it does not cover the 
-     *     entire viewport, it is reloaded.
+     * APIProperty: internalProjection
+     * {<OpenLayers.Projection>} When passed a externalProjection and
+     *     internalProjection, the format will reproject the geometries it
+     *     reads or writes. The internalProjection is the projection used by
+     *     the geometries which are returned by read or which are passed into
+     *     write.  In order to reproject, a projection transformation function
+     *     for the specified projections must be available. This support may be
+     *     provided via proj4js or via a custom transformation function. See
+     *     {<OpenLayers.Projection.addTransform>} for more information on
+     *     custom transformations.
      */
-    singleTile: false,
+    internalProjection: null,
 
-    /** APIProperty: ratio
-     *  {Float} Used only when in single-tile mode, this specifies the 
-     *          ratio of the size of the single tile to the size of the map.
+    /**
+     * APIProperty: data
+     * {Object} When <keepData> is true, this is the parsed string sent to
+     *     <read>.
      */
-    ratio: 1.5,
+    data: null,
 
     /**
-     * APIProperty: buffer
-     * {Integer} Used only when in gridded mode, this specifies the number of 
-     *           extra rows and colums of tiles on each side which will
-     *           surround the minimum grid tiles to cover the map.
+     * APIProperty: keepData
+     * {Object} Maintain a reference (<data>) to the most recently read data.
+     *     Default is false.
      */
-    buffer: 2,
+    keepData: false,
 
     /**
-     * APIProperty: numLoadingTiles
-     * {Integer} How many tiles are still loading?
+     * Constructor: OpenLayers.Format
+     * Instances of this class are not useful.  See one of the subclasses.
+     *
+     * Parameters:
+     * 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
      */
-    numLoadingTiles: 0,
+    initialize: function(options) {
+        OpenLayers.Util.extend(this, options);
+        this.options = options;
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Clean up.
+     */
+    destroy: function() {
+    },
 
     /**
-     * Constructor: OpenLayers.Layer.Grid
-     * Create a new grid layer
+     * Method: read
+     * Read data from a string, and return an object whose type depends on the
+     * subclass. 
+     * 
+     * Parameters:
+     * data - {string} Data to read/parse.
      *
+     * Returns:
+     * Depends on the subclass
+     */
+    read: function(data) {
+        OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));
+    },
+    
+    /**
+     * Method: write
+     * Accept an object, and return a string. 
+     *
      * Parameters:
-     * name - {String}
-     * url - {String}
-     * params - {Object}
-     * options - {Object} Hashtable of extra options to tag onto the layer
+     * object - {Object} Object to be serialized
+     *
+     * Returns:
+     * {String} A string representation of the object.
      */
-    initialize: function(name, url, params, options) {
-        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
-                                                                arguments);
-        
-        //grid layers will trigger 'tileloaded' when each new tile is 
-        // loaded, as a means of progress update to listeners.
-        // listeners can access 'numLoadingTiles' if they wish to keep track
-        // of the loading progress
-        //
-        this.events.addEventType("tileloaded");
-
-        this.grid = [];
+    write: function(object) {
+        OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));
     },
 
+    CLASS_NAME: "OpenLayers.Format"
+});     
+/* ======================================================================
+    OpenLayers/Format/XML.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format.js
+ */
+
+/**
+ * Class: OpenLayers.Format.XML
+ * Read and write XML.  For cross-browser XML generation, use methods on an
+ *     instance of the XML format class instead of on <code>document<end>.
+ *     The DOM creation and traversing methods exposed here all mimic the
+ *     W3C XML DOM methods.  Create a new parser with the
+ *     <OpenLayers.Format.XML> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format>
+ */
+OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
+    
     /**
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.  Properties
+     *     of this object should not be set individually.  Read-only.  All
+     *     XML subclasses should have their own namespaces object.  Use
+     *     <setNamespace> to add or set a namespace alias after construction.
+     */
+    namespaces: null,
+    
+    /**
+     * Property: namespaceAlias
+     * {Object} Mapping of namespace URI to namespace alias.  This object
+     *     is read-only.  Use <setNamespace> to add or set a namespace alias.
+     */
+    namespaceAlias: null,
+    
+    /**
+     * Property: defaultPrefix
+     * {String} The default namespace alias for creating element nodes.
+     */
+    defaultPrefix: null,
+    
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {},
+    
+    /**
+     * Property: writers
+     * As a compliment to the <readers> property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {},
+
+    /**
+     * Property: xmldom
+     * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
+     *     object.  It is not intended to be a browser sniffing property.
+     *     Instead, the xmldom property is used instead of <code>document<end>
+     *     where namespaced node creation methods are not supported. In all
+     *     other browsers, this remains null.
+     */
+    xmldom: null,
+
+    /**
+     * Constructor: OpenLayers.Format.XML
+     * Construct an XML parser.  The parser is used to read and write XML.
+     *     Reading XML from a string returns a DOM element.  Writing XML from
+     *     a DOM element returns a string.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on
+     *     the object.
+     */
+    initialize: function(options) {
+        if(window.ActiveXObject) {
+            this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
+        }
+        OpenLayers.Format.prototype.initialize.apply(this, [options]);
+        // clone the namespace object and set all namespace aliases
+        this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
+        this.namespaceAlias = {};
+        for(var alias in this.namespaces) {
+            this.namespaceAlias[this.namespaces[alias]] = alias;
+        }
+    },
+    
+    /**
      * APIMethod: destroy
-     * Deconstruct the layer and clear the grid.
+     * Clean up.
      */
     destroy: function() {
-        this.clearGrid();
-        this.grid = null;
-        this.tileSize = null;
-        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); 
+        this.xmldom = null;
+        OpenLayers.Format.prototype.destroy.apply(this, arguments);
     },
+    
+    /**
+     * Method: setNamespace
+     * Set a namespace alias and URI for the format.
+     *
+     * Parameters:
+     * alias - {String} The namespace alias (prefix).
+     * uri - {String} The namespace URI.
+     */
+    setNamespace: function(alias, uri) {
+        this.namespaces[alias] = uri;
+        this.namespaceAlias[uri] = alias;
+    },
 
     /**
-     * Method: clearGrid
-     * Go through and remove all tiles from the grid, calling
-     *    destroy() on each of them to kill circular references
+     * APIMethod: read
+     * Deserialize a XML string and return a DOM node.
+     *
+     * Parameters:
+     * text - {String} A XML string
+     
+     * Returns:
+     * {DOMElement} A DOM node
      */
-    clearGrid:function() {
-        if (this.grid) {
-            for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
-                var row = this.grid[iRow];
-                for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
-                    var tile = row[iCol];
-                    this.removeTileMonitoringHooks(tile);
-                    tile.destroy();
+    read: function(text) {
+        var index = text.indexOf('<');
+        if(index > 0) {
+            text = text.substring(index);
+        }
+        var node = OpenLayers.Util.Try(
+            OpenLayers.Function.bind((
+                function() {
+                    var xmldom;
+                    /**
+                     * Since we want to be able to call this method on the prototype
+                     * itself, this.xmldom may not exist even if in IE.
+                     */
+                    if(window.ActiveXObject && !this.xmldom) {
+                        xmldom = new ActiveXObject("Microsoft.XMLDOM");
+                    } else {
+                        xmldom = this.xmldom;
+                        
+                    }
+                    xmldom.loadXML(text);
+                    return xmldom;
                 }
+            ), this),
+            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;
             }
-            this.grid = [];
+        );
+
+        if(this.keepData) {
+            this.data = node;
         }
+
+        return node;
     },
 
     /**
-     * APIMethod: clone
-     * Create a clone of this layer
+     * APIMethod: write
+     * Serialize a DOM node into a XML string.
+     * 
+     * Parameters:
+     * node - {DOMElement} A DOM node.
      *
+     * Returns:
+     * {String} The XML string representation of the input node.
+     */
+    write: function(node) {
+        var data;
+        if(this.xmldom) {
+            data = node.xml;
+        } else {
+            var serializer = new XMLSerializer();
+            if (node.nodeType == 1) {
+                // Add nodes to a document before serializing. Everything else
+                // is serialized as is. This may need more work. See #1218 .
+                var doc = document.implementation.createDocument("", "", null);
+                if (doc.importNode) {
+                    node = doc.importNode(node, true);
+                }
+                doc.appendChild(node);
+                data = serializer.serializeToString(doc);
+            } else {
+                data = serializer.serializeToString(node);
+            }
+        }
+        return data;
+    },
+
+    /**
+     * APIMethod: createElementNS
+     * Create a new element with namespace.  This node can be appended to
+     *     another node with the standard node.appendChild method.  For
+     *     cross-browser support, this method must be used instead of
+     *     document.createElementNS.
+     *
      * Parameters:
-     * obj - {Object} Is this ever used?
+     * uri - {String} Namespace URI for the element.
+     * name - {String} The qualified name of the element (prefix:localname).
      * 
      * Returns:
-     * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
+     * {Element} A DOM element with namespace.
      */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.Grid(this.name,
-                                            this.url,
-                                            this.params,
-                                            this.getOptions());
+    createElementNS: function(uri, name) {
+        var element;
+        if(this.xmldom) {
+            if(typeof uri == "string") {
+                element = this.xmldom.createNode(1, name, uri);
+            } else {
+                element = this.xmldom.createNode(1, name, "");
+            }
+        } else {
+            element = document.createElementNS(uri, name);
         }
+        return element;
+    },
 
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-        if (this.tileSize != null) {
-            obj.tileSize = this.tileSize.clone();
+    /**
+     * APIMethod: createTextNode
+     * Create a text node.  This node can be appended to another node with
+     *     the standard node.appendChild method.  For cross-browser support,
+     *     this method must be used instead of document.createTextNode.
+     * 
+     * Parameters:
+     * text - {String} The text of the node.
+     * 
+     * Returns: 
+     * {DOMElement} A DOM text node.
+     */
+    createTextNode: function(text) {
+        var node;
+        if (typeof text !== "string") {
+            text = String(text);
         }
-        
-        // we do not want to copy reference to grid, so we make a new array
-        obj.grid = [];
+        if(this.xmldom) {
+            node = this.xmldom.createTextNode(text);
+        } else {
+            node = document.createTextNode(text);
+        }
+        return node;
+    },
 
-        return obj;
-    },    
-
     /**
-     * Method: moveTo
-     * This function is called whenever the map is moved. All the moving
-     * of actual 'tiles' is done by the map, but moveTo's role is to accept
-     * a bounds and make sure the data that that bounds requires is pre-loaded.
-     *
+     * APIMethod: getElementsByTagNameNS
+     * Get a list of elements on a node given the namespace URI and local name.
+     *     To return all nodes in a given namespace, use '*' for the name
+     *     argument.  To return all nodes of a given (local) name, regardless
+     *     of namespace, use '*' for the uri argument.
+     * 
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * zoomChanged - {Boolean}
-     * dragging - {Boolean}
+     * node - {Element} Node on which to search for other nodes.
+     * uri - {String} Namespace URI.
+     * name - {String} Local name of the tag (without the prefix).
+     * 
+     * Returns:
+     * {NodeList} A node list or array of elements.
      */
-    moveTo:function(bounds, zoomChanged, dragging) {
-        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
-        
-        bounds = bounds || this.map.getExtent();
-
-        if (bounds != null) {
-             
-            // if grid is empty or zoom has changed, we *must* re-tile
-            var forceReTile = !this.grid.length || zoomChanged;
-
-            // total bounds of the tiles
-            var tilesBounds = this.getTilesBounds();            
-      
-            if (this.singleTile) {
-                
-                // We want to redraw whenever even the slightest part of the 
-                //  current bounds is not contained by our tile.
-                //  (thus, we do not specify partial -- its default is false)
-                if ( forceReTile || 
-                     (!dragging && !tilesBounds.containsBounds(bounds))) {
-                    this.initSingleTile(bounds);
+    getElementsByTagNameNS: function(node, uri, name) {
+        var elements = [];
+        if(node.getElementsByTagNameNS) {
+            elements = node.getElementsByTagNameNS(uri, name);
+        } else {
+            // brute force method
+            var allNodes = node.getElementsByTagName("*");
+            var potentialNode, fullName;
+            for(var i=0, len=allNodes.length; i<len; ++i) {
+                potentialNode = allNodes[i];
+                fullName = (potentialNode.prefix) ?
+                           (potentialNode.prefix + ":" + name) : name;
+                if((name == "*") || (fullName == potentialNode.nodeName)) {
+                    if((uri == "*") || (uri == potentialNode.namespaceURI)) {
+                        elements.push(potentialNode);
+                    }
                 }
-            } else {
-             
-                // if the bounds have changed such that they are not even 
-                //  *partially* contained by our tiles (IE user has 
-                //  programmatically panned to the other side of the earth) 
-                //  then we want to reTile (thus, partial true).  
-                //
-                if (forceReTile || !tilesBounds.containsBounds(bounds, true)) {
-                    this.initGriddedTiles(bounds);
-                } else {
-                    //we might have to shift our buffer tiles
-                    this.moveGriddedTiles(bounds);
-                }
             }
         }
+        return elements;
     },
-    
+
     /**
-     * APIMethod: setTileSize
-     * Check if we are in singleTile mode and if so, set the size as a ratio
-     *     of the map size (as specified by the layer's 'ratio' property).
+     * APIMethod: getAttributeNodeNS
+     * Get an attribute node given the namespace URI and local name.
      * 
      * Parameters:
-     * size - {<OpenLayers.Size>}
+     * node - {Element} Node on which to search for attribute nodes.
+     * uri - {String} Namespace URI.
+     * name - {String} Local name of the attribute (without the prefix).
+     * 
+     * Returns:
+     * {DOMElement} An attribute node or null if none found.
      */
-    setTileSize: function(size) { 
-        if (this.singleTile) {
-            size = this.map.getSize();
-            size.h = parseInt(size.h * this.ratio);
-            size.w = parseInt(size.w * this.ratio);
-        } 
-        OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
+    getAttributeNodeNS: function(node, uri, name) {
+        var attributeNode = null;
+        if(node.getAttributeNodeNS) {
+            attributeNode = node.getAttributeNodeNS(uri, name);
+        } else {
+            var attributes = node.attributes;
+            var potentialNode, fullName;
+            for(var i=0, len=attributes.length; i<len; ++i) {
+                potentialNode = attributes[i];
+                if(potentialNode.namespaceURI == uri) {
+                    fullName = (potentialNode.prefix) ?
+                               (potentialNode.prefix + ":" + name) : name;
+                    if(fullName == potentialNode.nodeName) {
+                        attributeNode = potentialNode;
+                        break;
+                    }
+                }
+            }
+        }
+        return attributeNode;
     },
-        
+
     /**
-     * Method: getGridBounds
-     * Deprecated. This function will be removed in 3.0. Please use 
-     *     getTilesBounds() instead.
+     * APIMethod: getAttributeNS
+     * Get an attribute value given the namespace URI and local name.
      * 
+     * Parameters:
+     * node - {Element} Node on which to search for an attribute.
+     * uri - {String} Namespace URI.
+     * name - {String} Local name of the attribute (without the prefix).
+     * 
      * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
-     * currently loaded tiles (including those partially or not at all seen 
-     * onscreen)
+     * {String} An attribute value or and empty string if none found.
      */
-    getGridBounds: function() {
-        var msg = "The getGridBounds() function is deprecated. It will be " +
-                  "removed in 3.0. Please use getTilesBounds() instead.";
-        OpenLayers.Console.warn(msg);
-        return this.getTilesBounds();
+    getAttributeNS: function(node, uri, name) {
+        var attributeValue = "";
+        if(node.getAttributeNS) {
+            attributeValue = node.getAttributeNS(uri, name) || "";
+        } else {
+            var attributeNode = this.getAttributeNodeNS(node, uri, name);
+            if(attributeNode) {
+                attributeValue = attributeNode.nodeValue;
+            }
+        }
+        return attributeValue;
     },
+    
+    /**
+     * APIMethod: getChildValue
+     * Get the textual value of the node if it exists, or return an
+     *     optional default string.  Returns an empty string if no first child
+     *     exists and no default value is supplied.
+     *
+     * Parameters:
+     * node - {DOMElement} The element used to look for a first child value.
+     * def - {String} Optional string to return in the event that no
+     *     first child value exists.
+     *
+     * Returns:
+     * {String} The value of the first child of the given node.
+     */
+    getChildValue: function(node, def) {
+        var value = def || "";
+        if(node) {
+            for(var child=node.firstChild; child; child=child.nextSibling) {
+                switch(child.nodeType) {
+                    case 3: // text node
+                    case 4: // cdata section
+                        value += child.nodeValue;
+                }
+            }
+        }
+        return value;
+    },
 
     /**
-     * APIMethod: getTilesBounds
-     * Return the bounds of the tile grid.
+     * APIMethod: concatChildValues
+     * *Deprecated*. Use <getChildValue> instead.
      *
+     * Concatenate the value of all child nodes if any exist, or return an
+     *     optional default string.  Returns an empty string if no children
+     *     exist and no default value is supplied.  Not optimized for large
+     *     numbers of child nodes.
+     *
+     * Parameters:
+     * node - {DOMElement} The element used to look for child values.
+     * def - {String} Optional string to return in the event that no
+     *     child exist.
+     *
      * Returns:
-     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
-     *     currently loaded tiles (including those partially or not at all seen 
-     *     onscreen).
+     * {String} The concatenated value of all child nodes of the given node.
      */
-    getTilesBounds: function() {    
-        var bounds = null; 
-        
-        if (this.grid.length) {
-            var bottom = this.grid.length - 1;
-            var bottomLeftTile = this.grid[bottom][0];
+    concatChildValues: function(node, def) {
+        var value = "";
+        var child = node.firstChild;
+        var childValue;
+        while(child) {
+            childValue = child.nodeValue;
+            if(childValue) {
+                value += childValue;
+            }
+            child = child.nextSibling;
+        }
+        if(value == "" && def != undefined) {
+            value = def;
+        }
+        return value;
+    },
     
-            var right = this.grid[0].length - 1; 
-            var topRightTile = this.grid[0][right];
+    /**
+     * APIMethod: isSimpleContent
+     * Test if the given node has only simple content (i.e. no child element
+     *     nodes).
+     *
+     * Parameters:
+     * node - {DOMElement} An element node.
+     *
+     * Returns:
+     * {Boolean} The node has no child element nodes (nodes of type 1). 
+     */
+    isSimpleContent: function(node) {
+        var simple = true;
+        for(var child=node.firstChild; child; child=child.nextSibling) {
+            if(child.nodeType === 1) {
+                simple = false;
+                break;
+            }
+        }
+        return simple;
+    },
     
-            bounds = new OpenLayers.Bounds(bottomLeftTile.bounds.left, 
-                                           bottomLeftTile.bounds.bottom,
-                                           topRightTile.bounds.right, 
-                                           topRightTile.bounds.top);
+    /**
+     * APIMethod: contentType
+     * Determine the content type for a given node.
+     *
+     * Parameters:
+     * node - {DOMElement}
+     *
+     * Returns:
+     * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
+     *     if the node has no, simple, complex, or mixed content.
+     */
+    contentType: function(node) {
+        var simple = false,
+            complex = false;
             
-        }   
-        return bounds;
+        var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
+
+        for(var child=node.firstChild; child; child=child.nextSibling) {
+            switch(child.nodeType) {
+                case 1: // element
+                    complex = true;
+                    break;
+                case 8: // comment
+                    break;
+                default:
+                    simple = true;
+            }
+            if(complex && simple) {
+                break;
+            }
+        }
+        
+        if(complex && simple) {
+            type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
+        } else if(complex) {
+            return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
+        } else if(simple) {
+            return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
+        }
+        return type;
     },
 
     /**
-     * Method: initSingleTile
+     * APIMethod: hasAttributeNS
+     * Determine whether a node has a particular attribute matching the given
+     *     name and namespace.
      * 
-     * Parameters: 
-     * bounds - {<OpenLayers.Bounds>}
+     * Parameters:
+     * node - {Element} Node on which to search for an attribute.
+     * uri - {String} Namespace URI.
+     * name - {String} Local name of the attribute (without the prefix).
+     * 
+     * Returns:
+     * {Boolean} The node has an attribute matching the name and namespace.
      */
-    initSingleTile: function(bounds) {
-
-        //determine new tile bounds
-        var center = bounds.getCenterLonLat();
-        var tileWidth = bounds.getWidth() * this.ratio;
-        var tileHeight = bounds.getHeight() * this.ratio;
-                                       
-        var tileBounds = 
-            new OpenLayers.Bounds(center.lon - (tileWidth/2),
-                                  center.lat - (tileHeight/2),
-                                  center.lon + (tileWidth/2),
-                                  center.lat + (tileHeight/2));
-  
-        var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
-        var px = this.map.getLayerPxFromLonLat(ul);
-
-        if (!this.grid.length) {
-            this.grid[0] = [];
+    hasAttributeNS: function(node, uri, name) {
+        var found = false;
+        if(node.hasAttributeNS) {
+            found = node.hasAttributeNS(uri, name);
+        } else {
+            found = !!this.getAttributeNodeNS(node, uri, name);
         }
-
-        var tile = this.grid[0][0];
-        if (!tile) {
-            tile = this.addTile(tileBounds, px);
-            
-            this.addTileMonitoringHooks(tile);
-            tile.draw();
-            this.grid[0][0] = tile;
+        return found;
+    },
+    
+    /**
+     * APIMethod: setAttributeNS
+     * Adds a new attribute or changes the value of an attribute with the given
+     *     namespace and name.
+     *
+     * Parameters:
+     * node - {Element} Element node on which to set the attribute.
+     * uri - {String} Namespace URI for the attribute.
+     * name - {String} Qualified name (prefix:localname) for the attribute.
+     * value - {String} Attribute value.
+     */
+    setAttributeNS: function(node, uri, name, value) {
+        if(node.setAttributeNS) {
+            node.setAttributeNS(uri, name, value);
         } else {
-            tile.moveTo(tileBounds, px);
-        }           
-        
-        //remove all but our single tile
-        this.removeExcessTiles(1,1);
+            if(this.xmldom) {
+                if(uri) {
+                    var attribute = node.ownerDocument.createNode(
+                        2, name, uri
+                    );
+                    attribute.nodeValue = value;
+                    node.setAttributeNode(attribute);
+                } else {
+                    node.setAttribute(name, value);
+                }
+            } else {
+                throw "setAttributeNS not implemented";
+            }
+        }
     },
 
-    /** 
-     * Method: calculateGridLayout
-     * Generate parameters for the grid layout. This  
+    /**
+     * Method: createElementNSPlus
+     * Shorthand for creating namespaced elements with optional attributes and
+     *     child text nodes.
      *
      * Parameters:
-     * bounds - {<OpenLayers.Bound>}
-     * extent - {<OpenLayers.Bounds>}
-     * resolution - {Number}
+     * name - {String} The qualified node name.
+     * options - {Object} Optional object for node configuration.
      *
+     * Valid options:
+     * uri - {String} Optional namespace uri for the element - supply a prefix
+     *     instead if the namespace uri is a property of the format's namespace
+     *     object.
+     * attributes - {Object} Optional attributes to be set using the
+     *     <setAttributes> method.
+     * value - {String} Optional text to be appended as a text node.
+     *
      * Returns:
-     * Object containing properties tilelon, tilelat, tileoffsetlat,
-     * tileoffsetlat, tileoffsetx, tileoffsety
+     * {Element} An element node.
      */
-    calculateGridLayout: function(bounds, extent, resolution) {
-        var tilelon = resolution * this.tileSize.w;
-        var tilelat = resolution * this.tileSize.h;
-        
-        var offsetlon = bounds.left - extent.left;
-        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
-        var tilecolremain = offsetlon/tilelon - tilecol;
-        var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
-        
-        var offsetlat = bounds.top - (extent.bottom + tilelat);  
-        var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
-        var tilerowremain = tilerow - offsetlat/tilelat;
-        var tileoffsety = -tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.bottom + tilerow * tilelat;
-        
-        return { 
-          tilelon: tilelon, tilelat: tilelat,
-          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
-          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
-        };
+    createElementNSPlus: function(name, options) {
+        options = options || {};
+        // order of prefix preference
+        // 1. in the uri option
+        // 2. in the prefix option
+        // 3. in the qualified name
+        // 4. from the defaultPrefix
+        var uri = options.uri || this.namespaces[options.prefix];
+        if(!uri) {
+            var loc = name.indexOf(":");
+            uri = this.namespaces[name.substring(0, loc)];
+        }
+        if(!uri) {
+            uri = this.namespaces[this.defaultPrefix];
+        }
+        var node = this.createElementNS(uri, name);
+        if(options.attributes) {
+            this.setAttributes(node, options.attributes);
+        }
+        var value = options.value;
+        if(value != null) {
+            node.appendChild(this.createTextNode(value));
+        }
+        return node;
+    },
+    
+    /**
+     * Method: setAttributes
+     * Set multiple attributes given key value pairs from an object.
+     *
+     * Parameters:
+     * node - {Element} An element node.
+     * obj - {Object || Array} An object whose properties represent attribute
+     *     names and values represent attribute values.  If an attribute name
+     *     is a qualified name ("prefix:local"), the prefix will be looked up
+     *     in the parsers {namespaces} object.  If the prefix is found,
+     *     setAttributeNS will be used instead of setAttribute.
+     */
+    setAttributes: function(node, obj) {
+        var value, uri;
+        for(var name in obj) {
+            if(obj[name] != null && obj[name].toString) {
+                value = obj[name].toString();
+                // check for qualified attribute name ("prefix:local")
+                uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
+                this.setAttributeNS(node, uri, name, value);
+            }
+        }
+    },
 
+    /**
+     * Method: readNode
+     * Shorthand for applying one of the named readers given the node
+     *     namespace and local name.  Readers take two args (node, obj) and
+     *     generally extend or modify the second.
+     *
+     * Parameters:
+     * node - {DOMElement} The node to be read (required).
+     * obj - {Object} The object to be modified (optional).
+     *
+     * Returns:
+     * {Object} The input object, modified (or a new one if none was provided).
+     */
+    readNode: function(node, obj) {
+        if(!obj) {
+            obj = {};
+        }
+        var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
+        if(group) {
+            var local = node.localName || node.nodeName.split(":").pop();
+            var reader = group[local] || group["*"];
+            if(reader) {
+                reader.apply(this, [node, obj]);
+            }
+        }
+        return obj;
     },
 
     /**
-     * Method: initGriddedTiles
-     * 
+     * Method: readChildNodes
+     * Shorthand for applying the named readers to all children of a node.
+     *     For each child of type 1 (element), <readSelf> is called.
+     *
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
+     * node - {DOMElement} The node to be read (required).
+     * obj - {Object} The object to be modified (optional).
+     *
+     * Returns:
+     * {Object} The input object, modified.
      */
-    initGriddedTiles:function(bounds) {
-        
-        // work out mininum number of rows and columns; this is the number of
-        // tiles required to cover the viewport plus at least one for panning
+    readChildNodes: function(node, obj) {
+        if(!obj) {
+            obj = {};
+        }
+        var children = node.childNodes;
+        var child;
+        for(var i=0, len=children.length; i<len; ++i) {
+            child = children[i];
+            if(child.nodeType == 1) {
+                this.readNode(child, obj);
+            }
+        }
+        return obj;
+    },
 
-        var viewSize = this.map.getSize();
-        var minRows = Math.ceil(viewSize.h/this.tileSize.h) + 
-                      Math.max(1, 2 * this.buffer);
-        var minCols = Math.ceil(viewSize.w/this.tileSize.w) +
-                      Math.max(1, 2 * this.buffer);
-        
-        var extent = this.getMaxExtent();
-        var resolution = this.map.getResolution();
-        
-        var tileLayout = this.calculateGridLayout(bounds, extent, resolution);
+    /**
+     * Method: writeNode
+     * Shorthand for applying one of the named writers and appending the
+     *     results to a node.  If a qualified name is not provided for the
+     *     second argument (and a local name is used instead), the namespace
+     *     of the parent node will be assumed.
+     *
+     * Parameters:
+     * name - {String} The name of a node to generate.  If a qualified name
+     *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be
+     *     in the <writers> group.  If a local name is used (e.g. "Name") then
+     *     the namespace of the parent is assumed.  If a local name is used
+     *     and no parent is supplied, then the default namespace is assumed.
+     * obj - {Object} Structure containing data for the writer.
+     * parent - {DOMElement} Result will be appended to this node.  If no parent
+     *     is supplied, the node will not be appended to anything.
+     *
+     * Returns:
+     * {DOMElement} The child node.
+     */
+    writeNode: function(name, obj, parent) {
+        var prefix, local;
+        var split = name.indexOf(":");
+        if(split > 0) {
+            prefix = name.substring(0, split);
+            local = name.substring(split + 1);
+        } else {
+            if(parent) {
+                prefix = this.namespaceAlias[parent.namespaceURI];
+            } else {
+                prefix = this.defaultPrefix;
+            }
+            local = name;
+        }
+        var child = this.writers[prefix][local].apply(this, [obj]);
+        if(parent) {
+            parent.appendChild(child);
+        }
+        return child;
+    },
 
-        var tileoffsetx = Math.round(tileLayout.tileoffsetx); // heaven help us
-        var tileoffsety = Math.round(tileLayout.tileoffsety);
+    /**
+     * APIMethod: getChildEl
+     * Get the first child element.  Optionally only return the first child
+     *     if it matches the given name and namespace URI.
+     *
+     * Parameters:
+     * node - {DOMElement} The parent node.
+     * name - {String} Optional node name (local) to search for.
+     * uri - {String} Optional namespace URI to search for.
+     *
+     * Returns:
+     * {DOMElement} The first child.  Returns null if no element is found, if
+     *     something significant besides an element is found, or if the element
+     *     found does not match the optional name and uri.
+     */
+    getChildEl: function(node, name, uri) {
+        return node && this.getThisOrNextEl(node.firstChild, name, uri);
+    },
+    
+    /**
+     * APIMethod: getNextEl
+     * Get the next sibling element.  Optionally get the first sibling only
+     *     if it matches the given local name and namespace URI.
+     *
+     * Parameters:
+     * node - {DOMElement} The node.
+     * name - {String} Optional local name of the sibling to search for.
+     * uri - {String} Optional namespace URI of the sibling to search for.
+     *
+     * Returns:
+     * {DOMElement} The next sibling element.  Returns null if no element is
+     *     found, something significant besides an element is found, or the
+     *     found element does not match the optional name and uri.
+     */
+    getNextEl: function(node, name, uri) {
+        return node && this.getThisOrNextEl(node.nextSibling, name, uri);
+    },
+    
+    /**
+     * Method: getThisOrNextEl
+     * Return this node or the next element node.  Optionally get the first
+     *     sibling with the given local name or namespace URI.
+     *
+     * Parameters:
+     * node - {DOMElement} The node.
+     * name - {String} Optional local name of the sibling to search for.
+     * uri - {String} Optional namespace URI of the sibling to search for.
+     *
+     * Returns:
+     * {DOMElement} The next sibling element.  Returns null if no element is
+     *     found, something significant besides an element is found, or the
+     *     found element does not match the query.
+     */
+    getThisOrNextEl: function(node, name, uri) {
+        outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
+            switch(sibling.nodeType) {
+                case 1: // Element
+                    if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
+                       (!uri || uri === sibling.namespaceURI)) {
+                        // matches
+                        break outer;
+                    }
+                    sibling = null;
+                    break outer;
+                case 3: // Text
+                    if(/^\s*$/.test(sibling.nodeValue)) {
+                        break;
+                    }
+                case 4: // CDATA
+                case 6: // ENTITY_NODE
+                case 12: // NOTATION_NODE
+                case 10: // DOCUMENT_TYPE_NODE
+                case 11: // DOCUMENT_FRAGMENT_NODE
+                    sibling = null;
+                    break outer;
+            } // ignore comments and processing instructions
+        }
+        return sibling || null;
+    },
+    
+    /**
+     * APIMethod: lookupNamespaceURI
+     * Takes a prefix and returns the namespace URI associated with it on the given
+     *     node if found (and null if not). Supplying null for the prefix will
+     *     return the default namespace.
+     *
+     * For browsers that support it, this calls the native lookupNamesapceURI
+     *     function.  In other browsers, this is an implementation of
+     *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
+     *
+     * For browsers that don't support the attribute.ownerElement property, this
+     *     method cannot be called on attribute nodes.
+     *     
+     * Parameters:
+     * node - {DOMElement} The node from which to start looking.
+     * prefix - {String} The prefix to lookup or null to lookup the default namespace.
+     * 
+     * Returns:
+     * {String} The namespace URI for the given prefix.  Returns null if the prefix
+     *     cannot be found or the node is the wrong type.
+     */
+    lookupNamespaceURI: function(node, prefix) {
+        var uri = null;
+        if(node) {
+            if(node.lookupNamespaceURI) {
+                uri = node.lookupNamespaceURI(prefix);
+            } else {
+                outer: switch(node.nodeType) {
+                    case 1: // ELEMENT_NODE
+                        if(node.namespaceURI !== null && node.prefix === prefix) {
+                            uri = node.namespaceURI;
+                            break outer;
+                        }
+                        var len = node.attributes.length;
+                        if(len) {
+                            var attr;
+                            for(var i=0; i<len; ++i) {
+                                attr = node.attributes[i];
+                                if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
+                                    uri = attr.value || null;
+                                    break outer;
+                                } else if(attr.name === "xmlns" && prefix === null) {
+                                    uri = attr.value || null;
+                                    break outer;
+                                }
+                            }
+                        }
+                        uri = this.lookupNamespaceURI(node.parentNode, prefix);
+                        break outer;
+                    case 2: // ATTRIBUTE_NODE
+                        uri = this.lookupNamespaceURI(node.ownerElement, prefix);
+                        break outer;
+                    case 9: // DOCUMENT_NODE
+                        uri = this.lookupNamespaceURI(node.documentElement, prefix);
+                        break outer;
+                    case 6: // ENTITY_NODE
+                    case 12: // NOTATION_NODE
+                    case 10: // DOCUMENT_TYPE_NODE
+                    case 11: // DOCUMENT_FRAGMENT_NODE
+                        break outer;
+                    default: 
+                        // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
+                        // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
+                        uri =  this.lookupNamespaceURI(node.parentNode, prefix);
+                        break outer;
+                }
+            }
+        }
+        return uri;
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.XML" 
 
-        var tileoffsetlon = tileLayout.tileoffsetlon;
-        var tileoffsetlat = tileLayout.tileoffsetlat;
-        
-        var tilelon = tileLayout.tilelon;
-        var tilelat = tileLayout.tilelat;
+});     
 
-        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
+OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
 
-        var startX = tileoffsetx; 
-        var startLon = tileoffsetlon;
+/**
+ * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
+ * Takes a prefix and returns the namespace URI associated with it on the given
+ *     node if found (and null if not). Supplying null for the prefix will
+ *     return the default namespace.
+ *
+ * For browsers that support it, this calls the native lookupNamesapceURI
+ *     function.  In other browsers, this is an implementation of
+ *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
+ *
+ * For browsers that don't support the attribute.ownerElement property, this
+ *     method cannot be called on attribute nodes.
+ *     
+ * Parameters:
+ * node - {DOMElement} The node from which to start looking.
+ * prefix - {String} The prefix to lookup or null to lookup the default namespace.
+ * 
+ * Returns:
+ * {String} The namespace URI for the given prefix.  Returns null if the prefix
+ *     cannot be found or the node is the wrong type.
+ */
+OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
+    OpenLayers.Format.XML.prototype.lookupNamespaceURI,
+    OpenLayers.Format.XML.prototype
+);
+/* ======================================================================
+    OpenLayers/Format/WFST.js
+   ====================================================================== */
 
-        var rowidx = 0;
-        
-        var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
-        var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
-        
-    
-        do {
-            var row = this.grid[rowidx++];
-            if (!row) {
-                row = [];
-                this.grid.push(row);
-            }
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
 
-            tileoffsetlon = startLon;
-            tileoffsetx = startX;
-            var colidx = 0;
- 
-            do {
-                var tileBounds = 
-                    new OpenLayers.Bounds(tileoffsetlon, 
-                                          tileoffsetlat, 
-                                          tileoffsetlon + tilelon,
-                                          tileoffsetlat + tilelat);
+/**
+ * @requires OpenLayers/Format.js
+ */
 
-                var x = tileoffsetx;
-                x -= layerContainerDivLeft;
+/**
+ * Function: OpenLayers.Format.WFST
+ * Used to create a versioned WFS protocol.  Default version is 1.0.0.
+ *
+ * Returns:
+ * {<OpenLayers.Format>} A WFST format of the given version.
+ */
+OpenLayers.Format.WFST = function(options) {
+    options = OpenLayers.Util.applyDefaults(
+        options, OpenLayers.Format.WFST.DEFAULTS
+    );
+    var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
+    if(!cls) {
+        throw "Unsupported WFST version: " + options.version;
+    }
+    return new cls(options);
+};
 
-                var y = tileoffsety;
-                y -= layerContainerDivTop;
+/**
+ * Constant: OpenLayers.Format.WFST.DEFAULTS
+ * {Object} Default properties for the WFST format.
+ */
+OpenLayers.Format.WFST.DEFAULTS = {
+    "version": "1.0.0"
+};
+/* ======================================================================
+    OpenLayers/Format/WFST/v1.js
+   ====================================================================== */
 
-                var px = new OpenLayers.Pixel(x, y);
-                var tile = row[colidx++];
-                if (!tile) {
-                    tile = this.addTile(tileBounds, px);
-                    this.addTileMonitoringHooks(tile);
-                    row.push(tile);
-                } else {
-                    tile.moveTo(tileBounds, px, false);
-                }
-     
-                tileoffsetlon += tilelon;       
-                tileoffsetx += this.tileSize.w;
-            } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer)
-                     || colidx < minCols);
-             
-            tileoffsetlat -= tilelat;
-            tileoffsety += this.tileSize.h;
-        } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
-                || rowidx < minRows);
-        
-        //shave off exceess rows and colums
-        this.removeExcessTiles(rowidx, colidx);
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
 
-        //now actually draw the tiles
-        this.spiralTileLoad();
-    },
+/**
+ * @requires OpenLayers/Format/XML.js
+ * @requires OpenLayers/Format/WFST.js
+ */
 
+/**
+ * Class: OpenLayers.Format.WFST.v1
+ * Superclass for WFST parsers.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+    
     /**
-     * Method: getMaxExtent
-     * Get this layer's maximum extent. (Implemented as a getter for
-     *     potential specific implementations in sub-classes.)
-     *
-     * Returns:
-     * {OpenLayers.Bounds}
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.
      */
-    getMaxExtent: function() {
-        return this.maxExtent;
+    namespaces: {
+        xlink: "http://www.w3.org/1999/xlink",
+        xsi: "http://www.w3.org/2001/XMLSchema-instance",
+        wfs: "http://www.opengis.net/wfs",
+        gml: "http://www.opengis.net/gml",
+        ogc: "http://www.opengis.net/ogc"
     },
     
     /**
-     * Method: spiralTileLoad
-     *   Starts at the top right corner of the grid and proceeds in a spiral 
-     *    towards the center, adding tiles one at a time to the beginning of a 
-     *    queue. 
-     * 
-     *   Once all the grid's tiles have been added to the queue, we go back 
-     *    and iterate through the queue (thus reversing the spiral order from 
-     *    outside-in to inside-out), calling draw() on each tile. 
+     * Property: defaultPrefix
      */
-    spiralTileLoad: function() {
-        var tileQueue = [];
- 
-        var directions = ["right", "down", "left", "up"];
+    defaultPrefix: "wfs",
 
-        var iRow = 0;
-        var iCell = -1;
-        var direction = OpenLayers.Util.indexOf(directions, "right");
-        var directionsTried = 0;
-        
-        while( directionsTried < directions.length) {
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: null,
 
-            var testRow = iRow;
-            var testCell = iCell;
+    /**
+     * Property: schemaLocation
+     * {String} Schema location for a particular minor version.
+     */
+    schemaLocations: null,
+    
+    /**
+     * APIProperty: srsName
+     * {String} URI for spatial reference system.
+     */
+    srsName: null,
 
-            switch (directions[direction]) {
-                case "right":
-                    testCell++;
-                    break;
-                case "down":
-                    testRow++;
-                    break;
-                case "left":
-                    testCell--;
-                    break;
-                case "up":
-                    testRow--;
-                    break;
-            } 
+    /**
+     * APIProperty: extractAttributes
+     * {Boolean} Extract attributes from GML.  Default is true.
+     */
+    extractAttributes: true,
     
-            // if the test grid coordinates are within the bounds of the 
-            //  grid, get a reference to the tile.
-            var tile = null;
-            if ((testRow < this.grid.length) && (testRow >= 0) &&
-                (testCell < this.grid[0].length) && (testCell >= 0)) {
-                tile = this.grid[testRow][testCell];
-            }
-            
-            if ((tile != null) && (!tile.queued)) {
-                //add tile to beginning of queue, mark it as queued.
-                tileQueue.unshift(tile);
-                tile.queued = true;
-                
-                //restart the directions counter and take on the new coords
-                directionsTried = 0;
-                iRow = testRow;
-                iCell = testCell;
+    /**
+     * APIProperty: xy
+     * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+     * Changing is not recommended, a new Format should be instantiated.
+     */ 
+    xy: true,
+
+    /**
+     * Property: stateName
+     * {Object} Maps feature states to node names.
+     */
+    stateName: null,
+
+    /**
+     * Constructor: OpenLayers.Format.WFST.v1
+     * Instances of this class are not created directly.  Use the
+     *     <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
+     *     constructor instead.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        // set state name mapping
+        this.stateName = {};
+        this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
+        this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
+        this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Method: getSrsName
+     */
+    getSrsName: function(feature, options) {
+        var srsName = options && options.srsName;
+        if(!srsName) {
+            if(feature && feature.layer) {
+                srsName = feature.layer.projection.getCode();
             } else {
-                //need to try to load a tile in a different direction
-                direction = (direction + 1) % 4;
-                directionsTried++;
+                srsName = this.srsName;
             }
-        } 
-        
-        // now we go through and draw the tiles in forward order
-        for(var i=0, len=tileQueue.length; i<len; i++) {
-            var tile = tileQueue[i];
-            tile.draw();
-            //mark tile as unqueued for the next time (since tiles are reused)
-            tile.queued = false;       
         }
+        return srsName;
     },
 
     /**
-     * APIMethod: addTile
-     * Gives subclasses of Grid the opportunity to create an 
-     * OpenLayer.Tile of their choosing. The implementer should initialize 
-     * the new tile and take whatever steps necessary to display it.
+     * APIMethod: read
+     * Parse the response from a transaction.  Because WFS is split into
+     *     Transaction requests (create, update, and delete) and GetFeature
+     *     requests (read), this method handles parsing of both types of
+     *     responses.
      *
-     * Parameters
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
+     * Parameters:
+     * data - {String | Document} The WFST document to read
+     * options - {Object} Options for the reader
      *
+     * Valid options properties:
+     * output - {String} either "features" or "object". The default is
+     *     "features", which means that the method will return an array of
+     *     features. If set to "object", an object with a "features" property
+     *     and other properties read by the parser will be returned.
+     *
      * Returns:
-     * {<OpenLayers.Tile>} The added OpenLayers.Tile
+     * {Array | Object} Output depending on the output option.
      */
-    addTile:function(bounds, position) {
-        // Should be implemented by subclasses
+    read: function(data, options) {
+        options = options || {};
+        OpenLayers.Util.applyDefaults(options, {
+            output: "features"
+        });
+        
+        if(typeof data == "string") { 
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+        }
+        if(data && data.nodeType == 9) {
+            data = data.documentElement;
+        }
+        var obj = {};
+        if(data) {
+            this.readNode(data, obj);
+        }
+        if(obj.features && options.output === "features") {
+            obj = obj.features;
+        }
+        return obj;
     },
     
-    /** 
-     * Method: addTileMonitoringHooks
-     * This function takes a tile as input and adds the appropriate hooks to 
-     *     the tile so that the layer can keep track of the loading tiles.
-     * 
-     * Parameters: 
-     * tile - {<OpenLayers.Tile>}
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
      */
-    addTileMonitoringHooks: function(tile) {
+    readers: {
+        "wfs": {
+            "FeatureCollection": function(node, obj) {
+                obj.features = [];
+                this.readChildNodes(node, obj);
+            }
+        }
+    },
+    
+    /**
+     * Method: write
+     * Given an array of features, write a WFS transaction.  This assumes
+     *     the features have a state property that determines the operation
+     *     type - insert, update, or delete.
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} A list of features.
+     *
+     * Returns:
+     * {String} A serialized WFS transaction.
+     */
+    write: function(features) {
+        var node = this.writeNode("wfs:Transaction", features);
+        var value = this.schemaLocationAttr();
+        if(value) {
+            this.setAttributeNS(
+                node, this.namespaces["xsi"], "xsi:schemaLocation",  value
+            )
+        }
+        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
+    },
+    
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "wfs": {
+            "GetFeature": function(options) {
+                var node = this.createElementNSPlus("wfs:GetFeature", {
+                    attributes: {
+                        service: "WFS",
+                        version: this.version,
+                        outputFormat: options && options.outputFormat,
+                        maxFeatures: options && options.maxFeatures,
+                        "xsi:schemaLocation": this.schemaLocationAttr(options)
+                    }
+                });
+                if (typeof this.featureType == "string") {
+                    this.writeNode("Query", options, node);
+                } else {
+                    for (var i=0,len = this.featureType.length; i<len; i++) { 
+                        options.featureType = this.featureType[i]; 
+                        this.writeNode("Query", options, node); 
+                    } 
+                }
+                return node;
+            },
+            "Transaction": function(features) {
+                var node = this.createElementNSPlus("wfs:Transaction", {
+                    attributes: {
+                        service: "WFS",
+                        version: this.version
+                    }
+                });
+                if(features) {
+                    var name, feature;
+                    for(var i=0, len=features.length; i<len; ++i) {
+                        feature = features[i];
+                        name = this.stateName[feature.state];
+                        if(name) {
+                            this.writeNode(name, feature, node);
+                        }
+                    }
+                }
+                return node;
+            },
+            "Insert": function(feature) {
+                var node = this.createElementNSPlus("wfs:Insert");
+                this.srsName = this.getSrsName(feature);
+                this.writeNode("feature:_typeName", feature, node);
+                return node;
+            },
+            "Update": function(feature) {
+                var node = this.createElementNSPlus("wfs:Update", {
+                    attributes: {
+                        typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
+                            this.featureType
+                    }
+                });
+                if(this.featureNS) {
+                    node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
+                }
+                
+                // add in geometry
+                if (this.geometryName !== null) {
+                    this.srsName = this.getSrsName(feature);
+                    this.writeNode(
+                        "Property", {name: this.geometryName, value: feature.geometry}, node
+                    );
+                }
         
-        tile.onLoadStart = function() {
-            //if that was first tile then trigger a 'loadstart' on the layer
-            if (this.numLoadingTiles == 0) {
-                this.events.triggerEvent("loadstart");
+                // add in attributes
+                for(var key in feature.attributes) {
+                    if(feature.attributes[key] !== undefined) {
+                        this.writeNode(
+                            "Property", {name: key, value: feature.attributes[key]}, node
+                        );
+                    }
+                }
+                
+                // add feature id filter
+                this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
+                    fids: [feature.fid]
+                }), node);
+        
+                return node;
+            },
+            "Property": function(obj) {
+                var node = this.createElementNSPlus("wfs:Property");
+                this.writeNode("Name", obj.name, node);
+                if(obj.value !== null) {
+                    this.writeNode("Value", obj.value, node);
+                }
+                return node;
+            },
+            "Name": function(name) {
+                return this.createElementNSPlus("wfs:Name", {value: name});
+            },
+            "Value": function(obj) {
+                var node;
+                if(obj instanceof OpenLayers.Geometry) {
+                    node = this.createElementNSPlus("wfs:Value");
+                    var geom = this.writeNode("feature:_geometry", obj).firstChild;
+                    node.appendChild(geom);
+                } else {
+                    node = this.createElementNSPlus("wfs:Value", {value: obj});                
+                }
+                return node;
+            },
+            "Delete": function(feature) {
+                var node = this.createElementNSPlus("wfs:Delete", {
+                    attributes: {
+                        typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
+                            this.featureType
+                    }
+                });
+                if(this.featureNS) {
+                    node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
+                }
+                this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
+                    fids: [feature.fid]
+                }), node);
+                return node;
             }
-            this.numLoadingTiles++;
-        };
-        tile.events.register("loadstart", this, tile.onLoadStart);
-      
-        tile.onLoadEnd = function() {
-            this.numLoadingTiles--;
-            this.events.triggerEvent("tileloaded");
-            //if that was the last tile, then trigger a 'loadend' on the layer
-            if (this.numLoadingTiles == 0) {
-                this.events.triggerEvent("loadend");
+        }
+    },
+
+    /**
+     * Method: schemaLocationAttr
+     * Generate the xsi:schemaLocation attribute value.
+     *
+     * Returns:
+     * {String} The xsi:schemaLocation attribute or undefined if none.
+     */
+    schemaLocationAttr: function(options) {
+        options = OpenLayers.Util.extend({
+            featurePrefix: this.featurePrefix,
+            schema: this.schema
+        }, options);
+        var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
+        if(options.schema) {
+            schemaLocations[options.featurePrefix] = options.schema;
+        }
+        var parts = [];
+        var uri;
+        for(var key in schemaLocations) {
+            uri = this.namespaces[key];
+            if(uri) {
+                parts.push(uri + " " + schemaLocations[key]);
             }
-        };
-        tile.events.register("loadend", this, tile.onLoadEnd);
-        tile.events.register("unload", this, tile.onLoadEnd);
+        }
+        var value = parts.join(" ") || undefined;
+        return value;
     },
+    
+    /**
+     * Method: setFilterProperty
+     * Set the property of each spatial filter.
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter>}
+     */
+    setFilterProperty: function(filter) {
+        if(filter.filters) {
+            for(var i=0, len=filter.filters.length; i<len; ++i) {
+                this.setFilterProperty(filter.filters[i]);
+            }
+        } else {
+            if(filter instanceof OpenLayers.Filter.Spatial) {
+                // got a spatial filter, set its property
+                filter.property = this.geometryName;
+            }
+        }
+    },
 
+    CLASS_NAME: "OpenLayers.Format.WFST.v1" 
+
+});
+/* ======================================================================
+    OpenLayers/Icon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
+ * Class: OpenLayers.Icon
+ * 
+ * The icon represents a graphical icon on the screen.  Typically used in
+ * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
+ *
+ * An icon has a url, size and position.  It also contains an offset which 
+ * allows the center point to be represented correctly.  This can be
+ * provided either as a fixed offset or a function provided to calculate
+ * the desired offset. 
+ * 
+ */
+OpenLayers.Icon = OpenLayers.Class({
+    
     /** 
-     * Method: removeTileMonitoringHooks
-     * This function takes a tile as input and removes the tile hooks 
-     *     that were added in addTileMonitoringHooks()
+     * Property: url 
+     * {String}  image url
+     */
+    url: null,
+    
+    /** 
+     * Property: size 
+     * {<OpenLayers.Size>} 
+     */
+    size: null,
+
+    /** 
+     * Property: offset 
+     * {<OpenLayers.Pixel>} distance in pixels to offset the image when being rendered
+     */
+    offset: null,    
+    
+    /** 
+     * Property: calculateOffset 
+     * {<OpenLayers.Pixel>} Function to calculate the offset (based on the size) 
+     */
+    calculateOffset: null,    
+    
+    /** 
+     * Property: imageDiv 
+     * {DOMElement} 
+     */
+    imageDiv: null,
+
+    /** 
+     * Property: px 
+     * {<OpenLayers.Pixel>} 
+     */
+    px: null,
+    
+    /** 
+     * Constructor: OpenLayers.Icon
+     * Creates an icon, which is an image tag in a div.  
+     *
+     * url - {String} 
+     * size - {<OpenLayers.Size>} 
+     * offset - {<OpenLayers.Pixel>}
+     * calculateOffset - {Function} 
+     */
+    initialize: function(url, size, offset, calculateOffset) {
+        this.url = url;
+        this.size = (size) ? size : new OpenLayers.Size(20,20);
+        this.offset = offset ? offset : new OpenLayers.Pixel(-(this.size.w/2), -(this.size.h/2));
+        this.calculateOffset = calculateOffset;
+
+        var id = OpenLayers.Util.createUniqueID("OL_Icon_");
+        this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
+    },
+    
+    /** 
+     * Method: destroy
+     * Nullify references and remove event listeners to prevent circular 
+     * references and memory leaks
+     */
+    destroy: function() {
+        // erase any drawn elements
+        this.erase();
+
+        OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
+        this.imageDiv.innerHTML = "";
+        this.imageDiv = null;
+    },
+
+    /** 
+     * Method: clone
      * 
-     * Parameters: 
-     * tile - {<OpenLayers.Tile>}
+     * Returns:
+     * {<OpenLayers.Icon>} A fresh copy of the icon.
      */
-    removeTileMonitoringHooks: function(tile) {
-        tile.unload();
-        tile.events.un({
-            "loadstart": tile.onLoadStart,
-            "loadend": tile.onLoadEnd,
-            "unload": tile.onLoadEnd,
-            scope: this
-        });
+    clone: function() {
+        return new OpenLayers.Icon(this.url, 
+                                   this.size, 
+                                   this.offset, 
+                                   this.calculateOffset);
     },
     
     /**
-     * Method: moveGriddedTiles
+     * Method: setSize
      * 
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
+     * size - {<OpenLayers.Size>} 
      */
-    moveGriddedTiles: function(bounds) {
-        var buffer = this.buffer || 1;
-        while (true) {
-            var tlLayer = this.grid[0][0].position;
-            var tlViewPort = 
-                this.map.getViewPortPxFromLayerPx(tlLayer);
-            if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
-                this.shiftColumn(true);
-            } else if (tlViewPort.x < -this.tileSize.w * buffer) {
-                this.shiftColumn(false);
-            } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) {
-                this.shiftRow(true);
-            } else if (tlViewPort.y < -this.tileSize.h * buffer) {
-                this.shiftRow(false);
-            } else {
-                break;
-            }
-        };
+    setSize: function(size) {
+        if (size != null) {
+            this.size = size;
+        }
+        this.draw();
     },
-
+    
     /**
-     * Method: shiftRow
-     * Shifty grid work
-     *
+     * Method: setUrl
+     * 
      * Parameters:
-     * prepend - {Boolean} if true, prepend to beginning.
-     *                          if false, then append to end
+     * url - {String} 
      */
-    shiftRow:function(prepend) {
-        var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
-        var grid = this.grid;
-        var modelRow = grid[modelRowIndex];
+    setUrl: function(url) {
+        if (url != null) {
+            this.url = url;
+        }
+        this.draw();
+    },
 
-        var resolution = this.map.getResolution();
-        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
-        var deltaLat = resolution * -deltaY;
+    /** 
+     * Method: draw
+     * Move the div to the given pixel.
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} 
+     * 
+     * Returns:
+     * {DOMElement} A new DOM Image of this icon set at the location passed-in
+     */
+    draw: function(px) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
+                                            null, 
+                                            null, 
+                                            this.size, 
+                                            this.url, 
+                                            "absolute");
+        this.moveTo(px);
+        return this.imageDiv;
+    }, 
 
-        var row = (prepend) ? grid.pop() : grid.shift();
-
-        for (var i=0, len=modelRow.length; i<len; i++) {
-            var modelTile = modelRow[i];
-            var bounds = modelTile.bounds.clone();
-            var position = modelTile.position.clone();
-            bounds.bottom = bounds.bottom + deltaLat;
-            bounds.top = bounds.top + deltaLat;
-            position.y = position.y + deltaY;
-            row[i].moveTo(bounds, position);
+    /** 
+     * Method: erase
+     * Erase the underlying image element.
+     *
+     */
+    erase: function() {
+        if (this.imageDiv != null && this.imageDiv.parentNode != null) {
+            OpenLayers.Element.remove(this.imageDiv);
         }
+    }, 
+    
+    /** 
+     * Method: setOpacity
+     * Change the icon's opacity
+     *
+     * Parameters:
+     * opacity - {float} 
+     */
+    setOpacity: function(opacity) {
+        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
+                                            null, null, null, null, opacity);
 
-        if (prepend) {
-            grid.unshift(row);
-        } else {
-            grid.push(row);
-        }
     },
-
+    
     /**
-     * Method: shiftColumn
-     * Shift grid work in the other dimension
+     * Method: moveTo
+     * move icon to passed in px.
      *
      * Parameters:
-     * prepend - {Boolean} if true, prepend to beginning.
-     *                          if false, then append to end
+     * px - {<OpenLayers.Pixel>} 
      */
-    shiftColumn: function(prepend) {
-        var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
-        var resolution = this.map.getResolution();
-        var deltaLon = resolution * deltaX;
+    moveTo: function (px) {
+        //if no px passed in, use stored location
+        if (px != null) {
+            this.px = px;
+        }
 
-        for (var i=0, len=this.grid.length; i<len; i++) {
-            var row = this.grid[i];
-            var modelTileIndex = (prepend) ? 0 : (row.length - 1);
-            var modelTile = row[modelTileIndex];
-            
-            var bounds = modelTile.bounds.clone();
-            var position = modelTile.position.clone();
-            bounds.left = bounds.left + deltaLon;
-            bounds.right = bounds.right + deltaLon;
-            position.x = position.x + deltaX;
-
-            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift();
-            tile.moveTo(bounds, position);
-            if (prepend) {
-                row.unshift(tile);
+        if (this.imageDiv != null) {
+            if (this.px == null) {
+                this.display(false);
             } else {
-                row.push(tile);
+                if (this.calculateOffset) {
+                    this.offset = this.calculateOffset(this.size);  
+                }
+                var offsetPx = this.px.offset(this.offset);
+                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
             }
         }
     },
     
+    /** 
+     * Method: display
+     * Hide or show the icon
+     *
+     * Parameters:
+     * display - {Boolean} 
+     */
+    display: function(display) {
+        this.imageDiv.style.display = (display) ? "" : "none"; 
+    },
+    
+
     /**
-     * Method: removeExcessTiles
-     * When the size of the map or the buffer changes, we may need to
-     *     remove some excess rows and columns.
+     * APIMethod: isDrawn
      * 
+     * Returns:
+     * {Boolean} Whether or not the icon is drawn.
+     */
+    isDrawn: function() {
+        // nodeType 11 for ie, whose nodes *always* have a parentNode
+        // (of type document fragment)
+        var isDrawn = (this.imageDiv && this.imageDiv.parentNode && 
+                       (this.imageDiv.parentNode.nodeType != 11));    
+
+        return isDrawn;   
+    },
+
+    CLASS_NAME: "OpenLayers.Icon"
+});
+/* ======================================================================
+    OpenLayers/Marker.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Events.js
+ * @requires OpenLayers/Icon.js
+ */
+
+/**
+ * Class: OpenLayers.Marker
+ * Instances of OpenLayers.Marker are a combination of a 
+ * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
+ *
+ * Markers are generally added to a special layer called
+ * <OpenLayers.Layer.Markers>.
+ *
+ * Example:
+ * (code)
+ * var markers = new OpenLayers.Layer.Markers( "Markers" );
+ * map.addLayer(markers);
+ *
+ * var size = new OpenLayers.Size(21,25);
+ * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
+ * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
+ *
+ * (end)
+ *
+ * Note that if you pass an icon into the Marker constructor, it will take
+ * that icon and use it. This means that you should not share icons between
+ * markers -- you use them once, but you should clone() for any additional
+ * markers using that same icon.
+ */
+OpenLayers.Marker = OpenLayers.Class({
+    
+    /** 
+     * Property: icon 
+     * {<OpenLayers.Icon>} The icon used by this marker.
+     */
+    icon: null,
+
+    /** 
+     * Property: lonlat 
+     * {<OpenLayers.LonLat>} location of object
+     */
+    lonlat: null,
+    
+    /** 
+     * Property: events 
+     * {<OpenLayers.Events>} the event handler.
+     */
+    events: null,
+    
+    /** 
+     * Property: map 
+     * {<OpenLayers.Map>} the map this marker is attached to
+     */
+    map: null,
+    
+    /** 
+     * Constructor: OpenLayers.Marker
      * Parameters:
-     * rows - {Integer} Maximum number of rows we want our grid to have.
-     * colums - {Integer} Maximum number of columns we want our grid to have.
+     * lonlat - {<OpenLayers.LonLat>} the position of this marker
+     * icon - {<OpenLayers.Icon>}  the icon for this marker
      */
-    removeExcessTiles: function(rows, columns) {
+    initialize: function(lonlat, icon) {
+        this.lonlat = lonlat;
         
-        // remove extra rows
-        while (this.grid.length > rows) {
-            var row = this.grid.pop();
-            for (var i=0, l=row.length; i<l; i++) {
-                var tile = row[i];
-                this.removeTileMonitoringHooks(tile);
-                tile.destroy();
-            }
+        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
+        if (this.icon == null) {
+            this.icon = newIcon;
+        } else {
+            this.icon.url = newIcon.url;
+            this.icon.size = newIcon.size;
+            this.icon.offset = newIcon.offset;
+            this.icon.calculateOffset = newIcon.calculateOffset;
         }
-        
-        // remove extra columns
-        while (this.grid[0].length > columns) {
-            for (var i=0, l=this.grid.length; i<l; i++) {
-                var row = this.grid[i];
-                var tile = row.pop();
-                this.removeTileMonitoringHooks(tile);
-                tile.destroy();
-            }
+        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Destroy the marker. You must first remove the marker from any 
+     * layer which it has been added to, or you will get buggy behavior.
+     * (This can not be done within the marker since the marker does not
+     * know which layer it is attached to.)
+     */
+    destroy: function() {
+        // erase any drawn features
+        this.erase();
+
+        this.map = null;
+
+        this.events.destroy();
+        this.events = null;
+
+        if (this.icon != null) {
+            this.icon.destroy();
+            this.icon = null;
         }
     },
+    
+    /** 
+    * Method: draw
+    * Calls draw on the icon, and returns that output.
+    * 
+    * Parameters:
+    * px - {<OpenLayers.Pixel>}
+    * 
+    * Returns:
+    * {DOMElement} A new DOM Image with this marker's icon set at the 
+    * location passed-in
+    */
+    draw: function(px) {
+        return this.icon.draw(px);
+    }, 
 
+    /** 
+    * Method: erase
+    * Erases any drawn elements for this marker.
+    */
+    erase: function() {
+        if (this.icon != null) {
+            this.icon.erase();
+        }
+    }, 
+
     /**
-     * Method: onMapResize
-     * For singleTile layers, this will set a new tile size according to the
-     * dimensions of the map pane.
+    * Method: moveTo
+    * Move the marker to the new location.
+    *
+    * Parameters:
+    * px - {<OpenLayers.Pixel>} the pixel position to move to
+    */
+    moveTo: function (px) {
+        if ((px != null) && (this.icon != null)) {
+            this.icon.moveTo(px);
+        }           
+        this.lonlat = this.map.getLonLatFromLayerPx(px);
+    },
+
+    /**
+     * APIMethod: isDrawn
+     * 
+     * Returns:
+     * {Boolean} Whether or not the marker is drawn.
      */
-    onMapResize: function() {
-        if (this.singleTile) {
-            this.clearGrid();
-            this.setTileSize();
-        }
+    isDrawn: function() {
+        var isDrawn = (this.icon && this.icon.isDrawn());
+        return isDrawn;   
     },
+
+    /**
+     * Method: onScreen
+     *
+     * Returns:
+     * {Boolean} Whether or not the marker is currently visible on screen.
+     */
+    onScreen:function() {
+        
+        var onScreen = false;
+        if (this.map) {
+            var screenBounds = this.map.getExtent();
+            onScreen = screenBounds.containsLonLat(this.lonlat);
+        }    
+        return onScreen;
+    },
     
     /**
-     * APIMethod: getTileBounds
-     * Returns The tile bounds for a layer given a pixel location.
+     * Method: inflate
+     * Englarges the markers icon by the specified ratio.
      *
      * Parameters:
-     * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
-     *
-     * Returns:
-     * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
+     * inflate - {float} the ratio to enlarge the marker by (passing 2
+     *                   will double the size).
      */
-    getTileBounds: function(viewPortPx) {
-        var maxExtent = this.maxExtent;
-        var resolution = this.getResolution();
-        var tileMapWidth = resolution * this.tileSize.w;
-        var tileMapHeight = resolution * this.tileSize.h;
-        var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
-        var tileLeft = maxExtent.left + (tileMapWidth *
-                                         Math.floor((mapPoint.lon -
-                                                     maxExtent.left) /
-                                                    tileMapWidth));
-        var tileBottom = maxExtent.bottom + (tileMapHeight *
-                                             Math.floor((mapPoint.lat -
-                                                         maxExtent.bottom) /
-                                                        tileMapHeight));
-        return new OpenLayers.Bounds(tileLeft, tileBottom,
-                                     tileLeft + tileMapWidth,
-                                     tileBottom + tileMapHeight);
+    inflate: function(inflate) {
+        if (this.icon) {
+            var newSize = new OpenLayers.Size(this.icon.size.w * inflate,
+                                              this.icon.size.h * inflate);
+            this.icon.setSize(newSize);
+        }        
     },
     
-    CLASS_NAME: "OpenLayers.Layer.Grid"
+    /** 
+     * Method: setOpacity
+     * Change the opacity of the marker by changin the opacity of 
+     *   its icon
+     * 
+     * Parameters:
+     * opacity - {float}  Specified as fraction (0.4, etc)
+     */
+    setOpacity: function(opacity) {
+        this.icon.setOpacity(opacity);
+    },
+
+    /**
+     * Method: setUrl
+     * Change URL of the Icon Image.
+     * 
+     * url - {String} 
+     */
+    setUrl: function(url) {
+        this.icon.setUrl(url);
+    },    
+
+    /** 
+     * Method: display
+     * Hide or show the icon
+     * 
+     * display - {Boolean} 
+     */
+    display: function(display) {
+        this.icon.display(display);
+    },
+
+    CLASS_NAME: "OpenLayers.Marker"
 });
+
+
+/**
+ * Function: defaultIcon
+ * Creates a default <OpenLayers.Icon>.
+ * 
+ * Returns:
+ * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
+ */
+OpenLayers.Marker.defaultIcon = function() {
+    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
+    var size = new OpenLayers.Size(21, 25);
+    var calculateOffset = function(size) {
+                    return new OpenLayers.Pixel(-(size.w/2), -size.h);
+                 };
+
+    return new OpenLayers.Icon(url, size, null, calculateOffset);        
+};
+    
+
 /* ======================================================================
-    OpenLayers/Layer/VirtualEarth.js
+    OpenLayers/Popup.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Layer/SphericalMercator.js
- * @requires OpenLayers/Layer/EventPane.js
- * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/BaseTypes/Class.js
  */
 
+
 /**
- * Class: OpenLayers.Layer.VirtualEarth
- * 
- * Inherits from:
- *  - <OpenLayers.Layer.EventPane>
- *  - <OpenLayers.Layer.FixedZoomLevels>
+ * Class: OpenLayers.Popup
+ * A popup is a small div that can opened and closed on the map.
+ * Typically opened in response to clicking on a marker.  
+ * See <OpenLayers.Marker>.  Popup's don't require their own
+ * layer and are added the the map using the <OpenLayers.Map.addPopup>
+ * method.
+ *
+ * Example:
+ * (code)
+ * popup = new OpenLayers.Popup("chicken", 
+ *                    new OpenLayers.LonLat(5,40),
+ *                    new OpenLayers.Size(200,200),
+ *                    "example popup",
+ *                    true);
+ *       
+ * map.addPopup(popup);
+ * (end)
  */
-OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
-    OpenLayers.Layer.EventPane,
-    OpenLayers.Layer.FixedZoomLevels, {
+OpenLayers.Popup = OpenLayers.Class({
+
+    /** 
+     * Property: events  
+     * {<OpenLayers.Events>} custom event manager 
+     */
+    events: null,
     
+    /** Property: id
+     * {String} the unique identifier assigned to this popup.
+     */
+    id: "",
+
     /** 
-     * Constant: MIN_ZOOM_LEVEL
-     * {Integer} 1 
+     * Property: lonlat 
+     * {<OpenLayers.LonLat>} the position of this popup on the map
      */
-    MIN_ZOOM_LEVEL: 1,
+    lonlat: null,
+
+    /** 
+     * Property: div 
+     * {DOMElement} the div that contains this popup.
+     */
+    div: null,
+
+    /** 
+     * Property: contentSize 
+     * {<OpenLayers.Size>} the width and height of the content.
+     */
+    contentSize: null,    
+
+    /** 
+     * Property: size 
+     * {<OpenLayers.Size>} the width and height of the popup.
+     */
+    size: null,    
+
+    /** 
+     * Property: contentHTML 
+     * {String} An HTML string for this popup to display.
+     */
+    contentHTML: null,
     
     /** 
-     * Constant: MAX_ZOOM_LEVEL
-     * {Integer} 19
+     * Property: backgroundColor 
+     * {String} the background color used by the popup.
      */
-    MAX_ZOOM_LEVEL: 19,
+    backgroundColor: "",
+    
+    /** 
+     * Property: opacity 
+     * {float} the opacity of this popup (between 0.0 and 1.0)
+     */
+    opacity: "",
 
     /** 
-     * Constant: RESOLUTIONS
-     * {Array(Float)} Hardcode these resolutions so that they are more closely
-     *                tied with the standard wms projection
+     * Property: border 
+     * {String} the border size of the popup.  (eg 2px)
      */
-    RESOLUTIONS: [
-        1.40625, 
-        0.703125, 
-        0.3515625, 
-        0.17578125, 
-        0.087890625, 
-        0.0439453125,
-        0.02197265625, 
-        0.010986328125, 
-        0.0054931640625, 
-        0.00274658203125,
-        0.001373291015625, 
-        0.0006866455078125, 
-        0.00034332275390625, 
-        0.000171661376953125, 
-        0.0000858306884765625, 
-        0.00004291534423828125,
-        0.00002145767211914062, 
-        0.00001072883605957031,
-        0.00000536441802978515
-    ],
+    border: "",
+    
+    /** 
+     * Property: contentDiv 
+     * {DOMElement} a reference to the element that holds the content of
+     *              the div.
+     */
+    contentDiv: null,
+    
+    /** 
+     * Property: groupDiv 
+     * {DOMElement} First and only child of 'div'. The group Div contains the
+     *     'contentDiv' and the 'closeDiv'.
+     */
+    groupDiv: null,
 
-    /**
-     * APIProperty: type
-     * {VEMapType}
+    /** 
+     * Property: closeDiv
+     * {DOMElement} the optional closer image
      */
-    type: null,
+    closeDiv: null,
 
-    /**
-     * APIProperty: wrapDateLine
-     * {Boolean} Allow user to pan forever east/west.  Default is true.  
-     *     Setting this to false only restricts panning if 
-     *     <sphericalMercator> is true. 
+    /** 
+     * APIProperty: autoSize
+     * {Boolean} Resize the popup to auto-fit the contents.
+     *     Default is false.
      */
-    wrapDateLine: true,
+    autoSize: false,
 
     /**
-     * APIProperty: sphericalMercator
-     * {Boolean} Should the map act as a mercator-projected map? This will
-     *     cause all interactions with the map to be in the actual map
-     *     projection, which allows support for vector drawing, overlaying
-     *     other maps, etc. 
+     * APIProperty: minSize
+     * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
      */
-    sphericalMercator: false,
-    
+    minSize: null,
+
     /**
-     * APIProperty: animationEnabled
-     * {Boolean} If set to true, the transition between zoom levels will be
-     *     animated. Set to false to match the zooming experience of other
-     *     layer types. Default is true.
+     * APIProperty: maxSize
+     * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
      */
-    animationEnabled: true, 
+    maxSize: null,
 
     /** 
-     * Constructor: OpenLayers.Layer.VirtualEarth
+     * Property: displayClass
+     * {String} The CSS class of the popup.
+     */
+    displayClass: "olPopup",
+
+    /** 
+     * Property: contentDisplayClass
+     * {String} The CSS class of the popup content div.
+     */
+    contentDisplayClass: "olPopupContent",
+
+    /** 
+     * Property: padding 
+     * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal 
+     *     padding of the content div inside the popup. This was originally
+     *     confused with the css padding as specified in style.css's 
+     *     'olPopupContent' class. We would like to get rid of this altogether,
+     *     except that it does come in handy for the framed and anchoredbubble
+     *     popups, who need to maintain yet another barrier between their 
+     *     content and the outer border of the popup itself. 
      * 
-     * Parameters:
-     * name - {String}
-     * options - {Object}
+     *     Note that in order to not break API, we must continue to support 
+     *     this property being set as an integer. Really, though, we'd like to 
+     *     have this specified as a Bounds object so that user can specify
+     *     distinct left, top, right, bottom paddings. With the 3.0 release
+     *     we can make this only a bounds.
      */
-    initialize: function(name, options) {
-        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
-        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
-                                                                    arguments);
-        if(this.sphericalMercator) {
-            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
-            this.initMercatorParameters();
+    padding: 0,
+
+    /** 
+     * Property: disableFirefoxOverflowHack
+     * {Boolean} The hack for overflow in Firefox causes all elements 
+     *     to be re-drawn, which causes Flash elements to be 
+     *     re-initialized, which is troublesome.
+     *     With this property the hack can be disabled.
+     */
+    disableFirefoxOverflowHack: false,
+
+    /**
+     * Method: fixPadding
+     * To be removed in 3.0, this function merely helps us to deal with the 
+     *     case where the user may have set an integer value for padding, 
+     *     instead of an <OpenLayers.Bounds> object.
+     */
+    fixPadding: function() {
+        if (typeof this.padding == "number") {
+            this.padding = new OpenLayers.Bounds(
+                this.padding, this.padding, this.padding, this.padding
+            );
         }
     },
+
+    /**
+     * APIProperty: panMapIfOutOfView
+     * {Boolean} When drawn, pan map such that the entire popup is visible in
+     *     the current viewport (if necessary).
+     *     Default is false.
+     */
+    panMapIfOutOfView: false,
     
     /**
-     * Method: loadMapObject
+     * 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.
      */
-    loadMapObject:function() {
+    keepInMap: false,
 
-        // create div and set to same size as map
-        var veDiv = OpenLayers.Util.createDiv(this.name);
-        var sz = this.map.getSize();
-        veDiv.style.width = sz.w + "px";
-        veDiv.style.height = sz.h + "px";
-        this.div.appendChild(veDiv);
+    /**
+     * APIProperty: closeOnMove
+     * {Boolean} When map pans, close the popup.
+     *     Default is false.
+     */
+    closeOnMove: false,
+    
+    /** 
+     * Property: map 
+     * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
+     */
+    map: null,
 
-        try { // crash prevention
-            this.mapObject = new VEMap(this.name);
-        } catch (e) { }
+    /** 
+    * Constructor: OpenLayers.Popup
+    * Create a popup.
+    * 
+    * Parameters: 
+    * id - {String} a unqiue identifier for this popup.  If null is passed
+    *               an identifier will be automatically generated. 
+    * lonlat - {<OpenLayers.LonLat>}  The position on the map the popup will
+    *                                 be shown.
+    * contentSize - {<OpenLayers.Size>} The size of the content.
+    * contentHTML - {String}          An HTML string to display inside the   
+    *                                 popup.
+    * closeBox - {Boolean}            Whether to display a close box inside
+    *                                 the popup.
+    * closeBoxCallback - {Function}   Function to be called on closeBox click.
+    */
+    initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
+        if (id == null) {
+            id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+        }
 
-        if (this.mapObject != null) {
-            try { // this is to catch a Mozilla bug without falling apart
+        this.id = id;
+        this.lonlat = lonlat;
 
-                // The fourth argument is whether the map is 'fixed' -- not 
-                // draggable. See: 
-                // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
-                //
-                this.mapObject.LoadMap(null, null, this.type, true);
-                this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
-
-            } catch (e) { }
-            this.mapObject.HideDashboard();
-            if(typeof this.mapObject.SetAnimationEnabled == "function") {
-                this.mapObject.SetAnimationEnabled(this.animationEnabled);
-            }
+        this.contentSize = (contentSize != null) ? contentSize 
+                                  : new OpenLayers.Size(
+                                                   OpenLayers.Popup.WIDTH,
+                                                   OpenLayers.Popup.HEIGHT);
+        if (contentHTML != null) { 
+             this.contentHTML = contentHTML;
         }
+        this.backgroundColor = OpenLayers.Popup.COLOR;
+        this.opacity = OpenLayers.Popup.OPACITY;
+        this.border = OpenLayers.Popup.BORDER;
 
-        //can we do smooth panning? this is an unpublished method, so we need 
-        // to be careful
-        if ( !this.mapObject ||
-             !this.mapObject.vemapcontrol ||
-             !this.mapObject.vemapcontrol.PanMap ||
-             (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
+        this.div = OpenLayers.Util.createDiv(this.id, null, null, 
+                                             null, null, null, "hidden");
+        this.div.className = this.displayClass;
+        
+        var groupDivId = this.id + "_GroupDiv";
+        this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, 
+                                                    null, "relative", null,
+                                                    "hidden");
 
-            this.dragPanMapObject = null;
-        }
+        var id = this.div.id + "_contentDiv";
+        this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), 
+                                                    null, "relative");
+        this.contentDiv.className = this.contentDisplayClass;
+        this.groupDiv.appendChild(this.contentDiv);
+        this.div.appendChild(this.groupDiv);
 
-    },
+        if (closeBox) {
+            this.addCloseBox(closeBoxCallback);
+        } 
 
-    /**
-     * Method: onMapResize
-     */
-    onMapResize: function() {
-        this.mapObject.Resize(this.map.size.w, this.map.size.h);
+        this.registerEvents();
     },
 
     /** 
-     * APIMethod: getWarningHTML
-     * 
-     * Returns: 
-     * {String} String with information on why layer is broken, how to get
-     *          it working.
+     * Method: destroy
+     * nullify references to prevent circular references and memory leaks
      */
-    getWarningHTML:function() {
-        return OpenLayers.i18n(
-            "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
-        );
+    destroy: function() {
+
+        this.id = null;
+        this.lonlat = null;
+        this.size = null;
+        this.contentHTML = null;
+        
+        this.backgroundColor = null;
+        this.opacity = null;
+        this.border = null;
+        
+        if (this.closeOnMove && this.map) {
+            this.map.events.unregister("movestart", this, this.hide);
+        }
+
+        this.events.destroy();
+        this.events = null;
+        
+        if (this.closeDiv) {
+            OpenLayers.Event.stopObservingElement(this.closeDiv); 
+            this.groupDiv.removeChild(this.closeDiv);
+        }
+        this.closeDiv = null;
+        
+        this.div.removeChild(this.groupDiv);
+        this.groupDiv = null;
+
+        if (this.map != null) {
+            this.map.removePopup(this);
+        }
+        this.map = null;
+        this.div = null;
+        
+        this.autoSize = null;
+        this.minSize = null;
+        this.maxSize = null;
+        this.padding = null;
+        this.panMapIfOutOfView = null;
     },
 
+    /** 
+    * Method: draw
+    * Constructs the elements that make up the popup.
+    *
+    * Parameters:
+    * px - {<OpenLayers.Pixel>} the position the popup in pixels.
+    * 
+    * Returns:
+    * {DOMElement} Reference to a div that contains the drawn popup
+    */
+    draw: function(px) {
+        if (px == null) {
+            if ((this.lonlat != null) && (this.map != null)) {
+                px = this.map.getLayerPxFromLonLat(this.lonlat);
+            }
+        }
 
+        // this assumes that this.map already exists, which is okay because 
+        // this.draw is only called once the popup has been added to the map.
+        if (this.closeOnMove) {
+            this.map.events.register("movestart", this, this.hide);
+        }
+        
+        //listen to movestart, moveend to disable overflow (FF bug)
+        if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
+            this.map.events.register("movestart", this, function() {
+                var style = document.defaultView.getComputedStyle(
+                    this.contentDiv, null
+                );
+                var currentOverflow = style.getPropertyValue("overflow");
+                if (currentOverflow != "hidden") {
+                    this.contentDiv._oldOverflow = currentOverflow;
+                    this.contentDiv.style.overflow = "hidden";
+                }
+            });
+            this.map.events.register("moveend", this, function() {
+                var oldOverflow = this.contentDiv._oldOverflow;
+                if (oldOverflow) {
+                    this.contentDiv.style.overflow = oldOverflow;
+                    this.contentDiv._oldOverflow = null;
+                }
+            });
+        }
 
-    /************************************
-     *                                  *
-     *   MapObject Interface Controls   *
-     *                                  *
-     ************************************/
+        this.moveTo(px);
+        if (!this.autoSize && !this.size) {
+            this.setSize(this.contentSize);
+        }
+        this.setBackgroundColor();
+        this.setOpacity();
+        this.setBorder();
+        this.setContentHTML();
+        
+        if (this.panMapIfOutOfView) {
+            this.panIntoView();
+        }    
 
+        return this.div;
+    },
 
-  // Get&Set Center, Zoom
+    /** 
+     * Method: updatePosition
+     * if the popup has a lonlat and its map members set, 
+     * then have it move itself to its proper position
+     */
+    updatePosition: function() {
+        if ((this.lonlat) && (this.map)) {
+            var px = this.map.getLayerPxFromLonLat(this.lonlat);
+            if (px) {
+                this.moveTo(px);           
+            }    
+        }
+    },
 
-    /** 
-     * APIMethod: setMapObjectCenter
-     * Set the mapObject to the specified center and zoom
+    /**
+     * Method: moveTo
      * 
      * Parameters:
-     * center - {Object} MapObject LonLat format
-     * zoom - {int} MapObject zoom format
+     * px - {<OpenLayers.Pixel>} the top and left position of the popup div. 
      */
-    setMapObjectCenter: function(center, zoom) {
-        this.mapObject.SetCenterAndZoom(center, zoom); 
+    moveTo: function(px) {
+        if ((px != null) && (this.div != null)) {
+            this.div.style.left = px.x + "px";
+            this.div.style.top = px.y + "px";
+        }
     },
-   
+
     /**
-     * APIMethod: getMapObjectCenter
-     * 
-     * Returns: 
-     * {Object} The mapObject's current center in Map Object format
+     * Method: visible
+     *
+     * Returns:      
+     * {Boolean} Boolean indicating whether or not the popup is visible
      */
-    getMapObjectCenter: function() {
-        return this.mapObject.GetCenter();
+    visible: function() {
+        return OpenLayers.Element.visible(this.div);
     },
 
     /**
-     * APIMethod: dragPanMapObject
-     * 
-     * Parameters:
-     * dX - {Integer}
-     * dY - {Integer}
+     * Method: toggle
+     * Toggles visibility of the popup.
      */
-    dragPanMapObject: function(dX, dY) {
-        this.mapObject.vemapcontrol.PanMap(dX, -dY);
+    toggle: function() {
+        if (this.visible()) {
+            this.hide();
+        } else {
+            this.show();
+        }
     },
 
-    /** 
-     * APIMethod: getMapObjectZoom
-     * 
-     * Returns:
-     * {Integer} The mapObject's current zoom, in Map Object format
+    /**
+     * Method: show
+     * Makes the popup visible.
      */
-    getMapObjectZoom: function() {
-        return this.mapObject.GetZoomLevel();
+    show: function() {
+        OpenLayers.Element.show(this.div);
+
+        if (this.panMapIfOutOfView) {
+            this.panIntoView();
+        }    
     },
 
+    /**
+     * Method: hide
+     * Makes the popup invisible.
+     */
+    hide: function() {
+        OpenLayers.Element.hide(this.div);
+    },
 
-  // LonLat - Pixel Translation
-  
     /**
-     * APIMethod: getMapObjectLonLatFromMapObjectPixel
+     * Method: setSize
+     * Used to adjust the size of the popup. 
+     *
+     * Parameters:
+     * contentSize - {<OpenLayers.Size>} the new size for the popup's 
+     *     contents div (in pixels).
+     */
+    setSize:function(contentSize) { 
+        this.size = contentSize.clone(); 
+        
+        // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
+        //  must add that to the desired "size". 
+        var contentDivPadding = this.getContentDivPadding();
+        var wPadding = contentDivPadding.left + contentDivPadding.right;
+        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
+
+        // take into account the popup's 'padding' property
+        this.fixPadding();
+        wPadding += this.padding.left + this.padding.right;
+        hPadding += this.padding.top + this.padding.bottom;
+
+        // make extra space for the close div
+        if (this.closeDiv) {
+            var closeDivWidth = parseInt(this.closeDiv.style.width);
+            wPadding += closeDivWidth + contentDivPadding.right;
+        }
+
+        //increase size of the main popup div to take into account the 
+        // users's desired padding and close div.        
+        this.size.w += wPadding;
+        this.size.h += hPadding;
+
+        //now if our browser is IE, we need to actually make the contents 
+        // div itself bigger to take its own padding into effect. this makes 
+        // me want to shoot someone, but so it goes.
+        if (OpenLayers.BROWSER_NAME == "msie") {
+            this.contentSize.w += 
+                contentDivPadding.left + contentDivPadding.right;
+            this.contentSize.h += 
+                contentDivPadding.bottom + contentDivPadding.top;
+        }
+
+        if (this.div != null) {
+            this.div.style.width = this.size.w + "px";
+            this.div.style.height = this.size.h + "px";
+        }
+        if (this.contentDiv != null){
+            this.contentDiv.style.width = contentSize.w + "px";
+            this.contentDiv.style.height = contentSize.h + "px";
+        }
+    },  
+
+    /**
+     * APIMethod: updateSize
+     * Auto size the popup so that it precisely fits its contents (as 
+     *     determined by this.contentDiv.innerHTML). Popup size will, of
+     *     course, be limited by the available space on the current map
+     */
+    updateSize: function() {
+        
+        // determine actual render dimensions of the contents by putting its
+        // contents into a fake contentDiv (for the CSS) and then measuring it
+        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,
+                containerElement: containerElement
+            }
+        );
+
+        // is the "real" size of the div is safe to display in our map?
+        var safeSize = this.getSafeContentSize(realSize);
+
+        var newSize = null;
+        if (safeSize.equals(realSize)) {
+            //real size of content is small enough to fit on the map, 
+            // so we use real size.
+            newSize = realSize;
+
+        } else {
+
+            //make a new OL.Size object with the clipped dimensions 
+            // set or null if not clipped.
+            var fixedSize = new OpenLayers.Size();
+            fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
+            fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
+        
+            if (fixedSize.w && fixedSize.h) {
+                //content is too big in both directions, so we will use 
+                // max popup size (safeSize), knowing well that it will 
+                // overflow both ways.                
+                newSize = safeSize;
+            } else {
+                //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,
+                        containerElement: containerElement
+                    }
+                );
+                
+                //if the clipped size is still the same as the safeSize, 
+                // that means that our content must be fixed in the 
+                // offending direction. If overflow is 'auto', this means 
+                // we are going to have a scrollbar for sure, so we must 
+                // adjust for that.
+                //
+                var currentOverflow = OpenLayers.Element.getStyle(
+                    this.contentDiv, "overflow"
+                );
+                if ( (currentOverflow != "hidden") && 
+                     (clippedSize.equals(safeSize)) ) {
+                    var scrollBar = OpenLayers.Util.getScrollbarWidth();
+                    if (fixedSize.w) {
+                        clippedSize.h += scrollBar;
+                    } else {
+                        clippedSize.w += scrollBar;
+                    }
+                }
+                
+                newSize = this.getSafeContentSize(clippedSize);
+            }
+        }                        
+        this.setSize(newSize);     
+    },    
+
+    /**
+     * Method: setBackgroundColor
+     * Sets the background color of the popup.
+     *
+     * Parameters:
+     * color - {String} the background color.  eg "#FFBBBB"
+     */
+    setBackgroundColor:function(color) { 
+        if (color != undefined) {
+            this.backgroundColor = color; 
+        }
+        
+        if (this.div != null) {
+            this.div.style.backgroundColor = this.backgroundColor;
+        }
+    },  
+    
+    /**
+     * Method: setOpacity
+     * Sets the opacity of the popup.
      * 
      * Parameters:
-     * moPixel - {Object} MapObject Pixel format
-     * 
-     * Returns:
-     * {Object} MapObject LonLat translated from MapObject Pixel
+     * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
      */
-    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
-        //the conditional here is to test if we are running the v6 of VE
-        return (typeof VEPixel != 'undefined') 
-            ? this.mapObject.PixelToLatLong(moPixel)
-            : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
+    setOpacity:function(opacity) { 
+        if (opacity != undefined) {
+            this.opacity = opacity; 
+        }
+        
+        if (this.div != null) {
+            // for Mozilla and Safari
+            this.div.style.opacity = this.opacity;
+
+            // for IE
+            this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
+        }
+    },  
+    
+    /**
+     * Method: setBorder
+     * Sets the border style of the popup.
+     *
+     * Parameters:
+     * border - {String} The border style value. eg 2px 
+     */
+    setBorder:function(border) { 
+        if (border != undefined) {
+            this.border = border;
+        }
+        
+        if (this.div != null) {
+            this.div.style.border = this.border;
+        }
+    },      
+    
+    /**
+     * Method: setContentHTML
+     * Allows the user to set the HTML content of the popup.
+     *
+     * Parameters:
+     * contentHTML - {String} HTML for the div.
+     */
+    setContentHTML:function(contentHTML) {
+
+        if (contentHTML != null) {
+            this.contentHTML = contentHTML;
+        }
+       
+        if ((this.contentDiv != null) && 
+            (this.contentHTML != null) &&
+            (this.contentHTML != this.contentDiv.innerHTML)) {
+       
+            this.contentDiv.innerHTML = this.contentHTML;
+       
+            if (this.autoSize) {
+                
+                //if popup has images, listen for when they finish
+                // loading and resize accordingly
+                this.registerImageListeners();
+
+                //auto size the popup to its current contents
+                this.updateSize();
+            }
+        }    
+
     },
+    
+    /**
+     * Method: registerImageListeners
+     * Called when an image contained by the popup loaded. this function
+     *     updates the popup size, then unregisters the image load listener.
+     */   
+    registerImageListeners: function() { 
 
+        // As the images load, this function will call updateSize() to 
+        // resize the popup to fit the content div (which presumably is now
+        // bigger than when the image was not loaded).
+        // 
+        // If the 'panMapIfOutOfView' property is set, we will pan the newly
+        // resized popup back into view.
+        // 
+        // Note that this function, when called, will have 'popup' and 
+        // 'img' properties in the context.
+        //
+        var onImgLoad = function() {
+            
+            this.popup.updateSize();
+     
+            if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
+                this.popup.panIntoView();
+            }
+
+            OpenLayers.Event.stopObserving(
+                this.img, "load", this.img._onImageLoad
+            );
+    
+        };
+
+        //cycle through the images and if their size is 0x0, that means that 
+        // they haven't been loaded yet, so we attach the listener, which 
+        // will fire when the images finish loading and will resize the 
+        // popup accordingly to its new size.
+        var images = this.contentDiv.getElementsByTagName("img");
+        for (var i = 0, len = images.length; i < len; i++) {
+            var img = images[i];
+            if (img.width == 0 || img.height == 0) {
+
+                var context = {
+                    'popup': this,
+                    'img': img
+                };
+
+                //expando this function to the image itself before registering
+                // it. This way we can easily and properly unregister it.
+                img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
+
+                OpenLayers.Event.observe(img, 'load', img._onImgLoad);
+            }    
+        } 
+    },
+
     /**
-     * APIMethod: getMapObjectPixelFromMapObjectLonLat
+     * APIMethod: getSafeContentSize
      * 
      * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
+     * size - {<OpenLayers.Size>} Desired size to make the popup.
      * 
      * Returns:
-     * {Object} MapObject Pixel transtlated from MapObject LonLat
+     * {<OpenLayers.Size>} A size to make the popup which is neither smaller
+     *     than the specified minimum size, nor bigger than the maximum 
+     *     size (which is calculated relative to the size of the viewport).
      */
-    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
-        return this.mapObject.LatLongToPixel(moLonLat);
-    },
+    getSafeContentSize: function(size) {
 
+        var safeContentSize = size.clone();
 
-    /************************************
-     *                                  *
-     *       MapObject Primitives       *
-     *                                  *
-     ************************************/
+        // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
+        //  must add that to the desired "size". 
+        var contentDivPadding = this.getContentDivPadding();
+        var wPadding = contentDivPadding.left + contentDivPadding.right;
+        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
 
+        // take into account the popup's 'padding' property
+        this.fixPadding();
+        wPadding += this.padding.left + this.padding.right;
+        hPadding += this.padding.top + this.padding.bottom;
 
-  // LonLat
+        if (this.closeDiv) {
+            var closeDivWidth = parseInt(this.closeDiv.style.width);
+            wPadding += closeDivWidth + contentDivPadding.right;
+        }
+
+        // prevent the popup from being smaller than a specified minimal size
+        if (this.minSize) {
+            safeContentSize.w = Math.max(safeContentSize.w, 
+                (this.minSize.w - wPadding));
+            safeContentSize.h = Math.max(safeContentSize.h, 
+                (this.minSize.h - hPadding));
+        }
+
+        // prevent the popup from being bigger than a specified maximum size
+        if (this.maxSize) {
+            safeContentSize.w = Math.min(safeContentSize.w, 
+                (this.maxSize.w - wPadding));
+            safeContentSize.h = Math.min(safeContentSize.h, 
+                (this.maxSize.h - hPadding));
+        }
+        
+        //make sure the desired size to set doesn't result in a popup that 
+        // is bigger than the map's viewport.
+        //
+        if (this.map && this.map.size) {
+            
+            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 - extraY;
+            
+            var maxX = this.map.size.w - 
+                this.map.paddingForPopups.left - 
+                this.map.paddingForPopups.right - 
+                wPadding - extraX;
+            
+            safeContentSize.w = Math.min(safeContentSize.w, maxX);
+            safeContentSize.h = Math.min(safeContentSize.h, maxY);
+        }
+        
+        return safeContentSize;
+    },
     
     /**
-     * APIMethod: getLongitudeFromMapObjectLonLat
-     * 
-     * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
-     * 
+     * Method: getContentDivPadding
+     * Glorious, oh glorious hack in order to determine the css 'padding' of 
+     *     the contentDiv. IE/Opera return null here unless we actually add the 
+     *     popup's main 'div' element (which contains contentDiv) to the DOM. 
+     *     So we make it invisible and then add it to the document temporarily. 
+     *
+     *     Once we've taken the padding readings we need, we then remove it 
+     *     from the DOM (it will actually get added to the DOM in 
+     *     Map.js's addPopup)
+     *
      * Returns:
-     * {Float} Longitude of the given MapObject LonLat
+     * {<OpenLayers.Bounds>}
      */
-    getLongitudeFromMapObjectLonLat: function(moLonLat) {
-        return this.sphericalMercator ? 
-            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
-            moLonLat.Longitude;
+    getContentDivPadding: function() {
+
+        //use cached value if we have it
+        var contentDivPadding = this._contentDivPadding;
+        if (!contentDivPadding) {
+
+        	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"),
+                OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
+                OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
+                OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
+            );
+    
+            //cache the value
+            this._contentDivPadding = contentDivPadding;
+
+            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;
     },
 
     /**
-     * APIMethod: getLatitudeFromMapObjectLonLat
+     * Method: addCloseBox
      * 
      * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
-     * 
-     * Returns:
-     * {Float} Latitude of the given MapObject LonLat
+     * callback - {Function} The callback to be called when the close button
+     *     is clicked.
      */
-    getLatitudeFromMapObjectLonLat: function(moLonLat) {
-        return this.sphericalMercator ? 
-            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
-            moLonLat.Latitude;
+    addCloseBox: function(callback) {
+
+        this.closeDiv = OpenLayers.Util.createDiv(
+            this.id + "_close", null, new OpenLayers.Size(17, 17)
+        );
+        this.closeDiv.className = "olPopupCloseBox"; 
+        
+        // use the content div's css padding to determine if we should
+        //  padd the close div
+        var contentDivPadding = this.getContentDivPadding();
+         
+        this.closeDiv.style.right = contentDivPadding.right + "px";
+        this.closeDiv.style.top = contentDivPadding.top + "px";
+        this.groupDiv.appendChild(this.closeDiv);
+
+        var closePopup = callback || function(e) {
+            this.hide();
+            OpenLayers.Event.stop(e);
+        };
+        OpenLayers.Event.observe(this.closeDiv, "click", 
+                OpenLayers.Function.bindAsEventListener(closePopup, this));
     },
 
     /**
-     * APIMethod: getMapObjectLonLatFromLonLat
+     * Method: panIntoView
+     * Pans the map such that the popup is totaly viewable (if necessary)
+     */
+    panIntoView: function() {
+        
+        var mapSize = this.map.getSize();
+    
+        //start with the top left corner of the popup, in px, 
+        // relative to the viewport
+        var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
+            parseInt(this.div.style.left),
+            parseInt(this.div.style.top)
+        ));
+        var newTL = origTL.clone();
+    
+        //new left (compare to margins, using this.size to calculate right)
+        if (origTL.x < this.map.paddingForPopups.left) {
+            newTL.x = this.map.paddingForPopups.left;
+        } else 
+        if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
+            newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
+        }
+        
+        //new top (compare to margins, using this.size to calculate bottom)
+        if (origTL.y < this.map.paddingForPopups.top) {
+            newTL.y = this.map.paddingForPopups.top;
+        } else 
+        if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
+            newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
+        }
+        
+        var dx = origTL.x - newTL.x;
+        var dy = origTL.y - newTL.y;
+        
+        this.map.pan(dx, dy);
+    },
+
+    /** 
+     * Method: registerEvents
+     * Registers events on the popup.
+     *
+     * Do this in a separate function so that subclasses can 
+     *   choose to override it if they wish to deal differently
+     *   with mouse events
      * 
+     *   Note in the following handler functions that some special
+     *    care is needed to deal correctly with mousing and popups. 
+     *   
+     *   Because the user might select the zoom-rectangle option and
+     *    then drag it over a popup, we need a safe way to allow the
+     *    mousemove and mouseup events to pass through the popup when
+     *    they are initiated from outside.
+     * 
+     *   Otherwise, we want to essentially kill the event propagation
+     *    for all other events, though we have to do so carefully, 
+     *    without disabling basic html functionality, like clicking on 
+     *    hyperlinks or drag-selecting text.
+     */
+     registerEvents:function() {
+        this.events = new OpenLayers.Events(this, this.div, null, true);
+
+        this.events.on({
+            "mousedown": this.onmousedown,
+            "mousemove": this.onmousemove,
+            "mouseup": this.onmouseup,
+            "click": this.onclick,
+            "mouseout": this.onmouseout,
+            "dblclick": this.ondblclick,
+            scope: this
+        });
+        
+     },
+
+    /** 
+     * Method: onmousedown 
+     * When mouse goes down within the popup, make a note of
+     *   it locally, and then do not propagate the mousedown 
+     *   (but do so safely so that user can select text inside)
+     * 
      * Parameters:
-     * lon - {Float}
-     * lat - {Float}
+     * evt - {Event} 
+     */
+    onmousedown: function (evt) {
+        this.mousedown = true;
+        OpenLayers.Event.stop(evt, true);
+    },
+
+    /** 
+     * Method: onmousemove
+     * If the drag was started within the popup, then 
+     *   do not propagate the mousemove (but do so safely
+     *   so that user can select text inside)
      * 
-     * Returns:
-     * {Object} MapObject LonLat built from lon and lat params
+     * Parameters:
+     * evt - {Event} 
      */
-    getMapObjectLonLatFromLonLat: function(lon, lat) {
-        var veLatLong;
-        if(this.sphericalMercator) {
-            var lonlat = this.inverseMercator(lon, lat);
-            veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
-        } else {
-            veLatLong = new VELatLong(lat, lon);
+    onmousemove: function (evt) {
+        if (this.mousedown) {
+            OpenLayers.Event.stop(evt, true);
         }
-        return veLatLong;
     },
 
-  // Pixel
-    
-    /**
-     * APIMethod: getXFromMapObjectPixel
+    /** 
+     * Method: onmouseup
+     * When mouse comes up within the popup, after going down 
+     *   in it, reset the flag, and then (once again) do not 
+     *   propagate the event, but do so safely so that user can 
+     *   select text inside
      * 
      * Parameters:
-     * moPixel - {Object} MapObject Pixel format
-     * 
-     * Returns:
-     * {Integer} X value of the MapObject Pixel
+     * evt - {Event} 
      */
-    getXFromMapObjectPixel: function(moPixel) {
-        return moPixel.x;
+    onmouseup: function (evt) {
+        if (this.mousedown) {
+            this.mousedown = false;
+            OpenLayers.Event.stop(evt, true);
+        }
     },
 
     /**
-     * APIMethod: getYFromMapObjectPixel
+     * Method: onclick
+     * Ignore clicks, but allowing default browser handling
      * 
      * Parameters:
-     * moPixel - {Object} MapObject Pixel format
-     * 
-     * Returns:
-     * {Integer} Y value of the MapObject Pixel
+     * evt - {Event} 
      */
-    getYFromMapObjectPixel: function(moPixel) {
-        return moPixel.y;
+    onclick: function (evt) {
+        OpenLayers.Event.stop(evt, true);
     },
 
-    /**
-     * APIMethod: getMapObjectPixelFromXY
+    /** 
+     * Method: onmouseout
+     * When mouse goes out of the popup set the flag to false so that
+     *   if they let go and then drag back in, we won't be confused.
      * 
      * Parameters:
-     * x - {Integer}
-     * y - {Integer}
+     * evt - {Event} 
+     */
+    onmouseout: function (evt) {
+        this.mousedown = false;
+    },
+    
+    /** 
+     * Method: ondblclick
+     * Ignore double-clicks, but allowing default browser handling
      * 
-     * Returns:
-     * {Object} MapObject Pixel from x and y parameters
+     * Parameters:
+     * evt - {Event} 
      */
-    getMapObjectPixelFromXY: function(x, y) {
-        //the conditional here is to test if we are running the v6 of VE
-        return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
-                         : new Msn.VE.Pixel(x, y);
+    ondblclick: function (evt) {
+        OpenLayers.Event.stop(evt, true);
     },
 
-    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
+    CLASS_NAME: "OpenLayers.Popup"
 });
+
+OpenLayers.Popup.WIDTH = 200;
+OpenLayers.Popup.HEIGHT = 200;
+OpenLayers.Popup.COLOR = "white";
+OpenLayers.Popup.OPACITY = 1;
+OpenLayers.Popup.BORDER = "0px";
 /* ======================================================================
-    OpenLayers/Layer/Yahoo.js
+    OpenLayers/Popup/Anchored.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 
 /**
- * @requires OpenLayers/Layer/SphericalMercator.js
- * @requires OpenLayers/Layer/EventPane.js
- * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/Popup.js
  */
 
 /**
- * Class: OpenLayers.Layer.Yahoo
+ * Class: OpenLayers.Popup.Anchored
  * 
  * Inherits from:
- *  - <OpenLayers.Layer.EventPane>
- *  - <OpenLayers.Layer.FixedZoomLevels>
+ *  - <OpenLayers.Popup>
  */
-OpenLayers.Layer.Yahoo = OpenLayers.Class(
-  OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
-    
+OpenLayers.Popup.Anchored = 
+  OpenLayers.Class(OpenLayers.Popup, {
+
     /** 
-     * Constant: MIN_ZOOM_LEVEL
-     * {Integer} 0 
+     * Parameter: relativePosition
+     * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
      */
-    MIN_ZOOM_LEVEL: 0,
+    relativePosition: null,
     
-    /** 
-     * Constant: MAX_ZOOM_LEVEL
-     * {Integer} 17
+    /**
+     * 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.
      */
-    MAX_ZOOM_LEVEL: 17,
+    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,
+
     /** 
-     * Constant: RESOLUTIONS
-     * {Array(Float)} Hardcode these resolutions so that they are more closely
-     *                tied with the standard wms projection
+    * 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
      */
-    RESOLUTIONS: [
-        1.40625, 
-        0.703125, 
-        0.3515625, 
-        0.17578125, 
-        0.087890625, 
-        0.0439453125,
-        0.02197265625, 
-        0.010986328125, 
-        0.0054931640625, 
-        0.00274658203125, 
-        0.001373291015625, 
-        0.0006866455078125, 
-        0.00034332275390625, 
-        0.000171661376953125, 
-        0.0000858306884765625, 
-        0.00004291534423828125,
-        0.00002145767211914062,
-        0.00001072883605957031
-    ],
+    destroy: function() {
+        this.anchor = null;
+        this.relativePosition = null;
+        
+        OpenLayers.Popup.prototype.destroy.apply(this, arguments);        
+    },
 
     /**
-     * APIProperty: type
-     * {YahooMapType}
+     * 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)
      */
-    type: null,
-    
+    show: function() {
+        this.updatePosition();
+        OpenLayers.Popup.prototype.show.apply(this, arguments);
+    },
+
     /**
-     * APIProperty: wrapDateLine
-     * {Boolean} Allow user to pan forever east/west.  Default is true.  
-     *     Setting this to false only restricts panning if 
-     *     <sphericalMercator> is true. 
+     * 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>}
      */
-    wrapDateLine: true,
+    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();
+        }
+    },
 
     /**
-     * APIProperty: sphericalMercator
-     * {Boolean} Should the map act as a mercator-projected map? This will
-     * cause all interactions with the map to be in the actual map projection,
-     * which allows support for vector drawing, overlaying other maps, etc. 
+     * APIMethod: setSize
+     * 
+     * Parameters:
+     * contentSize - {<OpenLayers.Size>} the new size for the popup's 
+     *     contents div (in pixels).
      */
-    sphericalMercator: false, 
+    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);
+        }
+    },  
+    
     /** 
-     * Constructor: OpenLayers.Layer.Yahoo
+     * Method: calculateRelativePosition
      * 
      * Parameters:
-     * name - {String}
-     * options - {Object}
+     * px - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
+     *     should be placed.
      */
-    initialize: function(name, options) {
-        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
-        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
-                                                                    arguments);
-        if(this.sphericalMercator) {
-            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
-            this.initMercatorParameters();
-        }
-    },
-    
+    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: loadMapObject
+     * 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.
      */
-    loadMapObject:function() {
-        try { //do not crash! 
-            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
-            this.mapObject = new YMap(this.div, this.type, size);
-            this.mapObject.disableKeyControls();
-            this.mapObject.disableDragMap();
+    updateRelativePosition: function() {
+        //to be overridden by subclasses
+    },
 
-            //can we do smooth panning? (moveByXY is not an API function)
-            if ( !this.mapObject.moveByXY || 
-                 (typeof this.mapObject.moveByXY != "function" ) ) {
+    /** 
+     * 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;
 
-                this.dragPanMapObject = null;
-            }                
-        } catch(e) {}
+        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"
+});
+/* ======================================================================
+    Rico/Color.js
+   ====================================================================== */
+
+/** 
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+
+/*
+ * This file has been edited substantially from the Rico-released version by
+ * the OpenLayers development team.
+ *
+ * This file is licensed under the Apache License, Version 2.0.
+ */
+OpenLayers.Rico = OpenLayers.Rico || {};
+OpenLayers.Rico.Color = OpenLayers.Class({
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = OpenLayers.Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+});
+
+OpenLayers.Rico.Color.createFromHex = function(hexCode) {
+  if(hexCode.length==4) {
+    var shortHexCode = hexCode; 
+    var hexCode = '#';
+    for(var i=1;i<4;i++) { 
+        hexCode += (shortHexCode.charAt(i) + 
+shortHexCode.charAt(i)); 
+    }
+  }
+   if ( hexCode.indexOf('#') == 0 ) {
+       hexCode = hexCode.substring(1);
+   }
+   var red   = hexCode.substring(0,2);
+   var green = hexCode.substring(2,4);
+   var blue  = hexCode.substring(4,6);
+   return new OpenLayers.Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+};
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+OpenLayers.Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = 
+      RicoUtil.getElementsComputedStyle(OpenLayers.Util.getElement(elem), 
+                                        "backgroundColor", 
+                                        "background-color");
+
+   if ( actualColor == "transparent" && elem.parentNode ) {
+      return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);
+   }
+   if ( actualColor == null ) {
+      return new OpenLayers.Rico.Color(255,255,255);
+   }
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new OpenLayers.Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      return OpenLayers.Rico.Color.createFromHex(actualColor);
+   }
+   else {
+      return new OpenLayers.Rico.Color(255,255,255);
+   }
+};
+
+OpenLayers.Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+    var green = 0;
+    var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+       green = red;
+       blue = red;
+    }
+    else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+        }
+    }
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+};
+
+OpenLayers.Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturation;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax) {
+      cmax = b;
+   }
+   var cmin = (r < g) ? r : g;
+   if (b < cmin) {
+      cmin = b;
+   }
+   brightness = cmax / 255.0;
+   if (cmax != 0) {
+      saturation = (cmax - cmin)/cmax;
+   } else {
+      saturation = 0;
+   }
+   if (saturation == 0) {
+      hue = 0;
+   } else {
+      var redc   = (cmax - r)/(cmax - cmin);
+        var greenc = (cmax - g)/(cmax - cmin);
+        var bluec  = (cmax - b)/(cmax - cmin);
+
+        if (r == cmax) {
+           hue = bluec - greenc;
+        } else if (g == cmax) {
+           hue = 2.0 + redc - bluec;
+      } else {
+           hue = 4.0 + greenc - redc;
+      }
+        hue = hue / 6.0;
+        if (hue < 0) {
+           hue = hue + 1.0;
+        }
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+};
+
+/* ======================================================================
+    Rico/Corner.js
+   ====================================================================== */
+
+/**
+ * @requires Rico/Color.js
+ */
+
+
+/*
+ * This file has been edited substantially from the Rico-released
+ * version by the OpenLayers development team.
+ *  
+ *  Copyright 2005 Sabre Airline Solutions  
+ *  
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the
+ *  License. You may obtain a copy of the License at
+ *  
+ *         http://www.apache.org/licenses/LICENSE-2.0  
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the * License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or
+ * implied. See the License for the specific language governing
+ * permissions * and limitations under the License.
+ *
+ */
+OpenLayers.Rico = OpenLayers.Rico || {};
+OpenLayers.Rico.Corner = {
+
+    round: function(e, options) {
+        e = OpenLayers.Util.getElement(e);
+        this._setOptions(options);
+
+        var color = this.options.color;
+        if ( this.options.color == "fromElement" ) {
+            color = this._background(e);
+        }
+        var bgColor = this.options.bgColor;
+        if ( this.options.bgColor == "fromParent" ) {
+            bgColor = this._background(e.offsetParent);
+        }
+        this._roundCornersImpl(e, color, bgColor);
+    },
+
+    /**   This is a helper function to change the background
+    *     color of <div> that has had Rico rounded corners added.
+    *
+    *     It seems we cannot just set the background color for the
+    *     outer <div> so each <span> element used to create the
+    *     corners must have its background color set individually.
+    *
+    * @param {DOM} theDiv - A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {String} newColor - The new background color to use.
+    */
+    changeColor: function(theDiv, newColor) {
+   
+        theDiv.style.backgroundColor = newColor;
+
+        var spanElements = theDiv.parentNode.getElementsByTagName("span");
+        
+        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+            spanElements[currIdx].style.backgroundColor = newColor;
+        }
+    }, 
+
+
+    /**   This is a helper function to change the background
+    *     opacity of <div> that has had Rico rounded corners added.
+    *
+    *     See changeColor (above) for algorithm explanation
+    *
+    * @param {DOM} theDiv A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {int} newOpacity The new opacity to use (0-1).
+    */
+    changeOpacity: function(theDiv, newOpacity) {
+   
+        var mozillaOpacity = newOpacity;
+        var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
+        
+        theDiv.style.opacity = mozillaOpacity;
+        theDiv.style.filter = ieOpacity;
+
+        var spanElements = theDiv.parentNode.getElementsByTagName("span");
+        
+        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
+            spanElements[currIdx].style.opacity = mozillaOpacity;
+            spanElements[currIdx].style.filter = ieOpacity;
+        }
+
+    },
+
+    /** this function takes care of redoing the rico cornering
+    *    
+    *    you can't just call updateRicoCorners() again and pass it a 
+    *    new options string. you have to first remove the divs that 
+    *    rico puts on top and below the content div.
+    *
+    * @param {DOM} theDiv - A child of the outer <div> that was
+    *                        supplied to the `round` method.
+    *
+    * @param {Object} options - list of options
+    */
+    reRound: function(theDiv, options) {
+
+        var topRico = theDiv.parentNode.childNodes[0];
+        //theDiv would be theDiv.parentNode.childNodes[1]
+        var bottomRico = theDiv.parentNode.childNodes[2];
+       
+        theDiv.parentNode.removeChild(topRico);
+        theDiv.parentNode.removeChild(bottomRico); 
+
+        this.round(theDiv.parentNode, options);
+    }, 
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border) {
+         this._renderBorder(e,bgColor);
+      }
+      if(this._isTopRounded()) {
+         this._roundTopCorners(e,color,bgColor);
+      }
+      if(this._isBottomRounded()) {
+         this._roundBottomCorners(e,color,bgColor);
+      }
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ ) {
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      }
+      el.style.paddingTop = 0;
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) {
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      }
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) ) {
+         inStyle.height = "2px";
+      }
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      };
+      OpenLayers.Util.extend(this.options, options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() ) {
+         this.options.blend = false;
+      }
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") ) {
+         return "";
+      }
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) {
+         return "";
+      }
+      if (this.options.corners.indexOf("tl") >= 0) {
+         return "left";
+      } else if (this.options.corners.indexOf("tr") >= 0) {
+          return "right";
+      }
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") ) {
+         return "";
+      }
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) {
+         return "";
+      }
+
+      if(this.options.corners.indexOf("bl") >=0) {
+         return "left";
+      } else if(this.options.corners.indexOf("br")>=0) {
+         return "right";
+      }
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" ) {
+         return bgColor;
+      } else if ( this.options.border ) {
+         return this.options.border;
+      } else if ( this.options.blend ) {
+         return this._blend( bgColor, color );
+      } else {
+         return "";
+      }
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+      if (this.options.border != false) {
+        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() ) {
+         return 0;
+      }
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend ) {
+         return smBlendedMarginSizes[n];
+      } else if ( this.options.compact ) {
+         return compactMarginSizes[n];
+      } else if ( this.options.blend ) {
+         return blendedMarginSizes[n];
+      } else {
+         return marginSizes[n];
+      }
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) ) {
+         return 1;
+      } else if ( this.options.compact ) {
+         return compactBorderSizes[n];
+      } else if ( this.options.blend ) {
+         return blendedBorderSizes[n];
+      } else if ( this.options.border ) {
+         return actualBorderSizes[n];
+      } else if ( this._isTransparent() ) {
+         return transparentBorderSizes[n];
+      }
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) { return true; } return false; },
+   _blend: function(c1, c2) { var cc1 = OpenLayers.Rico.Color.createFromHex(c1); cc1.blend(OpenLayers.Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return OpenLayers.Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+};
+/* ======================================================================
+    OpenLayers/Popup/AnchoredBubble.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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 Rico/Corner.js
+ */
+
+/**
+ * Class: OpenLayers.Popup.AnchoredBubble
+ * 
+ * Inherits from: 
+ *  - <OpenLayers.Popup.Anchored>
+ */
+OpenLayers.Popup.AnchoredBubble = 
+  OpenLayers.Class(OpenLayers.Popup.Anchored, {
+
     /**
-     * Method: onMapResize
-     * 
+     * Property: rounded
+     * {Boolean} Has the popup been rounded yet?
      */
-    onMapResize: function() {
-        try {
-            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
-            this.mapObject.resizeTo(size);
-        } catch(e) {}     
-    },    
+    rounded: false, 
     
-    
     /** 
-     * APIMethod: setMap
-     * Overridden from EventPane because we need to remove this yahoo event
-     *     pane which prohibits our drag and drop, and we can only do this 
-     *     once the map has been loaded and centered.
+     * Constructor: OpenLayers.Popup.AnchoredBubble
      * 
      * Parameters:
-     * map - {<OpenLayers.Map>}
+     * 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.
      */
-    setMap: function(map) {
-        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
-
-        this.map.events.register("moveend", this, this.fixYahooEventPane);
+    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: fixYahooEventPane
-     * The map has been centered, so the mysterious yahoo eventpane has been
-     *     added. we remove it so that it doesnt mess with *our* event pane.
+     * Method: draw
+     * 
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {DOMElement} Reference to a div that contains the drawn popup.
      */
-    fixYahooEventPane: function() {
-        var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
-        if (yahooEventPane != null) {
-            if (yahooEventPane.parentNode != null) {
-                yahooEventPane.parentNode.removeChild(yahooEventPane);
-            }
-            this.map.events.unregister("moveend", this, 
-                                       this.fixYahooEventPane);
-        }
+    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;
     },
 
-    /** 
-     * APIMethod: getWarningHTML
-     * 
-     * Returns: 
-     * {String} String with information on why layer is broken, how to get
-     *          it working.
+    /**
+     * Method: updateRelativePosition
+     * The popup has been moved to a new relative location, in which case
+     *     we will want to re-do the rico corners.
      */
-    getWarningHTML:function() {
-        return OpenLayers.i18n(
-            "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
-        );
+    updateRelativePosition: function() {
+        this.setRicoCorners();
     },
 
-  /********************************************************/
-  /*                                                      */
-  /*             Translation Functions                    */
-  /*                                                      */
-  /*    The following functions translate GMaps and OL    */ 
-  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
-  /*                                                      */
-  /********************************************************/
+    /**
+     * 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();
+    },  
 
-  //
-  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
-  //
-  
     /**
-     * APIMethod: getOLZoomFromMapObjectZoom
+     * APIMethod: setBackgroundColor
      * 
      * Parameters:
-     * gZoom - {Integer}
-     * 
-     * Returns:
-     * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
-     *           Returns null if null value is passed in.
+     * color - {String}
      */
-    getOLZoomFromMapObjectZoom: function(moZoom) {
-        var zoom = null;
-        if (moZoom != null) {
-            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
-            zoom = 18 - zoom;
+    setBackgroundColor:function(color) { 
+        if (color != undefined) {
+            this.backgroundColor = color; 
         }
-        return zoom;
-    },
+        
+        if (this.div != null) {
+            if (this.contentDiv != null) {
+                this.div.style.background = "transparent";
+                OpenLayers.Rico.Corner.changeColor(this.groupDiv, 
+                                                   this.backgroundColor);
+            }
+        }
+    },  
     
     /**
-     * APIMethod: getMapObjectZoomFromOLZoom
+     * 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: setBorder
+     * Always sets border to 0. Bubble Popups can not have a border.
+     * 
      * Parameters:
-     * olZoom - {Integer}
-     * 
-     * Returns:
-     * {Integer} A MapObject level, translated from the passed in olZoom
-     *           Returns null if null value is passed in
+     * border - {Integer}
      */
-    getMapObjectZoomFromOLZoom: function(olZoom) {
-        var zoom = null; 
-        if (olZoom != null) {
-            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
-            zoom = 18 - zoom;
+    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};
+
+        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();
         }
-        return zoom;
     },
 
-    /************************************
-     *                                  *
-     *   MapObject Interface Controls   *
-     *                                  *
-     ************************************/
+    /** 
+     * Method: getCornersToRound
+     *  
+     * Returns:
+     * {String} The proper corners string ("tr tl bl br") for rico to round.
+     */
+    getCornersToRound:function() {
 
+        var corners = ['tl', 'tr', 'bl', 'br'];
 
-  // Get&Set Center, Zoom
+        //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/Feature.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Marker.js
+ * @requires OpenLayers/Popup/AnchoredBubble.js
+ */
+
+/**
+ * Class: OpenLayers.Feature
+ * Features are combinations of geography and attributes. The OpenLayers.Feature
+ *     class specifically combines a marker and a lonlat.
+ */
+OpenLayers.Feature = OpenLayers.Class({
+
     /** 
-     * APIMethod: setMapObjectCenter
-     * Set the mapObject to the specified center and zoom
-     * 
-     * Parameters:
-     * center - {Object} MapObject LonLat format
-     * zoom - {int} MapObject zoom format
+     * Property: layer 
+     * {<OpenLayers.Layer>} 
      */
-    setMapObjectCenter: function(center, zoom) {
-        this.mapObject.drawZoomAndCenter(center, zoom); 
-    },
-   
+    layer: null,
+
+    /** 
+     * Property: id 
+     * {String} 
+     */
+    id: null,
+    
+    /** 
+     * Property: lonlat 
+     * {<OpenLayers.LonLat>} 
+     */
+    lonlat: null,
+
+    /** 
+     * Property: data 
+     * {Object} 
+     */
+    data: null,
+
+    /** 
+     * Property: marker 
+     * {<OpenLayers.Marker>} 
+     */
+    marker: null,
+
     /**
-     * APIMethod: getMapObjectCenter
+     * APIProperty: popupClass
+     * {<OpenLayers.Class>} The class which will be used to instantiate
+     *     a new Popup. Default is <OpenLayers.Popup.AnchoredBubble>.
+     */
+    popupClass: OpenLayers.Popup.AnchoredBubble,
+
+    /** 
+     * Property: popup 
+     * {<OpenLayers.Popup>} 
+     */
+    popup: null,
+
+    /** 
+     * Constructor: OpenLayers.Feature
+     * Constructor for features.
+     *
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} 
+     * lonlat - {<OpenLayers.LonLat>} 
+     * data - {Object} 
      * 
-     * Returns: 
-     * {Object} The mapObject's current center in Map Object format
+     * Returns:
+     * {<OpenLayers.Feature>}
      */
-    getMapObjectCenter: function() {
-        return this.mapObject.getCenterLatLon();
+    initialize: function(layer, lonlat, data) {
+        this.layer = layer;
+        this.lonlat = lonlat;
+        this.data = (data != null) ? data : {};
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); 
     },
 
+    /** 
+     * Method: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+
+        //remove the popup from the map
+        if ((this.layer != null) && (this.layer.map != null)) {
+            if (this.popup != null) {
+                this.layer.map.removePopup(this.popup);
+            }
+        }
+        // remove the marker from the layer
+        if (this.layer != null && this.marker != null) {
+            this.layer.removeMarker(this.marker);
+        }
+
+        this.layer = null;
+        this.id = null;
+        this.lonlat = null;
+        this.data = null;
+        if (this.marker != null) {
+            this.destroyMarker(this.marker);
+            this.marker = null;
+        }
+        if (this.popup != null) {
+            this.destroyPopup(this.popup);
+            this.popup = null;
+        }
+    },
+    
     /**
-     * APIMethod: dragPanMapObject
+     * Method: onScreen
      * 
-     * Parameters:
-     * dX - {Integer}
-     * dY - {Integer}
+     * Returns:
+     * {Boolean} Whether or not the feature is currently visible on screen
+     *           (based on its 'lonlat' property)
      */
-    dragPanMapObject: function(dX, dY) {
-        this.mapObject.moveByXY({
-            'x': -dX,
-            'y': dY
-        });
+    onScreen:function() {
+        
+        var onScreen = false;
+        if ((this.layer != null) && (this.layer.map != null)) {
+            var screenBounds = this.layer.map.getExtent();
+            onScreen = screenBounds.containsLonLat(this.lonlat);
+        }    
+        return onScreen;
     },
     
-    /** 
-     * APIMethod: getMapObjectZoom
+
+    /**
+     * Method: createMarker
+     * Based on the data associated with the Feature, create and return a marker object.
+     *
+     * Returns: 
+     * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
+     *          set in this.data. If no 'lonlat' is set, returns null. If no
+     *          'icon' is set, OpenLayers.Marker() will load the default image.
+     *          
+     *          Note - this.marker is set to return value
      * 
-     * Returns:
-     * {Integer} The mapObject's current zoom, in Map Object format
      */
-    getMapObjectZoom: function() {
-        return this.mapObject.getZoomLevel();
+    createMarker: function() {
+
+        if (this.lonlat != null) {
+            this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
+        }
+        return this.marker;
     },
 
+    /**
+     * Method: destroyMarker
+     * Destroys marker.
+     * If user overrides the createMarker() function, s/he should be able
+     *   to also specify an alternative function for destroying it
+     */
+    destroyMarker: function() {
+        this.marker.destroy();  
+    },
 
-  // LonLat - Pixel Translation
-  
     /**
-     * APIMethod: getMapObjectLonLatFromMapObjectPixel
+     * Method: createPopup
+     * Creates a popup object created from the 'lonlat', 'popupSize',
+     *     and 'popupContentHTML' properties set in this.data. It uses
+     *     this.marker.icon as default anchor. 
+     *  
+     *  If no 'lonlat' is set, returns null. 
+     *  If no this.marker has been created, no anchor is sent.
+     *
+     *  Note - the returned popup object is 'owned' by the feature, so you
+     *      cannot use the popup's destroy method to discard the popup.
+     *      Instead, you must use the feature's destroyPopup
      * 
-     * Parameters:
-     * moPixel - {Object} MapObject Pixel format
+     *  Note - this.popup is set to return value
      * 
+     * Parameters: 
+     * closeBox - {Boolean} create popup with closebox or not
+     * 
      * Returns:
-     * {Object} MapObject LonLat translated from MapObject Pixel
+     * {<OpenLayers.Popup>} Returns the created popup, which is also set
+     *     as 'popup' property of this feature. Will be of whatever type
+     *     specified by this feature's 'popupClass' property, but must be
+     *     of type <OpenLayers.Popup>.
+     * 
      */
-    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
-        return this.mapObject.convertXYLatLon(moPixel);
+    createPopup: function(closeBox) {
+
+        if (this.lonlat != null) {
+            
+            var id = this.id + "_popup";
+            var anchor = (this.marker) ? this.marker.icon : null;
+
+            if (!this.popup) {
+                this.popup = new this.popupClass(id, 
+                                                 this.lonlat,
+                                                 this.data.popupSize,
+                                                 this.data.popupContentHTML,
+                                                 anchor, 
+                                                 closeBox); 
+            }    
+            if (this.data.overflow != null) {
+                this.popup.contentDiv.style.overflow = this.data.overflow;
+            }    
+            
+            this.popup.feature = this;
+        }        
+        return this.popup;
     },
 
+    
     /**
-     * APIMethod: getMapObjectPixelFromMapObjectLonLat
-     * 
-     * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
-     * 
-     * Returns:
-     * {Object} MapObject Pixel transtlated from MapObject LonLat
+     * Method: destroyPopup
+     * Destroys the popup created via createPopup.
+     *
+     * As with the marker, if user overrides the createPopup() function, s/he 
+     *   should also be able to override the destruction
      */
-    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
-        return this.mapObject.convertLatLonXY(moLonLat);
+    destroyPopup: function() {
+        if (this.popup) {
+            this.popup.feature = null;
+            this.popup.destroy();
+            this.popup = null;
+        }    
     },
 
+    CLASS_NAME: "OpenLayers.Feature"
+});
+/* ======================================================================
+    OpenLayers/Feature/Vector.js
+   ====================================================================== */
 
-    /************************************
-     *                                  *
-     *       MapObject Primitives       *
-     *                                  *
-     ************************************/
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
 
+// TRASH THIS
+OpenLayers.State = {
+    /** states */
+    UNKNOWN: 'Unknown',
+    INSERT: 'Insert',
+    UPDATE: 'Update',
+    DELETE: 'Delete'
+};
 
-  // LonLat
+/**
+ * @requires OpenLayers/Feature.js
+ * @requires OpenLayers/Util.js
+ */
+
+/**
+ * Class: OpenLayers.Feature.Vector
+ * Vector features use the OpenLayers.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object, and a 'style'
+ * property, the default values of which are defined in the 
+ * <OpenLayers.Feature.Vector.style> objects.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Feature>
+ */
+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
+
+    /** 
+     * Property: fid 
+     * {String} 
+     */
+    fid: null,
     
+    /** 
+     * APIProperty: geometry 
+     * {<OpenLayers.Geometry>} 
+     */
+    geometry: null,
+
+    /** 
+     * APIProperty: attributes 
+     * {Object} This object holds arbitrary, serializable properties that
+     *     describe the feature.
+     */
+    attributes: null,
+
     /**
-     * APIMethod: getLongitudeFromMapObjectLonLat
+     * 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} 
+     */
+    state: null,
+    
+    /** 
+     * APIProperty: style 
+     * {Object} 
+     */
+    style: null,
+
+    /**
+     * APIProperty: url
+     * {String} If this property is set it will be taken into account by
+     *     {<OpenLayers.HTTP>} when upadting or deleting the feature.
+     */
+    url: null,
+    
+    /**
+     * Property: renderIntent
+     * {String} rendering intent currently being used
+     */
+    renderIntent: "default",
+
+    /** 
+     * Constructor: OpenLayers.Feature.Vector
+     * Create a vector feature. 
      * 
      * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
-     * 
+     * geometry - {<OpenLayers.Geometry>} The geometry that this feature
+     *     represents.
+     * attributes - {Object} An optional object that will be mapped to the
+     *     <attributes> property. 
+     * style - {Object} An optional style object.
+     */
+    initialize: function(geometry, attributes, style) {
+        OpenLayers.Feature.prototype.initialize.apply(this,
+                                                      [null, null, attributes]);
+        this.lonlat = null;
+        this.geometry = geometry ? geometry : null;
+        this.state = null;
+        this.attributes = {};
+        if (attributes) {
+            this.attributes = OpenLayers.Util.extend(this.attributes,
+                                                     attributes);
+        }
+        this.style = style ? style : null; 
+    },
+    
+    /** 
+     * Method: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+        if (this.layer) {
+            this.layer.removeFeatures(this);
+            this.layer = null;
+        }
+            
+        this.geometry = null;
+        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * Method: clone
+     * Create a clone of this vector feature.  Does not set any non-standard
+     *     properties.
+     *
      * Returns:
-     * {Float} Longitude of the given MapObject LonLat
+     * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
      */
-    getLongitudeFromMapObjectLonLat: function(moLonLat) {
-        return this.sphericalMercator ? 
-            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
-            moLonLat.Lon;
+    clone: function () {
+        return new OpenLayers.Feature.Vector(
+            this.geometry ? this.geometry.clone() : null,
+            this.attributes,
+            this.style);
     },
 
     /**
-     * APIMethod: getLatitudeFromMapObjectLonLat
-     * 
+     * Method: onScreen
+     * Determine whether the feature is within the map viewport.  This method
+     *     tests for an intersection between the geometry and the viewport
+     *     bounds.  If a more effecient but less precise geometry bounds
+     *     intersection is desired, call the method with the boundsOnly
+     *     parameter true.
+     *
      * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
+     * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
+     *     the viewport bounds.  Default is false.  If false, the feature's
+     *     geometry must intersect the viewport for onScreen to return true.
      * 
      * Returns:
-     * {Float} Latitude of the given MapObject LonLat
+     * {Boolean} The feature is currently visible on screen (optionally
+     *     based on its bounds if boundsOnly is true).
      */
-    getLatitudeFromMapObjectLonLat: function(moLonLat) {
-        return this.sphericalMercator ? 
-            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
-            moLonLat.Lat;
+    onScreen:function(boundsOnly) {
+        var onScreen = false;
+        if(this.layer && this.layer.map) {
+            var screenBounds = this.layer.map.getExtent();
+            if(boundsOnly) {
+                var featureBounds = this.geometry.getBounds();
+                onScreen = screenBounds.intersectsBounds(featureBounds);
+            } else {
+                var screenPoly = screenBounds.toGeometry();
+                onScreen = screenPoly.intersects(this.geometry);
+            }
+        }    
+        return onScreen;
     },
 
     /**
-     * APIMethod: getMapObjectLonLatFromLonLat
+     * Method: getVisibility
+     * Determine whether the feature is displayed or not. It may not displayed
+     *     because:
+     *     - its style display property is set to 'none',
+     *     - it doesn't belong to any layer,
+     *     - the styleMap creates a symbolizer with display property set to 'none'
+     *          for it,
+     *     - the layer which it belongs to is not visible.
      * 
-     * Parameters:
-     * lon - {Float}
-     * lat - {Float}
-     * 
      * Returns:
-     * {Object} MapObject LonLat built from lon and lat params
+     * {Boolean} The feature is currently displayed.
      */
-    getMapObjectLonLatFromLonLat: function(lon, lat) {
-        var yLatLong;
-        if(this.sphericalMercator) {
-            var lonlat = this.inverseMercator(lon, lat);
-            yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
-        } else {
-            yLatLong = new YGeoPoint(lat, lon);
-        }
-        return yLatLong;
+    getVisibility: function() {
+        return !(this.style && this.style.display == 'none' ||
+                 !this.layer ||
+                 this.layer && this.layer.styleMap &&
+                 this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
+                 this.layer && !this.layer.getVisibility());
     },
-
-  // Pixel
     
     /**
-     * APIMethod: getXFromMapObjectPixel
+     * Method: createMarker
+     * HACK - we need to decide if all vector features should be able to
+     *     create markers
      * 
-     * Parameters:
-     * moPixel - {Object} MapObject Pixel format
-     * 
      * Returns:
-     * {Integer} X value of the MapObject Pixel
+     * {<OpenLayers.Marker>} For now just returns null
      */
-    getXFromMapObjectPixel: function(moPixel) {
-        return moPixel.x;
+    createMarker: function() {
+        return null;
     },
 
     /**
-     * APIMethod: getYFromMapObjectPixel
+     * Method: destroyMarker
+     * HACK - we need to decide if all vector features should be able to
+     *     delete markers
      * 
-     * Parameters:
-     * moPixel - {Object} MapObject Pixel format
+     * If user overrides the createMarker() function, s/he should be able
+     *   to also specify an alternative function for destroying it
+     */
+    destroyMarker: function() {
+        // pass
+    },
+
+    /**
+     * Method: createPopup
+     * HACK - we need to decide if all vector features should be able to
+     *     create popups
      * 
      * Returns:
-     * {Integer} Y value of the MapObject Pixel
+     * {<OpenLayers.Popup>} For now just returns null
      */
-    getYFromMapObjectPixel: function(moPixel) {
-        return moPixel.y;
+    createPopup: function() {
+        return null;
     },
 
     /**
-     * APIMethod: getMapObjectPixelFromXY
+     * Method: atPoint
+     * Determins whether the feature intersects with the specified location.
      * 
-     * Parameters:
-     * x - {Integer}
-     * y - {Integer}
+     * Parameters: 
+     * lonlat - {<OpenLayers.LonLat>} 
+     * toleranceLon - {float} Optional tolerance in Geometric Coords
+     * toleranceLat - {float} Optional tolerance in Geographic Coords
      * 
      * Returns:
-     * {Object} MapObject Pixel from x and y parameters
+     * {Boolean} Whether or not the feature is at the specified location
      */
-    getMapObjectPixelFromXY: function(x, y) {
-        return new YCoordPoint(x, y);
+    atPoint: function(lonlat, toleranceLon, toleranceLat) {
+        var atPoint = false;
+        if(this.geometry) {
+            atPoint = this.geometry.atPoint(lonlat, toleranceLon, 
+                                                    toleranceLat);
+        }
+        return atPoint;
     },
+
+    /**
+     * Method: destroyPopup
+     * HACK - we need to decide if all vector features should be able to
+     * delete popups
+     */
+    destroyPopup: function() {
+        // pass
+    },
+
+    /**
+     * Method: move
+     * Moves the feature and redraws it at its new location
+     *
+     * Parameters:
+     * state - {OpenLayers.LonLat or OpenLayers.Pixel} the
+     *         location to which to move the feature.
+     */
+    move: function(location) {
+
+        if(!this.layer || !this.geometry.move){
+            //do nothing if no layer or immoveable geometry
+            return undefined;
+        }
+
+        var pixel;
+        if (location.CLASS_NAME == "OpenLayers.LonLat") {
+            pixel = this.layer.getViewPortPxFromLonLat(location);
+        } else {
+            pixel = location;
+        }
+        
+        var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
+        var res = this.layer.map.getResolution();
+        this.geometry.move(res * (pixel.x - lastPixel.x),
+                           res * (lastPixel.y - pixel.y));
+        this.layer.drawFeature(this);
+        return lastPixel;
+    },
     
-  // Size
-  
     /**
-     * APIMethod: getMapObjectSizeFromOLSize
-     * 
+     * Method: toState
+     * Sets the new state
+     *
      * Parameters:
-     * olSize - {<OpenLayers.Size>}
-     * 
-     * Returns:
-     * {Object} MapObject Size from olSize parameter
+     * state - {String} 
      */
-    getMapObjectSizeFromOLSize: function(olSize) {
-        return new YSize(olSize.w, olSize.h);
+    toState: function(state) {
+        if (state == OpenLayers.State.UPDATE) {
+            switch (this.state) {
+                case OpenLayers.State.UNKNOWN:
+                case OpenLayers.State.DELETE:
+                    this.state = state;
+                    break;
+                case OpenLayers.State.UPDATE:
+                case OpenLayers.State.INSERT:
+                    break;
+            }
+        } else if (state == OpenLayers.State.INSERT) {
+            switch (this.state) {
+                case OpenLayers.State.UNKNOWN:
+                    break;
+                default:
+                    this.state = state;
+                    break;
+            }
+        } else if (state == OpenLayers.State.DELETE) {
+            switch (this.state) {
+                case OpenLayers.State.INSERT:
+                    // the feature should be destroyed
+                    break;
+                case OpenLayers.State.DELETE:
+                    break;
+                case OpenLayers.State.UNKNOWN:
+                case OpenLayers.State.UPDATE:
+                    this.state = state;
+                    break;
+            }
+        } else if (state == OpenLayers.State.UNKNOWN) {
+            this.state = state;
+        }
     },
     
-    CLASS_NAME: "OpenLayers.Layer.Yahoo"
+    CLASS_NAME: "OpenLayers.Feature.Vector"
 });
+
+
+/**
+ * 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. 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
+ *
+ * Symbolizer properties:
+ * fill - {Boolean} Set to false if no fill is desired.
+ * fillColor - {String} Hex fill color.  Default is "#ee9900".
+ * fillOpacity - {Number} Fill opacity (0-1).  Default is 0.4 
+ * stroke - {Boolean} Set to false if no stroke is desired.
+ * 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]
+ * graphic - {Boolean} Set to false if no graphic is desired.
+ * 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.
+ * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
+ * 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.
+ * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
+ *     fillText or mozDrawText to be available.
+ * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
+ *     composed of two characters. The first character is for the horizontal alignment, the second for the vertical
+ *     alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
+ *     alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". The canvas renderer does not
+ *     support vertical alignment, it will always use "b".
+ * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label.
+ * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label.
+ * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
+ *     Default is false.
+ * fontColor - {String} The font color for the label, to be provided like CSS.
+ * fontOpacity - {Number} Opacity (0-1) for the label
+ * fontFamily - {String} The font family for the label, to be provided like in CSS.
+ * fontSize - {String} The font size for the label, to be provided like in CSS.
+ * fontWeight - {String} The font weight for the label, to be provided like in CSS.
+ * display - {String} Symbolizers will have no effect if display is set to "none".  All other values have no effect.
+ */ 
+OpenLayers.Feature.Vector.style = {
+    'default': {
+        fillColor: "#ee9900",
+        fillOpacity: 0.4, 
+        hoverFillColor: "white",
+        hoverFillOpacity: 0.8,
+        strokeColor: "#ee9900",
+        strokeOpacity: 1,
+        strokeWidth: 1,
+        strokeLinecap: "round",
+        strokeDashstyle: "solid",
+        hoverStrokeColor: "red",
+        hoverStrokeOpacity: 1,
+        hoverStrokeWidth: 0.2,
+        pointRadius: 6,
+        hoverPointRadius: 1,
+        hoverPointUnit: "%",
+        pointerEvents: "visiblePainted",
+        cursor: "inherit"
+    },
+    'select': {
+        fillColor: "blue",
+        fillOpacity: 0.4, 
+        hoverFillColor: "white",
+        hoverFillOpacity: 0.8,
+        strokeColor: "blue",
+        strokeOpacity: 1,
+        strokeWidth: 2,
+        strokeLinecap: "round",
+        strokeDashstyle: "solid",
+        hoverStrokeColor: "red",
+        hoverStrokeOpacity: 1,
+        hoverStrokeWidth: 0.2,
+        pointRadius: 6,
+        hoverPointRadius: 1,
+        hoverPointUnit: "%",
+        pointerEvents: "visiblePainted",
+        cursor: "pointer"
+    },
+    'temporary': {
+        fillColor: "#66cccc",
+        fillOpacity: 0.2, 
+        hoverFillColor: "white",
+        hoverFillOpacity: 0.8,
+        strokeColor: "#66cccc",
+        strokeOpacity: 1,
+        strokeLinecap: "round",
+        strokeWidth: 2,
+        strokeDashstyle: "solid",
+        hoverStrokeColor: "red",
+        hoverStrokeOpacity: 1,
+        hoverStrokeWidth: 0.2,
+        pointRadius: 6,
+        hoverPointRadius: 1,
+        hoverPointUnit: "%",
+        pointerEvents: "visiblePainted",
+        cursor: "inherit"
+    },
+    'delete': {
+        display: "none"
+    }
+};    
 /* ======================================================================
     OpenLayers/Style.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
 
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Util.js
  * @requires OpenLayers/Feature/Vector.js
  */
@@ -30458,394 +17025,1500 @@
 OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
     'Raster'];
 /* ======================================================================
-    OpenLayers/Control/Navigation.js
+    OpenLayers/Filter.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
+
 /**
- * @requires OpenLayers/Control/ZoomBox.js
- * @requires OpenLayers/Control/DragPan.js
- * @requires OpenLayers/Handler/MouseWheel.js
- * @requires OpenLayers/Handler/Click.js
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Style.js
  */
 
 /**
- * Class: OpenLayers.Control.Navigation
- * The navigation control handles map browsing with mouse events (dragging,
- *     double-clicking, and scrolling the wheel).  Create a new navigation 
- *     control with the <OpenLayers.Control.Navigation> control.  
+ * Class: OpenLayers.Filter
+ * This class represents an OGC Filter.
+ */
+OpenLayers.Filter = OpenLayers.Class({
+    
+    /** 
+     * Constructor: OpenLayers.Filter
+     * This class represents a generic filter.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter>}
+     */
+    initialize: function(options) {
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /** 
+     * APIMethod: destroy
+     * Remove reference to anything added.
+     */
+    destroy: function() {
+    },
+
+    /**
+     * APIMethod: evaluate
+     * Evaluates this filter in a specific context.  Instances or subclasses
+     * are supposed to override this method.
+     * 
+     * Parameters:
+     * context - {Object} Context to use in evaluating the filter.  If a vector
+     *     feature is provided, the feature.attributes will be used as context.
+     * 
+     * Returns:
+     * {Boolean} The filter applies.
+     */
+    evaluate: function(context) {
+        return true;
+    },
+    
+    /**
+     * APIMethod: clone
+     * Clones this filter. Should be implementted by subclasses.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter>} Clone of this filter.
+     */
+    clone: function() {
+        return null;
+    },
+    
+    CLASS_NAME: "OpenLayers.Filter"
+});
+/* ======================================================================
+    OpenLayers/Filter/FeatureId.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.FeatureId
+ * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
+ * styling
  * 
- *     Note that this control is added to the map by default (if no controls 
- *     array is sent in the options object to the <OpenLayers.Map> 
- *     constructor).
- * 
- * Inherits:
- *  - <OpenLayers.Control>
+ * Inherits from
+ * - <OpenLayers.Filter>
  */
-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
+OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
 
     /** 
-     * Property: dragPan
-     * {<OpenLayers.Control.DragPan>} 
+     * APIProperty: fids
+     * {Array(String)} Feature Ids to evaluate this rule against. To be passed
+     * To be passed inside the params object.
      */
-    dragPan: null,
+    fids: null,
+    
+    /** 
+     * Constructor: OpenLayers.Filter.FeatureId
+     * Creates an ogc:FeatureId rule.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *           rule
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.FeatureId>}
+     */
+    initialize: function(options) {
+        this.fids = [];
+        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+    },
 
     /**
-     * APIProprety: dragPanOptions
-     * {Object} Options passed to the DragPan control.
+     * APIMethod: evaluate
+     * evaluates this rule for a specific feature
+     * 
+     * Parameters:
+     * feature - {<OpenLayers.Feature>} feature to apply the rule to.
+     *           For vector features, the check is run against the fid,
+     *           for plain features against the id.
+     * 
+     * Returns:
+     * {Boolean} true if the rule applies, false if it does not
      */
-    dragPanOptions: null,
+    evaluate: function(feature) {
+        for (var i=0, len=this.fids.length; i<len; i++) {
+            var fid = feature.fid || feature.id;
+            if (fid == this.fids[i]) {
+                return true;
+            }
+        }
+        return false;
+    },
+    
+    /**
+     * APIMethod: clone
+     * Clones this filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
+     */
+    clone: function() {
+        var filter = new OpenLayers.Filter.FeatureId();
+        OpenLayers.Util.extend(filter, this);
+        filter.fids = this.fids.slice();
+        return filter;
+    },
+    
+    CLASS_NAME: "OpenLayers.Filter.FeatureId"
+});
+/* ======================================================================
+    OpenLayers/Filter/Logical.js
+   ====================================================================== */
 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Logical
+ * This class represents ogc:And, ogc:Or and ogc:Not rules.
+ * 
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
+
     /**
-     * APIProperty: documentDrag
-     * {Boolean} Allow panning of the map by dragging outside map viewport.
-     *     Default is false.
+     * APIProperty: filters
+     * {Array(<OpenLayers.Filter>)} Child filters for this filter.
      */
-    documentDrag: false,
+    filters: null, 
+     
+    /**
+     * APIProperty: type
+     * {String} type of logical operator. Available types are:
+     * - OpenLayers.Filter.Logical.AND = "&&";
+     * - OpenLayers.Filter.Logical.OR  = "||";
+     * - OpenLayers.Filter.Logical.NOT = "!";
+     */
+    type: null,
 
     /** 
-     * Property: zoomBox
-     * {<OpenLayers.Control.ZoomBox>}
+     * Constructor: OpenLayers.Filter.Logical
+     * Creates a logical filter (And, Or, Not).
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *     filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Logical>}
      */
-    zoomBox: null,
+    initialize: function(options) {
+        this.filters = [];
+        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+    },
+    
+    /** 
+     * APIMethod: destroy
+     * Remove reference to child filters.
+     */
+    destroy: function() {
+        this.filters = null;
+        OpenLayers.Filter.prototype.destroy.apply(this);
+    },
 
     /**
-     * APIProperty: zoomBoxEnabled
-     * {Boolean} Whether the user can draw a box to zoom
+     * APIMethod: evaluate
+     * Evaluates this filter in a specific context.
+     * 
+     * Parameters:
+     * context - {Object} Context to use in evaluating the filter.  A vector
+     *     feature may also be provided to evaluate feature attributes in 
+     *     comparison filters or geometries in spatial filters.
+     * 
+     * Returns:
+     * {Boolean} The filter applies.
      */
-    zoomBoxEnabled: true, 
+    evaluate: function(context) {
+        var i, len;
+        switch(this.type) {
+            case OpenLayers.Filter.Logical.AND:
+                for (i=0, len=this.filters.length; i<len; i++) {
+                    if (this.filters[i].evaluate(context) == false) {
+                        return false;
+                    }
+                }
+                return true;
+                
+            case OpenLayers.Filter.Logical.OR:
+                for (i=0, len=this.filters.length; i<len; i++) {
+                    if (this.filters[i].evaluate(context) == true) {
+                        return true;
+                    }
+                }
+                return false;
+            
+            case OpenLayers.Filter.Logical.NOT:
+                return (!this.filters[0].evaluate(context));
+        }
+        return undefined;
+    },
+    
+    /**
+     * APIMethod: clone
+     * Clones this filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Logical>} Clone of this filter.
+     */
+    clone: function() {
+        var filters = [];        
+        for(var i=0, len=this.filters.length; i<len; ++i) {
+            filters.push(this.filters[i].clone());
+        }
+        return new OpenLayers.Filter.Logical({
+            type: this.type,
+            filters: filters
+        });
+    },
+    
+    CLASS_NAME: "OpenLayers.Filter.Logical"
+});
 
+
+OpenLayers.Filter.Logical.AND = "&&";
+OpenLayers.Filter.Logical.OR  = "||";
+OpenLayers.Filter.Logical.NOT = "!";
+/* ======================================================================
+    OpenLayers/Filter/Comparison.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Filter.js
+ * @requires OpenLayers/Console.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Comparison
+ * This class represents a comparison filter.
+ * 
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
+
     /**
-     * APIProperty: zoomWheelEnabled
-     * {Boolean} Whether the mousewheel should zoom the map
+     * APIProperty: type
+     * {String} type: type of the comparison. This is one of
+     * - OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
+     * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
+     * - OpenLayers.Filter.Comparison.LESS_THAN                = "<";
+     * - OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
+     * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
+     * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+     * - OpenLayers.Filter.Comparison.BETWEEN                  = "..";
+     * - OpenLayers.Filter.Comparison.LIKE                     = "~"; 
      */
-    zoomWheelEnabled: true,
+    type: null,
     
     /**
-     * Property: mouseWheelOptions
-     * {Object} Options passed to the MouseWheel control (only useful if
-     *     <zoomWheelEnabled> is set to true)
+     * APIProperty: property
+     * {String}
+     * name of the context property to compare
      */
-    mouseWheelOptions: null,
-
+    property: null,
+    
     /**
-     * APIProperty: handleRightClicks
-     * {Boolean} Whether or not to handle right clicks. Default is false.
+     * APIProperty: value
+     * {Number} or {String}
+     * comparison value for binary comparisons. In the case of a String, this
+     * can be a combination of text and propertyNames in the form
+     * "literal ${propertyName}"
      */
-    handleRightClicks: false,
-
+    value: null,
+    
     /**
-     * APIProperty: zoomBoxKeyMask
-     * {Integer} <OpenLayers.Handler> key code of the key, which has to be
-     *    pressed, while drawing the zoom box with the mouse on the screen. 
-     *    You should probably set handleRightClicks to true if you use this
-     *    with MOD_CTRL, to disable the context menu for machines which use
-     *    CTRL-Click as a right click.
-     * Default: <OpenLayers.Handler.MOD_SHIFT
+     * Property: matchCase
+     * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
+     *     comparisons.  The Filter Encoding 1.1 specification added a matchCase
+     *     attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
+     *     elements.  This property will be serialized with those elements only
+     *     if using the v1.1.0 filter format. However, when evaluating filters
+     *     here, the matchCase property will always be respected (for EQUAL_TO
+     *     and NOT_EQUAL_TO).  Default is true.
      */
-    zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
+    matchCase: true,
     
     /**
-     * APIProperty: autoActivate
-     * {Boolean} Activate the control when it is added to a map.  Default is
-     *     true.
+     * APIProperty: lowerBoundary
+     * {Number} or {String}
+     * lower boundary for between comparisons. In the case of a String, this
+     * can be a combination of text and propertyNames in the form
+     * "literal ${propertyName}"
      */
-    autoActivate: true,
+    lowerBoundary: null,
+    
+    /**
+     * APIProperty: upperBoundary
+     * {Number} or {String}
+     * upper boundary for between comparisons. In the case of a String, this
+     * can be a combination of text and propertyNames in the form
+     * "literal ${propertyName}"
+     */
+    upperBoundary: null,
 
-    /**
-     * Constructor: OpenLayers.Control.Navigation
-     * Create a new navigation control
+    /** 
+     * Constructor: OpenLayers.Filter.Comparison
+     * Creates a comparison rule.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *           rule
      * 
-     * Parameters:
-     * options - {Object} An optional object whose properties will be set on
-     *                    the control
+     * Returns:
+     * {<OpenLayers.Filter.Comparison>}
      */
     initialize: function(options) {
-        this.handlers = {};
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
     },
 
     /**
-     * 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.
+     * APIMethod: evaluate
+     * Evaluates this filter in a specific context.
+     * 
+     * Parameters:
+     * context - {Object} Context to use in evaluating the filter.  If a vector
+     *     feature is provided, the feature.attributes will be used as context.
+     * 
+     * Returns:
+     * {Boolean} The filter applies.
      */
-    destroy: function() {
-        this.deactivate();
-
-        if (this.dragPan) {
-            this.dragPan.destroy();
+    evaluate: function(context) {
+        if (context instanceof OpenLayers.Feature.Vector) {
+            context = context.attributes;
         }
-        this.dragPan = null;
-
-        if (this.zoomBox) {
-            this.zoomBox.destroy();
+        var result = false;
+        var got = context[this.property];
+        var exp;
+        switch(this.type) {
+            case OpenLayers.Filter.Comparison.EQUAL_TO:
+                exp = this.value;
+                if(!this.matchCase &&
+                   typeof got == "string" && typeof exp == "string") {
+                    result = (got.toUpperCase() == exp.toUpperCase());
+                } else {
+                    result = (got == exp);
+                }
+                break;
+            case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
+                exp = this.value;
+                if(!this.matchCase &&
+                   typeof got == "string" && typeof exp == "string") {
+                    result = (got.toUpperCase() != exp.toUpperCase());
+                } else {
+                    result = (got != exp);
+                }
+                break;
+            case OpenLayers.Filter.Comparison.LESS_THAN:
+                result = got < this.value;
+                break;
+            case OpenLayers.Filter.Comparison.GREATER_THAN:
+                result = got > this.value;
+                break;
+            case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
+                result = got <= this.value;
+                break;
+            case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
+                result = got >= this.value;
+                break;
+            case OpenLayers.Filter.Comparison.BETWEEN:
+                result = (got >= this.lowerBoundary) &&
+                    (got <= this.upperBoundary);
+                break;
+            case OpenLayers.Filter.Comparison.LIKE:
+                var regexp = new RegExp(this.value, "gi");
+                result = regexp.test(got);
+                break;
         }
-        this.zoomBox = null;
-        OpenLayers.Control.prototype.destroy.apply(this,arguments);
+        return result;
     },
     
     /**
-     * Method: activate
+     * APIMethod: value2regex
+     * Converts the value of this rule into a regular expression string,
+     * according to the wildcard characters specified. This method has to
+     * be called after instantiation of this class, if the value is not a
+     * regular expression already.
+     * 
+     * Parameters:
+     * wildCard   - {<Char>} wildcard character in the above value, default
+     *              is "*"
+     * singleChar - {<Char>) single-character wildcard in the above value
+     *              default is "."
+     * escape     - {<Char>) escape character in the above value, default is
+     *              "!"
+     * 
+     * Returns:
+     * {String} regular expression string
      */
-    activate: function() {
-        this.dragPan.activate();
-        if (this.zoomWheelEnabled) {
-            this.handlers.wheel.activate();
-        }    
-        this.handlers.click.activate();
-        if (this.zoomBoxEnabled) {
-            this.zoomBox.activate();
+    value2regex: function(wildCard, singleChar, escapeChar) {
+        if (wildCard == ".") {
+            var msg = "'.' is an unsupported wildCard character for "+
+                    "OpenLayers.Filter.Comparison";
+            OpenLayers.Console.error(msg);
+            return null;
         }
-        return OpenLayers.Control.prototype.activate.apply(this,arguments);
+        
+
+        // set UMN MapServer defaults for unspecified parameters
+        wildCard = wildCard ? wildCard : "*";
+        singleChar = singleChar ? singleChar : ".";
+        escapeChar = escapeChar ? escapeChar : "!";
+        
+        this.value = this.value.replace(
+                new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
+        this.value = this.value.replace(
+                new RegExp("\\"+singleChar, "g"), ".");
+        this.value = this.value.replace(
+                new RegExp("\\"+wildCard, "g"), ".*");
+        this.value = this.value.replace(
+                new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
+        this.value = this.value.replace(
+                new RegExp("\\\\\\.", "g"), "\\"+singleChar);
+        
+        return this.value;
     },
-
+    
     /**
-     * Method: deactivate
+     * Method: regex2value
+     * Convert the value of this rule from a regular expression string into an
+     *     ogc literal string using a wildCard of *, a singleChar of ., and an
+     *     escape of !.  Leaves the <value> property unmodified.
+     * 
+     * Returns:
+     * {String} A string value.
      */
-    deactivate: function() {
-        this.zoomBox.deactivate();
-        this.dragPan.deactivate();
-        this.handlers.click.deactivate();
-        this.handlers.wheel.deactivate();
-        return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
+    regex2value: function() {
+        
+        var value = this.value;
+        
+        // replace ! with !!
+        value = value.replace(/!/g, "!!");
+
+        // replace \. with !. (watching out for \\.)
+        value = value.replace(/(\\)?\\\./g, function($0, $1) {
+            return $1 ? $0 : "!.";
+        });
+        
+        // replace \* with #* (watching out for \\*)
+        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
+            return $1 ? $0 : "!*";
+        });
+        
+        // replace \\ with \
+        value = value.replace(/\\\\/g, "\\");
+
+        // convert .* to * (the sequence #.* is not allowed)
+        value = value.replace(/\.\*/g, "*");
+        
+        return value;
     },
     
     /**
-     * Method: draw
+     * APIMethod: clone
+     * Clones this filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Comparison>} Clone of this filter.
      */
-    draw: function() {
-        // disable right mouse context menu for support of right click events
-        if (this.handleRightClicks) {
-            this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
-        }
-
-        var clickCallbacks = { 
-            'dblclick': this.defaultDblClick, 
-            'dblrightclick': this.defaultDblRightClick 
-        };
-        var clickOptions = {
-            'double': true, 
-            'stopDouble': true
-        };
-        this.handlers.click = new OpenLayers.Handler.Click(
-            this, clickCallbacks, clickOptions
-        );
-        this.dragPan = new OpenLayers.Control.DragPan(
-            OpenLayers.Util.extend({
-                map: this.map,
-                documentDrag: this.documentDrag
-            }, this.dragPanOptions)
-        );
-        this.zoomBox = new OpenLayers.Control.ZoomBox(
-                    {map: this.map, keyMask: this.zoomBoxKeyMask});
-        this.dragPan.draw();
-        this.zoomBox.draw();
-        this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
-                                    this, {"up"  : this.wheelUp,
-                                           "down": this.wheelDown},
-                                    this.mouseWheelOptions );
+    clone: function() {
+        return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
     },
+    
+    CLASS_NAME: "OpenLayers.Filter.Comparison"
+});
 
+
+OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
+OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
+OpenLayers.Filter.Comparison.LESS_THAN                = "<";
+OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
+OpenLayers.Filter.Comparison.BETWEEN                  = "..";
+OpenLayers.Filter.Comparison.LIKE                     = "~";
+/* ======================================================================
+    OpenLayers/Format/Filter.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/XML.js
+ * @requires OpenLayers/Filter/FeatureId.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter
+ * Read/Wite ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
+ *     constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML, {
+    
     /**
-     * Method: defaultDblClick 
-     * 
+     * APIProperty: defaultVersion
+     * {String} Version number to assume if none found.  Default is "1.0.0".
+     */
+    defaultVersion: "1.0.0",
+    
+    /**
+     * APIProperty: version
+     * {String} Specify a version string if one is known.
+     */
+    version: null,
+    
+    /**
+     * Property: parser
+     * {Object} Instance of the versioned parser.  Cached for multiple read and
+     *     write calls of the same version.
+     */
+    parser: null,
+
+    /**
+     * Constructor: OpenLayers.Format.Filter
+     * Create a new parser for Filter.
+     *
      * Parameters:
-     * evt - {Event} 
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
      */
-    defaultDblClick: function (evt) {
-        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
-        this.map.setCenter(newCenter, this.map.zoom + 1);
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
     },
 
     /**
-     * Method: defaultDblRightClick 
-     * 
+     * APIMethod: write
+     * Write an ogc:Filter given a filter object.
+     *
      * Parameters:
-     * evt - {Event} 
+     * filter - {<OpenLayers.Filter>} An filter.
+     * options - {Object} Optional configuration object.
+     *
+     * Returns:
+     * {Elment} An ogc:Filter element node.
      */
-    defaultDblRightClick: function (evt) {
-        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
-        this.map.setCenter(newCenter, this.map.zoom - 1);
+    write: function(filter, options) {
+        var version = (options && options.version) ||
+                      this.version || this.defaultVersion;
+        if(!this.parser || this.parser.VERSION != version) {
+            var format = OpenLayers.Format.Filter[
+                "v" + version.replace(/\./g, "_")
+            ];
+            if(!format) {
+                throw "Can't find a Filter parser for version " +
+                      version;
+            }
+            this.parser = new format(this.options);
+        }
+        return this.parser.write(filter);
+        //return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
     },
     
     /**
-     * Method: wheelChange  
+     * APIMethod: read
+     * Read and Filter doc and return an object representing the Filter.
      *
      * Parameters:
-     * evt - {Event}
-     * deltaZ - {Integer}
+     * data - {String | DOMElement} Data to read.
+     *
+     * Returns:
+     * {<OpenLayers.Filter>} A filter object.
      */
-    wheelChange: function(evt, deltaZ) {
-        var currentZoom = this.map.getZoom();
-        var newZoom = this.map.getZoom() + Math.round(deltaZ);
-        newZoom = Math.max(newZoom, 0);
-        newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
-        if (newZoom === currentZoom) {
-            return;
+    read: function(data) {
+        if(typeof data == "string") {
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
         }
-        var size    = this.map.getSize();
-        var deltaX  = size.w/2 - evt.xy.x;
-        var deltaY  = evt.xy.y - size.h/2;
-        var newRes  = this.map.baseLayer.getResolutionForZoom(newZoom);
-        var zoomPoint = this.map.getLonLatFromPixel(evt.xy);
-        var newCenter = new OpenLayers.LonLat(
-                            zoomPoint.lon + deltaX * newRes,
-                            zoomPoint.lat + deltaY * newRes );
-        this.map.setCenter( newCenter, newZoom );
+        var version = this.version;
+        if(!version) {
+            version = this.defaultVersion;
+        }
+        if(!this.parser || this.parser.VERSION != version) {
+            var format = OpenLayers.Format.Filter[
+                "v" + version.replace(/\./g, "_")
+            ];
+            if(!format) {
+                throw "Can't find a Filter parser for version " +
+                      version;
+            }
+            this.parser = new format(this.options);
+        }
+        var filter = this.parser.read(data);
+        return filter;
     },
 
-    /** 
-     * Method: wheelUp
-     * User spun scroll wheel up
-     * 
-     * Parameters:
-     * evt - {Event}
-     * delta - {Integer}
+    CLASS_NAME: "OpenLayers.Format.Filter" 
+});
+/* ======================================================================
+    OpenLayers/Format/Filter/v1.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+/**
+ * @requires OpenLayers/Format/Filter.js
+ * @requires OpenLayers/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1
+ * Superclass for Filter version 1 parsers.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
+    
+    /**
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.
      */
-    wheelUp: function(evt, delta) {
-        this.wheelChange(evt, delta || 1);
+    namespaces: {
+        ogc: "http://www.opengis.net/ogc",
+        gml: "http://www.opengis.net/gml",
+        xlink: "http://www.w3.org/1999/xlink",
+        xsi: "http://www.w3.org/2001/XMLSchema-instance"
     },
+    
+    /**
+     * Property: defaultPrefix
+     */
+    defaultPrefix: "ogc",
 
-    /** 
-     * Method: wheelDown
-     * User spun scroll wheel down
-     * 
+    /**
+     * Property: schemaLocation
+     * {String} Schema location for a particular minor version.
+     */
+    schemaLocation: null,
+    
+    /**
+     * Constructor: OpenLayers.Format.Filter.v1
+     * Instances of this class are not created directly.  Use the
+     *     <OpenLayers.Format.Filter> constructor instead.
+     *
      * Parameters:
-     * evt - {Event}
-     * delta - {Integer}
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
      */
-    wheelDown: function(evt, delta) {
-        this.wheelChange(evt, delta || -1);
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
     },
     
     /**
-     * Method: disableZoomBox
+     * Method: read
+     *
+     * Parameters:
+     * data - {DOMElement} A Filter document element.
+     *
+     * Returns:
+     * {<OpenLayers.Filter>} A filter object.
      */
-    disableZoomBox : function() {
-        this.zoomBoxEnabled = false;
-        this.zoomBox.deactivate();       
+    read: function(data) {
+        var obj = {};
+        this.readers.ogc["Filter"].apply(this, [data, obj]);
+        return obj.filter;
     },
     
     /**
-     * Method: enableZoomBox
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
      */
-    enableZoomBox : function() {
-        this.zoomBoxEnabled = true;
-        if (this.active) {
-            this.zoomBox.activate();
-        }    
+    readers: {
+        "ogc": {
+            "Filter": function(node, parent) {
+                // Filters correspond to subclasses of OpenLayers.Filter.
+                // Since they contain information we don't persist, we
+                // create a temporary object and then pass on the filter
+                // (ogc:Filter) to the parent obj.
+                var obj = {
+                    fids: [],
+                    filters: []
+                };
+                this.readChildNodes(node, obj);
+                if(obj.fids.length > 0) {
+                    parent.filter = new OpenLayers.Filter.FeatureId({
+                        fids: obj.fids
+                    });
+                } else if(obj.filters.length > 0) {
+                    parent.filter = obj.filters[0];
+                }
+            },
+            "FeatureId": function(node, obj) {
+                var fid = node.getAttribute("fid");
+                if(fid) {
+                    obj.fids.push(fid);
+                }
+            },
+            "And": function(node, obj) {
+                var filter = new OpenLayers.Filter.Logical({
+                    type: OpenLayers.Filter.Logical.AND
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "Or": function(node, obj) {
+                var filter = new OpenLayers.Filter.Logical({
+                    type: OpenLayers.Filter.Logical.OR
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "Not": function(node, obj) {
+                var filter = new OpenLayers.Filter.Logical({
+                    type: OpenLayers.Filter.Logical.NOT
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsLessThan": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LESS_THAN
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsGreaterThan": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.GREATER_THAN
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsLessThanOrEqualTo": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsBetween": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.BETWEEN
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "Literal": function(node, obj) {
+                obj.value = OpenLayers.String.numericIf(
+                    this.getChildValue(node));
+            },
+            "PropertyName": function(node, filter) {
+                filter.property = this.getChildValue(node);
+            },
+            "LowerBoundary": function(node, filter) {
+                filter.lowerBoundary = OpenLayers.String.numericIf(
+                    this.readOgcExpression(node));
+            },
+            "UpperBoundary": function(node, filter) {
+                filter.upperBoundary = OpenLayers.String.numericIf(
+                    this.readOgcExpression(node));
+            },
+            "Intersects": function(node, obj) {
+                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
+            },
+            "Within": function(node, obj) {
+                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
+            },
+            "Contains": function(node, obj) {
+                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
+            },
+            "DWithin": function(node, obj) {
+                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
+            },
+            "Distance": function(node, obj) {
+                obj.distance = parseInt(this.getChildValue(node));
+                obj.distanceUnits = node.getAttribute("units");
+            }
+        }
     },
     
     /**
-     * Method: disableZoomWheel
+     * Method: readSpatial
+     *
+     * Read a {<OpenLayers.Filter.Spatial>} filter.
+     * 
+     * Parameters:
+     * node - {DOMElement} A DOM element that contains an ogc:expression.
+     * obj - {Object} The target object.
+     * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
+     *
+     * Returns:
+     * {<OpenLayers.Filter.Spatial>} The created filter.
      */
+    readSpatial: function(node, obj, type) {
+        var filter = new OpenLayers.Filter.Spatial({
+            type: type
+        });
+        this.readChildNodes(node, filter);
+        filter.value = filter.components[0];
+        delete filter.components;
+        obj.filters.push(filter);
+    },
+
+    /**
+     * Method: readOgcExpression
+     * Limited support for OGC expressions.
+     *
+     * Parameters:
+     * node - {DOMElement} A DOM element that contains an ogc:expression.
+     *
+     * Returns:
+     * {String} A value to be used in a symbolizer.
+     */
+    readOgcExpression: function(node) {
+        var obj = {};
+        this.readChildNodes(node, obj);
+        var value = obj.value;
+        if(value === undefined) {
+            value = this.getChildValue(node);
+        }
+        return value;
+    },
+
+    /**
+     * Method: write
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter>} A filter object.
+     *
+     * Returns:
+     * {DOMElement} An ogc:Filter element.
+     */
+    write: function(filter) {
+        return this.writers.ogc["Filter"].apply(this, [filter]);
+    },
     
-    disableZoomWheel : function() {
-        this.zoomWheelEnabled = false;
-        this.handlers.wheel.deactivate();       
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "ogc": {
+            "Filter": function(filter) {
+                var node = this.createElementNSPlus("ogc:Filter");
+                var sub = filter.CLASS_NAME.split(".").pop();
+                if(sub == "FeatureId") {
+                    for(var i=0; i<filter.fids.length; ++i) {
+                        this.writeNode("FeatureId", filter.fids[i], node);
+                    }
+                } else {
+                    this.writeNode(this.getFilterType(filter), filter, node);
+                }
+                return node;
+            },
+            "FeatureId": function(fid) {
+                return this.createElementNSPlus("ogc:FeatureId", {
+                    attributes: {fid: fid}
+                });
+            },
+            "And": function(filter) {
+                var node = this.createElementNSPlus("ogc:And");
+                var childFilter;
+                for(var i=0; i<filter.filters.length; ++i) {
+                    childFilter = filter.filters[i];
+                    this.writeNode(
+                        this.getFilterType(childFilter), childFilter, node
+                    );
+                }
+                return node;
+            },
+            "Or": function(filter) {
+                var node = this.createElementNSPlus("ogc:Or");
+                var childFilter;
+                for(var i=0; i<filter.filters.length; ++i) {
+                    childFilter = filter.filters[i];
+                    this.writeNode(
+                        this.getFilterType(childFilter), childFilter, node
+                    );
+                }
+                return node;
+            },
+            "Not": function(filter) {
+                var node = this.createElementNSPlus("ogc:Not");
+                var childFilter = filter.filters[0];
+                this.writeNode(
+                    this.getFilterType(childFilter), childFilter, node
+                );
+                return node;
+            },
+            "PropertyIsLessThan": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);                
+                return node;
+            },
+            "PropertyIsGreaterThan": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsLessThanOrEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsGreaterThanOrEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsBetween": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsBetween");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("LowerBoundary", filter, node);
+                this.writeNode("UpperBoundary", filter, node);
+                return node;
+            },
+            "PropertyName": function(filter) {
+                // no ogc:expression handling for now
+                return this.createElementNSPlus("ogc:PropertyName", {
+                    value: filter.property
+                });
+            },
+            "Literal": function(value) {
+                // no ogc:expression handling for now
+                return this.createElementNSPlus("ogc:Literal", {
+                    value: value
+                });
+            },
+            "LowerBoundary": function(filter) {
+                // no ogc:expression handling for now
+                var node = this.createElementNSPlus("ogc:LowerBoundary");
+                this.writeNode("Literal", filter.lowerBoundary, node);
+                return node;
+            },
+            "UpperBoundary": function(filter) {
+                // no ogc:expression handling for now
+                var node = this.createElementNSPlus("ogc:UpperBoundary");
+                this.writeNode("Literal", filter.upperBoundary, node);
+                return node;
+            },
+            "INTERSECTS": function(filter) {
+                return this.writeSpatial(filter, "Intersects");
+            },
+            "WITHIN": function(filter) {
+                return this.writeSpatial(filter, "Within");
+            },
+            "CONTAINS": function(filter) {
+                return this.writeSpatial(filter, "Contains");
+            },
+            "DWITHIN": function(filter) {
+                var node = this.writeSpatial(filter, "DWithin");
+                this.writeNode("Distance", filter, node);
+                return node;
+            },
+            "Distance": function(filter) {
+                return this.createElementNSPlus("ogc:Distance", {
+                    attributes: {
+                        units: filter.distanceUnits
+                    },
+                    value: filter.distance
+                });
+            }
+        }
     },
+
+    /**
+     * Method: getFilterType
+     */
+    getFilterType: function(filter) {
+        var filterType = this.filterMap[filter.type];
+        if(!filterType) {
+            throw "Filter writing not supported for rule type: " + filter.type;
+        }
+        return filterType;
+    },
     
     /**
-     * Method: enableZoomWheel
+     * Property: filterMap
+     * {Object} Contains a member for each filter type.  Values are node names
+     *     for corresponding OGC Filter child elements.
      */
-    
-    enableZoomWheel : function() {
-        this.zoomWheelEnabled = true;
-        if (this.active) {
-            this.handlers.wheel.activate();
-        }    
+    filterMap: {
+        "&&": "And",
+        "||": "Or",
+        "!": "Not",
+        "==": "PropertyIsEqualTo",
+        "!=": "PropertyIsNotEqualTo",
+        "<": "PropertyIsLessThan",
+        ">": "PropertyIsGreaterThan",
+        "<=": "PropertyIsLessThanOrEqualTo",
+        ">=": "PropertyIsGreaterThanOrEqualTo",
+        "..": "PropertyIsBetween",
+        "~": "PropertyIsLike",
+        "BBOX": "BBOX",
+        "DWITHIN": "DWITHIN",
+        "WITHIN": "WITHIN",
+        "CONTAINS": "CONTAINS",
+        "INTERSECTS": "INTERSECTS"
     },
 
-    CLASS_NAME: "OpenLayers.Control.Navigation"
+    CLASS_NAME: "OpenLayers.Format.Filter.v1" 
+
 });
 /* ======================================================================
-    OpenLayers/Filter.js
+    OpenLayers/Format/WKT.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Style.js
+ * @requires OpenLayers/Format.js
+ * @requires OpenLayers/Feature/Vector.js
  */
 
 /**
- * Class: OpenLayers.Filter
- * This class represents an OGC Filter.
+ * Class: OpenLayers.Format.WKT
+ * Class for reading and writing Well-Known Text.  Create a new instance
+ * with the <OpenLayers.Format.WKT> constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format>
  */
-OpenLayers.Filter = OpenLayers.Class({
+OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
     
-    /** 
-     * Constructor: OpenLayers.Filter
-     * This is an abstract class.  Create an instance of a filter subclass.
+    /**
+     * Constructor: OpenLayers.Format.WKT
+     * Create a new parser for WKT
      *
      * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     * 
+     * options - {Object} An optional object whose properties will be set on
+     *           this instance
+     *
      * Returns:
-     * {<OpenLayers.Filter>}
+     * {<OpenLayers.Format.WKT>} A new WKT parser.
      */
     initialize: function(options) {
-        OpenLayers.Util.extend(this, options);
+        this.regExes = {
+            'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+            'spaces': /\s+/,
+            'parenComma': /\)\s*,\s*\(/,
+            'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,  // can't use {2} here
+            'trimParens': /^\s*\(?(.*?)\)?\s*$/
+        };
+        OpenLayers.Format.prototype.initialize.apply(this, [options]);
     },
 
-    /** 
-     * APIMethod: destroy
-     * Remove reference to anything added.
+    /**
+     * Method: read
+     * Deserialize a WKT string and return a vector feature or an
+     * array of vector features.  Supports WKT for POINT, MULTIPOINT,
+     * LINESTRING, MULTILINESTRING, POLYGON, MULTIPOLYGON, and
+     * GEOMETRYCOLLECTION.
+     *
+     * Parameters:
+     * wkt - {String} A WKT string
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>|Array} A feature or array of features for
+     * GEOMETRYCOLLECTION WKT.
      */
-    destroy: function() {
+    read: function(wkt) {
+        var features, type, str;
+        var matches = this.regExes.typeStr.exec(wkt);
+        if(matches) {
+            type = matches[1].toLowerCase();
+            str = matches[2];
+            if(this.parse[type]) {
+                features = this.parse[type].apply(this, [str]);
+            }
+            if (this.internalProjection && this.externalProjection) {
+                if (features && 
+                    features.CLASS_NAME == "OpenLayers.Feature.Vector") {
+                    features.geometry.transform(this.externalProjection,
+                                                this.internalProjection);
+                } else if (features &&
+                           type != "geometrycollection" &&
+                           typeof features == "object") {
+                    for (var i=0, len=features.length; i<len; i++) {
+                        var component = features[i];
+                        component.geometry.transform(this.externalProjection,
+                                                     this.internalProjection);
+                    }
+                }
+            }
+        }    
+        return features;
     },
 
     /**
-     * APIMethod: evaluate
-     * Evaluates this filter in a specific context.  Should be implemented by
-     *     subclasses.
-     * 
+     * Method: write
+     * Serialize a feature or array of features into a WKT string.
+     *
      * Parameters:
-     * context - {Object} Context to use in evaluating the filter.  If a vector
-     *     feature is provided, the feature.attributes will be used as context.
-     * 
+     * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
+     *            features
+     *
      * Returns:
-     * {Boolean} The filter applies.
+     * {String} The WKT string representation of the input geometries
      */
-    evaluate: function(context) {
-        return true;
+    write: function(features) {
+        var collection, geometry, type, data, isCollection;
+        if (features.constructor == Array) {
+            collection = features;
+            isCollection = true;
+        } else {
+            collection = [features];
+            isCollection = false;
+        }
+        var pieces = [];
+        if (isCollection) {
+            pieces.push('GEOMETRYCOLLECTION(');
+        }
+        for (var i=0, len=collection.length; i<len; ++i) {
+            if (isCollection && i>0) {
+                pieces.push(',');
+            }
+            geometry = collection[i].geometry;
+            pieces.push(this.extractGeometry(geometry));
+        }
+        if (isCollection) {
+            pieces.push(')');
+        }
+        return pieces.join('');
     },
-    
+
     /**
-     * APIMethod: clone
-     * Clones this filter. Should be implementted by subclasses.
-     * 
+     * Method: extractGeometry
+     * Entry point to construct the WKT for a single Geometry object.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry.Geometry>}
+     *
      * Returns:
-     * {<OpenLayers.Filter>} Clone of this filter.
+     * {String} A WKT string of representing the geometry
      */
-    clone: function() {
-        return null;
+    extractGeometry: function(geometry) {
+        var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+        if (!this.extract[type]) {
+            return null;
+        }
+        if (this.internalProjection && this.externalProjection) {
+            geometry = geometry.clone();
+            geometry.transform(this.internalProjection, this.externalProjection);
+        }                       
+        var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
+        var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
+        return data;
     },
     
-    CLASS_NAME: "OpenLayers.Filter"
-});
+    /**
+     * Object with properties corresponding to the geometry types.
+     * Property values are functions that do the actual data extraction.
+     */
+    extract: {
+        /**
+         * Return a space delimited string of point coordinates.
+         * @param {<OpenLayers.Geometry.Point>} point
+         * @returns {String} A string of coordinates representing the point
+         */
+        'point': function(point) {
+            return point.x + ' ' + point.y;
+        },
+
+        /**
+         * Return a comma delimited string of point coordinates from a multipoint.
+         * @param {<OpenLayers.Geometry.MultiPoint>} multipoint
+         * @returns {String} A string of point coordinate strings representing
+         *                  the multipoint
+         */
+        'multipoint': function(multipoint) {
+            var array = [];
+            for(var i=0, len=multipoint.components.length; i<len; ++i) {
+                array.push('(' +
+                           this.extract.point.apply(this, [multipoint.components[i]]) +
+                           ')');
+            }
+            return array.join(',');
+        },
+        
+        /**
+         * Return a comma delimited string of point coordinates from a line.
+         * @param {<OpenLayers.Geometry.LineString>} linestring
+         * @returns {String} A string of point coordinate strings representing
+         *                  the linestring
+         */
+        'linestring': function(linestring) {
+            var array = [];
+            for(var i=0, len=linestring.components.length; i<len; ++i) {
+                array.push(this.extract.point.apply(this, [linestring.components[i]]));
+            }
+            return array.join(',');
+        },
+
+        /**
+         * Return a comma delimited string of linestring strings from a multilinestring.
+         * @param {<OpenLayers.Geometry.MultiLineString>} multilinestring
+         * @returns {String} A string of of linestring strings representing
+         *                  the multilinestring
+         */
+        'multilinestring': function(multilinestring) {
+            var array = [];
+            for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+                array.push('(' +
+                           this.extract.linestring.apply(this, [multilinestring.components[i]]) +
+                           ')');
+            }
+            return array.join(',');
+        },
+        
+        /**
+         * Return a comma delimited string of linear ring arrays from a polygon.
+         * @param {<OpenLayers.Geometry.Polygon>} polygon
+         * @returns {String} An array of linear ring arrays representing the polygon
+         */
+        'polygon': function(polygon) {
+            var array = [];
+            for(var i=0, len=polygon.components.length; i<len; ++i) {
+                array.push('(' +
+                           this.extract.linestring.apply(this, [polygon.components[i]]) +
+                           ')');
+            }
+            return array.join(',');
+        },
+
+        /**
+         * Return an array of polygon arrays from a multipolygon.
+         * @param {<OpenLayers.Geometry.MultiPolygon>} multipolygon
+         * @returns {String} An array of polygon arrays representing
+         *                  the multipolygon
+         */
+        'multipolygon': function(multipolygon) {
+            var array = [];
+            for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+                array.push('(' +
+                           this.extract.polygon.apply(this, [multipolygon.components[i]]) +
+                           ')');
+            }
+            return array.join(',');
+        },
+
+        /**
+         * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an <OpenLayers.Geometry.Collection>
+         * @param {<OpenLayers.Geometry.Collection>} collection
+         * @returns {String} internal WKT representation of the collection
+         */
+        'collection': function(collection) {
+            var array = [];
+            for(var i=0, len=collection.components.length; i<len; ++i) {
+                array.push(this.extractGeometry.apply(this, [collection.components[i]]));
+            }
+            return array.join(',');
+        }
+
+    },
+
+    /**
+     * Object with properties corresponding to the geometry types.
+     * Property values are functions that do the actual parsing.
+     */
+    parse: {
+        /**
+         * Return point feature given a point WKT fragment.
+         * @param {String} str A WKT fragment representing the point
+         * @returns {<OpenLayers.Feature.Vector>} A point feature
+         * @private
+         */
+        'point': function(str) {
+            var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(coords[0], coords[1])
+            );
+        },
+
+        /**
+         * Return a multipoint feature given a multipoint WKT fragment.
+         * @param {String} A WKT fragment representing the multipoint
+         * @returns {<OpenLayers.Feature.Vector>} A multipoint feature
+         * @private
+         */
+        'multipoint': function(str) {
+            var point;
+            var points = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+            var components = [];
+            for(var i=0, len=points.length; i<len; ++i) {
+                point = points[i].replace(this.regExes.trimParens, '$1');
+                components.push(this.parse.point.apply(this, [point]).geometry);
+            }
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.MultiPoint(components)
+            );
+        },
+        
+        /**
+         * Return a linestring feature given a linestring WKT fragment.
+         * @param {String} A WKT fragment representing the linestring
+         * @returns {<OpenLayers.Feature.Vector>} A linestring feature
+         * @private
+         */
+        'linestring': function(str) {
+            var points = OpenLayers.String.trim(str).split(',');
+            var components = [];
+            for(var i=0, len=points.length; i<len; ++i) {
+                components.push(this.parse.point.apply(this, [points[i]]).geometry);
+            }
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.LineString(components)
+            );
+        },
+
+        /**
+         * Return a multilinestring feature given a multilinestring WKT fragment.
+         * @param {String} A WKT fragment representing the multilinestring
+         * @returns {<OpenLayers.Feature.Vector>} A multilinestring feature
+         * @private
+         */
+        'multilinestring': function(str) {
+            var line;
+            var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+            var components = [];
+            for(var i=0, len=lines.length; i<len; ++i) {
+                line = lines[i].replace(this.regExes.trimParens, '$1');
+                components.push(this.parse.linestring.apply(this, [line]).geometry);
+            }
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.MultiLineString(components)
+            );
+        },
+        
+        /**
+         * Return a polygon feature given a polygon WKT fragment.
+         * @param {String} A WKT fragment representing the polygon
+         * @returns {<OpenLayers.Feature.Vector>} A polygon feature
+         * @private
+         */
+        'polygon': function(str) {
+            var ring, linestring, linearring;
+            var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
+            var components = [];
+            for(var i=0, len=rings.length; i<len; ++i) {
+                ring = rings[i].replace(this.regExes.trimParens, '$1');
+                linestring = this.parse.linestring.apply(this, [ring]).geometry;
+                linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
+                components.push(linearring);
+            }
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Polygon(components)
+            );
+        },
+
+        /**
+         * Return a multipolygon feature given a multipolygon WKT fragment.
+         * @param {String} A WKT fragment representing the multipolygon
+         * @returns {<OpenLayers.Feature.Vector>} A multipolygon feature
+         * @private
+         */
+        'multipolygon': function(str) {
+            var polygon;
+            var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
+            var components = [];
+            for(var i=0, len=polygons.length; i<len; ++i) {
+                polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+                components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+            }
+            return new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.MultiPolygon(components)
+            );
+        },
+
+        /**
+         * Return an array of features given a geometrycollection WKT fragment.
+         * @param {String} A WKT fragment representing the geometrycollection
+         * @returns {Array} An array of OpenLayers.Feature.Vector
+         * @private
+         */
+        'geometrycollection': function(str) {
+            // separate components of the collection with |
+            str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+            var wktArray = OpenLayers.String.trim(str).split('|');
+            var components = [];
+            for(var i=0, len=wktArray.length; i<len; ++i) {
+                components.push(OpenLayers.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+            }
+            return components;
+        }
+
+    },
+
+    CLASS_NAME: "OpenLayers.Format.WKT" 
+});     
 /* ======================================================================
     OpenLayers/Geometry.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
  
 /**
+ * @requires OpenLayers/BaseTypes/Class.js
  * @requires OpenLayers/Format/WKT.js
  * @requires OpenLayers/Feature/Vector.js
  */
@@ -31297,2526 +18970,297 @@
     };
 };
 /* ======================================================================
-    OpenLayers/Layer/Google/v3.js
+    OpenLayers/Geometry/Point.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
 
-
 /**
- * @requires OpenLayers/Layer/Google.js
+ * @requires OpenLayers/Geometry.js
  */
 
 /**
- * Constant: OpenLayers.Layer.Google.v3
+ * Class: OpenLayers.Geometry.Point
+ * Point geometry class. 
  * 
- * Mixin providing functionality specific to the Google Maps API v3. Note that
- * this layer configures the google.maps.map object with the "disableDefaultUI"
- * option set to true. Using UI controls that the Google Maps API provides is
- * not supported by the OpenLayers API.
+ * Inherits from:
+ *  - <OpenLayers.Geometry> 
  */
-OpenLayers.Layer.Google.v3 = {
-    
-    /**
-     * Constant: DEFAULTS
-     * {Object} It is not recommended to change the properties set here. Note
-     * that Google.v3 layers only work when sphericalMercator is set to true.
-     * 
-     * (code)
-     * {
-     *     maxExtent: new OpenLayers.Bounds(
-     *         -128 * 156543.0339,
-     *         -128 * 156543.0339,
-     *         128 * 156543.0339,
-     *         128 * 156543.0339
-     *     ),
-     *     sphericalMercator: true,
-     *     maxResolution: 156543.0339,
-     *     units: "m",
-     *     projection: "EPSG:900913"
-     * }
-     * (end)
+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
+
+    /** 
+     * APIProperty: x 
+     * {float} 
      */
-    DEFAULTS: {
-        maxExtent: new OpenLayers.Bounds(
-            -128 * 156543.0339,
-            -128 * 156543.0339,
-            128 * 156543.0339,
-            128 * 156543.0339
-        ),
-        sphericalMercator: true,
-        maxResolution: 156543.0339,
-        units: "m",
-        projection: "EPSG:900913"
-    },
+    x: null,
 
     /** 
-     * Method: loadMapObject
-     * Load the GMap and register appropriate event listeners. If we can't 
-     *     load GMap2, then display a warning message.
+     * APIProperty: y 
+     * {float} 
      */
-    loadMapObject:function() {
-        if (!this.type) {
-            this.type = google.maps.MapTypeId.ROADMAP;
-        }
-        var mapObject;
-        var cache = OpenLayers.Layer.Google.cache[this.map.id];
-        if (cache) {
-            // there are already Google layers added to this map
-            mapObject = cache.mapObject;
-            // increment the layer count
-            ++cache.count;
-        } else {
-            // this is the first Google layer for this map
+    y: null,
 
-            var container = this.map.viewPortDiv;
-            var div = document.createElement("div");
-            div.id = this.map.id + "_GMapContainer";
-            div.style.position = "absolute";
-            div.style.width = "100%";
-            div.style.height = "100%";
-            container.appendChild(div);
-
-            // create GMap and shuffle elements
-            var center = this.map.getCenter();
-            mapObject = new google.maps.Map(div, {
-                center: center ?
-                    new google.maps.LatLng(center.lat, center.lon) :
-                    new google.maps.LatLng(0, 0),
-                zoom: this.map.getZoom() || 0,
-                mapTypeId: this.type,
-                disableDefaultUI: true,
-                keyboardShortcuts: false,
-                draggable: false,
-                disableDoubleClickZoom: true,
-                scrollwheel: false
-            });
-            
-            // cache elements for use by any other google layers added to
-            // this same map
-            cache = {
-                mapObject: mapObject,
-                count: 1
-            };
-            OpenLayers.Layer.Google.cache[this.map.id] = cache;
-            this.repositionListener = google.maps.event.addListenerOnce(
-                mapObject, 
-                "center_changed", 
-                OpenLayers.Function.bind(this.repositionMapElements, this)
-            );
-        }
-        this.mapObject = mapObject;
-        this.setGMapVisibility(this.visibility);
-    },
-    
     /**
-     * Method: repositionMapElements
+     * Constructor: OpenLayers.Geometry.Point
+     * Construct a point geometry.
      *
-     * Waits until powered by and terms of use elements are available and then
-     * moves them so they are clickable.
-     */
-    repositionMapElements: function() {
-
-        // This is the first time any Google layer in this mapObject has been
-        // made visible.  The mapObject needs to know the container size.
-        google.maps.event.trigger(this.mapObject, "resize");
-        
-        var div = this.mapObject.getDiv().firstChild;
-        if (!div || div.childNodes.length < 3) {
-            this.repositionTimer = window.setTimeout(
-                OpenLayers.Function.bind(this.repositionMapElements, this),
-                250
-            );
-            return false;
-        }
-
-        var cache = OpenLayers.Layer.Google.cache[this.map.id];
-        var container = this.map.viewPortDiv;
-
-        // move the ToS and branding stuff up to the container div
-        var termsOfUse = div.lastChild;
-        container.appendChild(termsOfUse);
-        termsOfUse.style.zIndex = "1100";
-        termsOfUse.style.bottom = "";
-        termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
-        termsOfUse.style.display = "";
-        cache.termsOfUse = termsOfUse;
-
-        var poweredBy = div.lastChild;
-        container.appendChild(poweredBy);
-        poweredBy.style.zIndex = "1100";
-        poweredBy.style.bottom = "";
-        poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
-        poweredBy.style.display = "";
-        cache.poweredBy = poweredBy;
-
-        this.setGMapVisibility(this.visibility);
-
-    },
-
-    /**
-     * APIMethod: onMapResize
-     */
-    onMapResize: function() {
-        if (this.visibility) {
-            google.maps.event.trigger(this.mapObject, "resize");
-        } else {
-            if (!this._resized) {
-                var layer = this;
-                google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
-                    delete layer._resized;
-                    google.maps.event.trigger(layer.mapObject, "resize");
-                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
-                });
-            }
-            this._resized = true;
-        }
-    },
-
-    /**
-     * Method: setGMapVisibility
-     * Display the GMap container and associated elements.
-     * 
      * Parameters:
-     * visible - {Boolean} Display the GMap elements.
-     */
-    setGMapVisibility: function(visible) {
-        var cache = OpenLayers.Layer.Google.cache[this.map.id];
-        if (cache) {
-            var type = this.type;
-            var layers = this.map.layers;
-            var layer;
-            for (var i=layers.length-1; i>=0; --i) {
-                layer = layers[i];
-                if (layer instanceof OpenLayers.Layer.Google &&
-                            layer.visibility === true && layer.inRange === true) {
-                    type = layer.type;
-                    visible = true;
-                    break;
-                }
-            }
-            var container = this.mapObject.getDiv();
-            if (visible === true) {
-                this.mapObject.setMapTypeId(type);                
-                container.style.left = "";
-                if (cache.termsOfUse && cache.termsOfUse.style) {
-                    cache.termsOfUse.style.left = "";
-                    cache.termsOfUse.style.display = "";
-                    cache.poweredBy.style.display = "";            
-                }
-                cache.displayed = this.id;
-            } else {
-                delete cache.displayed;
-                container.style.left = "-9999px";
-                if (cache.termsOfUse && cache.termsOfUse.style) {
-                    cache.termsOfUse.style.display = "none";
-                    // move ToU far to the left in addition to setting
-                    // display to "none", because at the end of the GMap
-                    // load sequence, display: none will be unset and ToU
-                    // would be visible after loading a map with a google
-                    // layer that is initially hidden. 
-                    cache.termsOfUse.style.left = "-9999px";
-                    cache.poweredBy.style.display = "none";
-                }
-            }
-        }
-    },
-    
-    /**
-     * Method: getMapContainer
+     * x - {float} 
+     * y - {float}
      * 
-     * Returns:
-     * {DOMElement} the GMap container's div
      */
-    getMapContainer: function() {
-        return this.mapObject.getDiv();
+    initialize: function(x, y) {
+        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+        
+        this.x = parseFloat(x);
+        this.y = parseFloat(y);
     },
-    
-  //
-  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
-  //
 
     /**
-     * APIMethod: getMapObjectBoundsFromOLBounds
+     * APIMethod: clone
      * 
-     * Parameters:
-     * olBounds - {<OpenLayers.Bounds>}
-     * 
      * Returns:
-     * {Object} A MapObject Bounds, translated from olBounds
-     *          Returns null if null value is passed in
+     * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
      */
-    getMapObjectBoundsFromOLBounds: function(olBounds) {
-        var moBounds = null;
-        if (olBounds != null) {
-            var sw = this.sphericalMercator ? 
-              this.inverseMercator(olBounds.bottom, olBounds.left) : 
-              new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
-            var ne = this.sphericalMercator ? 
-              this.inverseMercator(olBounds.top, olBounds.right) : 
-              new OpenLayers.LonLat(olBounds.top, olBounds.right);
-            moBounds = new google.maps.LatLngBounds(
-                new google.maps.LatLng(sw.lat, sw.lon),
-                new google.maps.LatLng(ne.lat, ne.lon)
-            );
+    clone: function(obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Geometry.Point(this.x, this.y);
         }
-        return moBounds;
-    },
 
+        // catch any randomly tagged-on properties
+        OpenLayers.Util.applyDefaults(obj, this);
 
-    /************************************
-     *                                  *
-     *   MapObject Interface Controls   *
-     *                                  *
-     ************************************/
-
-
-  // LonLat - Pixel Translation
-  
-    /**
-     * APIMethod: getMapObjectLonLatFromMapObjectPixel
-     * 
-     * Parameters:
-     * moPixel - {Object} MapObject Pixel format
-     * 
-     * Returns:
-     * {Object} MapObject LonLat translated from MapObject Pixel
-     */
-    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
-        var size = this.map.getSize();
-        var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
-        var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
-        var res = this.map.getResolution();
-
-        var delta_x = moPixel.x - (size.w / 2);
-        var delta_y = moPixel.y - (size.h / 2);
-    
-        var lonlat = new OpenLayers.LonLat(
-            lon + delta_x * res,
-            lat - delta_y * res
-        ); 
-
-        if (this.wrapDateLine) {
-            lonlat = lonlat.wrapDateLine(this.maxExtent);
-        }
-        return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
+        return obj;
     },
 
-    /**
-     * APIMethod: getMapObjectPixelFromMapObjectLonLat
-     * 
-     * Parameters:
-     * moLonLat - {Object} MapObject LonLat format
-     * 
-     * Returns:
-     * {Object} MapObject Pixel transtlated from MapObject LonLat
-     */
-    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
-        var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
-        var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
-        var res = this.map.getResolution();
-        var extent = this.map.getExtent();
-        var px = new OpenLayers.Pixel(
-            (1/res * (lon - extent.left)),
-            (1/res * (extent.top - lat))
-        );    
-        return this.getMapObjectPixelFromXY(px.x, px.y);
-    },
-
-  
     /** 
-     * APIMethod: setMapObjectCenter
-     * Set the mapObject to the specified center and zoom
-     * 
-     * Parameters:
-     * center - {Object} MapObject LonLat format
-     * zoom - {int} MapObject zoom format
+     * Method: calculateBounds
+     * Create a new Bounds based on the lon/lat
      */
-    setMapObjectCenter: function(center, zoom) {
-        this.mapObject.setOptions({
-            center: center,
-            zoom: zoom
-        });
+    calculateBounds: function () {
+        this.bounds = new OpenLayers.Bounds(this.x, this.y,
+                                            this.x, this.y);
     },
-   
-    
-  // Bounds
-  
-    /** 
-     * APIMethod: getMapObjectZoomFromMapObjectBounds
-     * 
-     * Parameters:
-     * moBounds - {Object} MapObject Bounds format
-     * 
-     * Returns:
-     * {Object} MapObject Zoom for specified MapObject Bounds
-     */
-    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
-        return this.mapObject.getBoundsZoomLevel(moBounds);
-    },
 
-    /************************************
-     *                                  *
-     *       MapObject Primitives       *
-     *                                  *
-     ************************************/
-
-
-  // LonLat
-    
     /**
-     * APIMethod: getMapObjectLonLatFromLonLat
-     * 
-     * Parameters:
-     * lon - {Float}
-     * lat - {Float}
-     * 
-     * Returns:
-     * {Object} MapObject LonLat built from lon and lat params
-     */
-    getMapObjectLonLatFromLonLat: function(lon, lat) {
-        var gLatLng;
-        if(this.sphericalMercator) {
-            var lonlat = this.inverseMercator(lon, lat);
-            gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
-        } else {
-            gLatLng = new google.maps.LatLng(lat, lon);
-        }
-        return gLatLng;
-    },
-    
-  // Pixel
-    
-    /**
-     * APIMethod: getMapObjectPixelFromXY
-     * 
-     * Parameters:
-     * x - {Integer}
-     * y - {Integer}
-     * 
-     * Returns:
-     * {Object} MapObject Pixel from x and y parameters
-     */
-    getMapObjectPixelFromXY: function(x, y) {
-        return new google.maps.Point(x, y);
-    },
-        
-    /**
-     * APIMethod: destroy
-     * Clean up this layer.
-     */
-    destroy: function() {
-        if (this.repositionListener) {
-            google.maps.event.removeListener(this.repositionListener);
-        }
-        if (this.repositionTimer) {
-            window.clearTimeout(this.repositionTimer);
-        }
-        OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments);
-    }
-    
-};
-/* ======================================================================
-    OpenLayers/Layer/MapGuide.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). 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/Layer/Grid.js
- */
-
-/**
- * Class: OpenLayers.Layer.MapGuide
- * Instances of OpenLayers.Layer.MapGuide are used to display
- * data from a MapGuide OS instance.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
-    /** 
-     * APIProperty: isBaseLayer
-     * {Boolean} Treat this layer as a base layer.  Default is true.
-     **/
-    isBaseLayer: true,
-    
-    /**
-     * APIProperty: 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. 
-     **/
-    singleTile: false,
-    
-    /** 
-     * APIProperty: useOverlay
-     * {Boolean} flag to indicate if the layer should be retrieved using
-     * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.
-     **/
-    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
-     */
-    TILE_PARAMS: {
-         operation: 'GETTILEIMAGE',
-         version: '1.2.0'
-    },
-
-    /**
-     * Constant: SINGLE_TILE_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs for untiled layer
-     */
-    SINGLE_TILE_PARAMS: {
-        operation: 'GETMAPIMAGE',
-        format: 'PNG',
-        locale: 'en',
-        clip: '1',
-        version: '1.0.0'
-    },
-    
-    /**
-     * Constant: OVERLAY_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs for untiled layer
-     */
-    OVERLAY_PARAMS: {
-        operation: 'GETDYNAMICMAPOVERLAYIMAGE',
-        format: 'PNG',
-        locale: 'en',
-        clip: '1',
-        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
-     **/
-    defaultSize: new OpenLayers.Size(300,300),
-
-    /**
-     * Constructor: OpenLayers.Layer.MapGuide
-     * Create a new Mapguide layer, either tiled or untiled.  
+     * APIMethod: distanceTo
+     * Calculate the closest distance between two geometries (on the x-y plane).
      *
-     * For tiled layers, the 'groupName' and 'mapDefinition' values 
-     * must be specified as parameters in the constructor.
-     *
-     * For untiled base layers, specify either combination of 'mapName' and
-     * 'session', or 'mapDefinition' and 'locale'.  
-     *
-     * 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, 
-     * so these must be adjusted accordingly in your application.  
-     * See the MapGuide example for how to set these values for MGOS.
-     *
      * Parameters:
-     * name - {String} Name of the layer displayed in the interface
-     * url - {String} Location of the MapGuide mapagent executable
-     *            (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
-     * params - {Object} hashtable of additional parameters to use. Some
-     *     parameters may require additional code on the server. The ones that
-     *     you may want to use are: 
-     *   - mapDefinition - {String} The MapGuide resource definition
-     *            (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
-     *   - locale - Locale setting 
-     *            (for untiled overlays layers only)
-     *   - mapName - {String} Name of the map as stored in the MapGuide session.
-     *          (for untiled layers with a session parameter only)
-     *   - session - { String} MapGuide session ID 
-     *            (for untiled overlays layers only)
-     *   - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
-     *   - format - Image format to be returned (for untiled overlay layers only)
-     *   - showLayers - {String} A comma separated list of GUID's for the
-     *       layers to display eg: 'cvc-xcv34,453-345-345sdf'.
-     *   - hideLayers - {String} A comma separated list of GUID's for the
-     *       layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
-     *   - showGroups - {String} A comma separated list of GUID's for the
-     *       groups to display eg: 'cvc-xcv34,453-345-345sdf'.
-     *   - hideGroups - {String} A comma separated list of GUID's for the
-     *       groups to hide eg: 'cvc-xcv34,453-345-345sdf'
-     *   - selectionXml - {String} A selection xml string Some server plumbing
-     *       is required to read such a value.
-     * options - {Ojbect} Hashtable of extra options to tag onto the layer; 
-     *          will vary depending if tiled or untiled maps are being requested
-     */
-    initialize: function(name, url, params, options) {
-        
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
-        
-        // unless explicitly set in options, if the layer is transparent, 
-        // it will be an overlay
-        if (options == null || options.isBaseLayer == null) {
-            this.isBaseLayer = ((this.transparent != "true") && 
-                                (this.transparent != true));
-        }
-
-        if (options && options.useOverlay!=null) {
-          this.useOverlay = options.useOverlay;
-        }
-        
-        //initialize for untiled layers
-        if (this.singleTile) {
-          if (this.useOverlay) {
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.OVERLAY_PARAMS
-                           );
-            if (!this.useAsyncOverlay) {
-              this.params.version = "1.0.0";
-            }
-          } else {
-            OpenLayers.Util.applyDefaults(
-                           this.params,
-                           this.SINGLE_TILE_PARAMS
-                           );
-          }         
-        } else {
-            //initialize for tiled layers
-            if (this.useHttpTile) {
-                OpenLayers.Util.applyDefaults(
-                               this.params,
-                               this.FOLDER_PARAMS
-                               );
-            } else {
-                OpenLayers.Util.applyDefaults(
-                               this.params,
-                               this.TILE_PARAMS
-                               );
-            }
-            this.setTileSize(this.defaultSize); 
-        }
-    },
-
-    /**
-     * Method: clone
-     * Create a clone of this layer
+     * geometry - {<OpenLayers.Geometry>} The target geometry.
+     * options - {Object} Optional properties for configuring the distance
+     *     calculation.
      *
-     * Returns:
-     * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
-     */
-    clone: function (obj) {
-      if (obj == null) {
-            obj = new OpenLayers.Layer.MapGuide(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.getOptions());
-      }
-      //get all additions from superclasses
-      obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-      return obj;
-    },
-
-    /**
-     * Method: addTile
-     * Creates a tile, initializes it, and adds it to the layer div. 
+     * 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.
      *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
      * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+     * {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.
      */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
-    /**
-     * Method: getURL
-     * Return a query string for this layer
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
-     *                                for the request
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also 
-     *          the passed-in bounds and appropriate tile size specified 
-     *          as parameters.
-     */
-    getURL: function (bounds) {
-        var url;
-        var center = bounds.getCenterLonLat();
-        var mapSize = this.map.getSize();
-
-        if (this.singleTile) {
-          //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with
-          //dynamic map parameters
-          var params = {
-            setdisplaydpi: OpenLayers.DOTS_PER_INCH,
-            setdisplayheight: mapSize.h*this.ratio,
-            setdisplaywidth: mapSize.w*this.ratio,
-            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 );
+    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 {
-
-          //tiled version
-          var currentRes = this.map.getResolution();
-          var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes);
-          colidx = Math.round(colidx/this.tileSize.w);
-          var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes);
-          rowidx = Math.round(rowidx/this.tileSize.h);
-
-          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;
-    },
-
-    /**
-     * Method: getFullRequestString
-     * getFullRequestString on MapGuide layers is special, because we 
-     * do a regular expression replace on ',' in parameters to '+'.
-     * This is why it is subclassed here.
-     *
-     * Parameters:
-     * altUrl - {String} Alternative base URL to use.
-     *
-     * Returns:
-     * {String} A string with the layer's url appropriately encoded for MapGuide
-     */
-    getFullRequestString:function(newParams, altUrl) {
-        // use layer's url unless altUrl passed in
-        var url = (altUrl == null) ? this.url : altUrl;
-        
-        // if url is not a string, it should be an array of strings, 
-        //  in which case we will randomly select one of them in order
-        //  to evenly distribute requests to different urls.
-        if (typeof url == "object") {
-            url = url[Math.floor(Math.random()*url.length)];
-        }   
-        // requestString always starts with url
-        var requestString = url;        
-
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        // ignore parameters that are already in the url search string
-        var urlParams = OpenLayers.Util.upperCaseObject(
-                            OpenLayers.Util.getParameters(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
+            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
+                };
             }
         }
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        /* MapGuide needs '+' seperating things like bounds/height/width.
-           Since typically this is URL encoded, we use a slight hack: we
-           depend on the list-like functionality of getParameterString to
-           leave ',' only in the case of list items (since otherwise it is
-           encoded) then do a regular expression replace on the , characters
-           to '+' */
-        paramsString = paramsString.replace(/,/g, "+");
-        
-        if (paramsString != "") {
-            var lastServerChar = url.charAt(url.length - 1);
-            if ((lastServerChar == "&") || (lastServerChar == "?")) {
-                requestString += paramsString;
-            } else {
-                if (url.indexOf('?') == -1) {
-                    //serverPath has no ? -- add one
-                    requestString += '?' + paramsString;
-                } else {
-                    //serverPath contains ?, so must already have paramsString at the end
-                    requestString += '&' + paramsString;
-                }
-            }
-        }
-        return requestString;
+        return result;
     },
-
-     /** 
-     * 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  
-     *
+     * APIMethod: equals
+     * Determine whether another geometry is equivalent to this one.  Geometries
+     *     are considered equivalent if all components have the same coordinates.
+     * 
      * Parameters:
-     * bounds - {<OpenLayers.Bound>}
-     * extent - {<OpenLayers.Bounds>}
-     * resolution - {Number}
+     * geom - {<OpenLayers.Geometry.Point>} The geometry to test. 
      *
      * Returns:
-     * Object containing properties tilelon, tilelat, tileoffsetlat,
-     * tileoffsetlat, tileoffsetx, tileoffsety
+     * {Boolean} The supplied geometry is equivalent to this geometry.
      */
-    calculateGridLayout: function(bounds, extent, resolution) {
-        var tilelon = resolution * this.tileSize.w;
-        var tilelat = resolution * this.tileSize.h;
-        
-        var offsetlon = bounds.left - extent.left;
-        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
-        var tilecolremain = offsetlon/tilelon - tilecol;
-        var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
-        
-        var offsetlat = extent.top - bounds.top + tilelat; 
-        var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
-        var tilerowremain = tilerow - offsetlat/tilelat;
-        var tileoffsety = tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.top - tilelat*tilerow;
-        
-        return { 
-          tilelon: tilelon, tilelat: tilelat,
-          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
-          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
-        };
+    equals: function(geom) {
+        var equals = false;
+        if (geom != null) {
+            equals = ((this.x == geom.x && this.y == geom.y) ||
+                      (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+        }
+        return equals;
     },
     
-    CLASS_NAME: "OpenLayers.Layer.MapGuide"
-});
-/* ======================================================================
-    OpenLayers/Layer/MapServer.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Layer/Grid.js
- */
-
-/**
- * Class: OpenLayers.Layer.MapServer
- * Instances of OpenLayers.Layer.MapServer are used to display
- * data from a MapServer CGI instance.
- *
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
     /**
-     * Constant: DEFAULT_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs 
-     */
-    DEFAULT_PARAMS: {
-        mode: "map",
-        map_imagetype: "png"
-    },
-
-    /**
-     * Constructor: OpenLayers.Layer.MapServer
-     * Create a new MapServer layer object
+     * Method: toShortString
      *
-     * Parameters:
-     * name - {String} A name for the layer
-     * url - {String} Base url for the MapServer CGI
-     *       (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)
-     * params - {Object} An object with key/value pairs representing the
-     *          GetMap query string parameters and parameter values.
-     * options - {Ojbect} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, params, options) {
-        var newArguments = [];
-        newArguments.push(name, url, params, options);
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-
-        this.params = OpenLayers.Util.applyDefaults(
-            this.params, this.DEFAULT_PARAMS
-        );
-
-        // unless explicitly set in options, if the layer is transparent, 
-        // it will be an overlay
-        if (options == null || options.isBaseLayer == null) {
-            this.isBaseLayer = ((this.params.transparent != "true") && 
-                                (this.params.transparent != true));
-        }
-    },
-
-    /**
-     * Method: clone
-     * Create a clone of this layer
-     *
      * Returns:
-     * {<OpenLayers.Layer.MapServer>} An exact clone of this layer
+     * {String} Shortened String representation of Point object. 
+     *         (ex. <i>"5, 42"</i>)
      */
-    clone: function (obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Layer.MapServer(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.getOptions());
-        }
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-
-        return obj;
+    toShortString: function() {
+        return (this.x + ", " + this.y);
     },
-
-    /**
-     * Method: addTile
-     * Creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
     
     /**
-     * Method: getURL
-     * Return a query string for this layer
+     * APIMethod: move
+     * 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:
-     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
-     *                                for the request
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also 
-     *          the passed-in bounds and appropriate tile size specified 
-     *          as parameters.
+     * x - {Float} Distance to move geometry in positive x direction. 
+     * y - {Float} Distance to move geometry in positive y direction.
      */
-    getURL: function (bounds) {
-        bounds = this.adjustBounds(bounds);
-        // Make a list, so that getFullRequestString uses literal "," 
-        var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top];
-
-        var imageSize = this.getImageSize(); 
-        
-        // make lists, so that literal ','s are used 
-        var url = this.getFullRequestString(
-                     {mapext:   extent,
-                      imgext:   extent,
-                      map_size: [imageSize.w, imageSize.h],
-                      imgx:     imageSize.w / 2,
-                      imgy:     imageSize.h / 2,
-                      imgxy:    [imageSize.w, imageSize.h]
-                      });
-        
-        return url;
+    move: function(x, y) {
+        this.x = this.x + x;
+        this.y = this.y + y;
+        this.clearBounds();
     },
-    
-    /** 
-     * Method: getFullRequestString
-     * combine the layer's url with its params and these newParams. 
-     *   
-     * Parameter:
-     * newParams - {Object} New parameters that should be added to the 
-     *                      request string.
-     * altUrl - {String} (optional) Replace the URL in the full request  
-     *                              string with the provided URL.
-     * 
-     * Returns: 
-     * {String} A string with the layer's url and parameters embedded in it.
-     */
-    getFullRequestString:function(newParams, altUrl) {
-        // use layer's url unless altUrl passed in
-        var url = (altUrl == null) ? this.url : altUrl;
-        
-        // create a new params hashtable with all the layer params and the 
-        // new params together. then convert to string
-        var allParams = OpenLayers.Util.extend({}, this.params);
-        allParams = OpenLayers.Util.extend(allParams, newParams);
-        var paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // if url is not a string, it should be an array of strings, 
-        // in which case we will deterministically select one of them in 
-        // order to evenly distribute requests to different urls.
-        if (url instanceof Array) {
-            url = this.selectUrl(paramsString, url);
-        }   
-        
-        // ignore parameters that are already in the url search string
-        var urlParams = OpenLayers.Util.upperCaseObject(
-                            OpenLayers.Util.getParameters(url));
-        for(var key in allParams) {
-            if(key.toUpperCase() in urlParams) {
-                delete allParams[key];
-            }
-        }
-        paramsString = OpenLayers.Util.getParameterString(allParams);
-        
-        // requestString always starts with url
-        var requestString = url;        
 
-        // MapServer needs '+' seperating things like bounds/height/width.
-        //   Since typically this is URL encoded, we use a slight hack: we
-        //  depend on the list-like functionality of getParameterString to
-        //  leave ',' only in the case of list items (since otherwise it is
-        //  encoded) then do a regular expression replace on the , characters
-        //  to '+'
-        //
-        paramsString = paramsString.replace(/,/g, "+");
-        
-        if (paramsString != "") {
-            var lastServerChar = url.charAt(url.length - 1);
-            if ((lastServerChar == "&") || (lastServerChar == "?")) {
-                requestString += paramsString;
-            } else {
-                if (url.indexOf('?') == -1) {
-                    //serverPath has no ? -- add one
-                    requestString += '?' + paramsString;
-                } else {
-                    //serverPath contains ?, so must already have paramsString at the end
-                    requestString += '&' + paramsString;
-                }
-            }
-        }
-        return requestString;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.MapServer"
-});
-/* ======================================================================
-    OpenLayers/Layer/WMS.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Layer/Grid.js
- * @requires OpenLayers/Tile/Image.js
- */
-
-/**
- * Class: OpenLayers.Layer.WMS
- * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web
- *     Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>
- *     constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
-
     /**
-     * Constant: DEFAULT_PARAMS
-     * {Object} Hashtable of default parameter key/value pairs 
-     */
-    DEFAULT_PARAMS: { service: "WMS",
-                      version: "1.1.1",
-                      request: "GetMap",
-                      styles: "",
-                      exceptions: "application/vnd.ogc.se_inimage",
-                      format: "image/jpeg"
-                     },
-    
-    /**
-     * Property: reproject
-     * *Deprecated*. See http://trac.openlayers.org/wiki/SphericalMercator
-     * for information on the replacement for this functionality. 
-     * {Boolean} Try to reproject this layer if its coordinate reference system
-     *           is different than that of the base layer.  Default is true.  
-     *           Set this in the layer options.  Should be set to false in 
-     *           most cases.
-     */
-    reproject: false,
- 
-    /**
-     * APIProperty: isBaseLayer
-     * {Boolean} Default is true for WMS layer
-     */
-    isBaseLayer: true,
-    
-    /**
-     * APIProperty: encodeBBOX
-     * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', 
-     * but some services want it that way. Default false.
-     */
-    encodeBBOX: false,
-    
-    /** 
-     * 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,
-    
-    /**
-     * Property: yx
-     * {Object} Keys in this object are EPSG codes for which the axis order
-     *     is to be reversed (yx instead of xy, LatLon instead of LonLat), with
-     *     true as value. This is only relevant for WMS versions >= 1.3.0.
-     */
-    yx: {'EPSG:4326': true},
-    
-    /**
-     * Constructor: OpenLayers.Layer.WMS
-     * Create a new WMS layer object
+     * APIMethod: rotate
+     * Rotate a point around another.
      *
-     * Example:
-     * (code)
-     * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
-     *                                    "http://wms.jpl.nasa.gov/wms.cgi", 
-     *                                    {layers: "modis,global_mosaic"});
-     * (end)
-     *
      * Parameters:
-     * name - {String} A name for the layer
-     * url - {String} Base url for the WMS
-     *                (e.g. http://wms.jpl.nasa.gov/wms.cgi)
-     * params - {Object} An object with key/value pairs representing the
-     *                   GetMap query string parameters and parameter values.
-     * options - {Ojbect} Hashtable of extra options to tag onto the layer
+     * angle - {Float} Rotation angle in degrees (measured counterclockwise
+     *                 from the positive x-axis)
+     * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
      */
-    initialize: function(name, url, params, options) {
-        var newArguments = [];
-        //uppercase params
-        params = OpenLayers.Util.upperCaseObject(params);
-        if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {
-            params.EXCEPTIONS = "INIMAGE";
-        } 
-        newArguments.push(name, url, params, options);
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-        OpenLayers.Util.applyDefaults(
-                       this.params, 
-                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
-                       );
-
-
-        //layer is transparent        
-        if (!this.noMagic && this.params.TRANSPARENT && 
-            this.params.TRANSPARENT.toString().toLowerCase() == "true") {
-            
-            // unless explicitly set in options, make layer an overlay
-            if ( (options == null) || (!options.isBaseLayer) ) {
-                this.isBaseLayer = false;
-            } 
-            
-            // jpegs can never be transparent, so intelligently switch the 
-            //  format, depending on teh browser's capabilities
-            if (this.params.FORMAT == "image/jpeg") {
-                this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif"
-                                                                 : "image/png";
-            }
-        }
-
-    },    
-
-    /**
-     * Method: destroy
-     * Destroy this layer
-     */
-    destroy: function() {
-        // for now, nothing special to do here. 
-        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);  
+    rotate: function(angle, origin) {
+        angle *= Math.PI / 180;
+        var radius = this.distanceTo(origin);
+        var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+        this.x = origin.x + (radius * Math.cos(theta));
+        this.y = origin.y + (radius * Math.sin(theta));
+        this.clearBounds();
     },
-
     
     /**
-     * Method: clone
-     * Create a clone of this layer
+     * APIMethod: getCentroid
      *
      * Returns:
-     * {<OpenLayers.Layer.WMS>} An exact clone of this layer
+     * {<OpenLayers.Geometry.Point>} The centroid of the collection
      */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.WMS(this.name,
-                                           this.url,
-                                           this.params,
-                                           this.getOptions());
-        }
-
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-
-        return obj;
-    },    
-    
-    /**
-     * APIMethod: reverseAxisOrder
-     * Returns true if the axis order is reversed for the WMS version and
-     * projection of the layer.
-     * 
-     * Returns:
-     * {Boolean} true if the axis order is reversed, false otherwise.
-     */
-    reverseAxisOrder: function() {
-        return (parseFloat(this.params.VERSION) >= 1.3 && 
-            !!this.yx[this.map.getProjectionObject().getCode()]);
+    getCentroid: function() {
+        return new OpenLayers.Geometry.Point(this.x, this.y);
     },
-    
-    /**
-     * Method: getURL
-     * Return a GetMap query string for this layer
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
-     *                                request.
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also the
-     *          passed-in bounds and appropriate tile size specified as 
-     *          parameters.
-     */
-    getURL: function (bounds) {
-        bounds = this.adjustBounds(bounds);
-        
-        var imageSize = this.getImageSize();
-        var newParams = {};
-        // WMS 1.3 introduced axis order
-        var reverseAxisOrder = this.reverseAxisOrder();
-        newParams.BBOX = this.encodeBBOX ?
-            bounds.toBBOX(null, reverseAxisOrder) :
-            bounds.toArray(reverseAxisOrder);
-        newParams.WIDTH = imageSize.w;
-        newParams.HEIGHT = imageSize.h;
-        var requestString = this.getFullRequestString(newParams);
-        return requestString;
-    },
 
     /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
+     * 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
+     *     more useful on geometry collection subclasses.
      *
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
+     * scale - {Float} Ratio of the new distance from the origin to the old
+     *                 distance from the origin.  A scale of 2 doubles the
+     *                 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.Tile.Image>} The added OpenLayers.Tile.Image
+     * {OpenLayers.Geometry} - The current geometry. 
      */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
+    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;
     },
-
-    /**
-     * APIMethod: mergeNewParams
-     * Catch changeParams and uppercase the new params to be merged in
-     *     before calling changeParams on the super class.
-     * 
-     *     Once params have been changed, the tiles will be reloaded with
-     *     the new parameters.
-     * 
-     * Parameters:
-     * newParams - {Object} Hashtable of new params to use
-     */
-    mergeNewParams:function(newParams) {
-        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
-        var newArguments = [upperParams];
-        return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, 
-                                                             newArguments);
-    },
-
-    /** 
-     * APIMethod: getFullRequestString
-     * Combine the layer's url with its params and these newParams. 
-     *   
-     *     Add the SRS parameter from projection -- this is probably
-     *     more eloquently done via a setProjection() method, but this 
-     *     works for now and always.
-     *
-     * Parameters:
-     * newParams - {Object}
-     * altUrl - {String} Use this as the url instead of the layer's url
-     * 
-     * Returns:
-     * {String} 
-     */
-    getFullRequestString:function(newParams, altUrl) {
-        var projectionCode = this.map.getProjection();
-        var value = (projectionCode == "none") ? null : projectionCode
-        if (parseFloat(this.params.VERSION) >= 1.3) {
-            this.params.CRS = value;
-        } else {
-            this.params.SRS = value;
-        }
-
-        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
-                                                    this, arguments);
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.WMS"
-});
-/* ======================================================================
-    OpenLayers/Layer/XYZ.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Layer/Grid.js
- * @requires OpenLayers/Tile/Image.js
- */
-
-/** 
- * Class: OpenLayers.Layer.XYZ
- * The XYZ class is designed to make it easier for people who have tiles
- * arranged by a standard XYZ grid. 
- * 
- * Inherits from:
- *  - <OpenLayers.Layer.Grid>
- */
-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
     
     /**
-     * APIProperty: isBaseLayer
-     * Default is true, as this is designed to be a base tile source. 
-     */
-    isBaseLayer: true,
-    
-    /**
-     * APIProperty: sphericalMecator
-     * Whether the tile extents should be set to the defaults for 
-     *    spherical mercator. Useful for things like OpenStreetMap.
-     *    Default is false, except for the OSM subclass.
-     */
-    sphericalMercator: false,
-
-    /**
-     * APIProperty: zoomOffset
-     * {Number} If your cache has more zoom levels than you want to provide
-     *     access to with this layer, supply a zoomOffset.  This zoom offset
-     *     is added to the current map zoom level to determine the level
-     *     for a requested tile.  For example, if you supply a zoomOffset
-     *     of 3, when the map is at the zoom 0, tiles will be requested from
-     *     level 3 of your cache.  Default is 0 (assumes cache level and map
-     *     zoom are equivalent).
-     */
-    zoomOffset: 0,
-    
-    /**
-     * Constructor: OpenLayers.Layer.XYZ
+     * APIMethod: intersects
+     * Determine if the input geometry intersects this one.
      *
      * Parameters:
-     * name - {String}
-     * url - {String}
-     * options - {Object} Hashtable of extra options to tag onto the layer
-     */
-    initialize: function(name, url, options) {
-        if (options && options.sphericalMercator || this.sphericalMercator) {
-            options = OpenLayers.Util.extend({
-                maxExtent: new OpenLayers.Bounds(
-                    -128 * 156543.0339,
-                    -128 * 156543.0339,
-                    128 * 156543.0339,
-                    128 * 156543.0339
-                ),
-                maxResolution: 156543.0339,
-                numZoomLevels: 19,
-                units: "m",
-                projection: "EPSG:900913"
-            }, options);
-        }
-        url = url || this.url;
-        name = name || this.name;
-        var newArguments = [name, url, {}, options];
-        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
-    },
-    
-    /**
-     * APIMethod: clone
-     * Create a clone of this layer
+     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
      *
-     * Parameters:
-     * obj - {Object} Is this ever used?
-     * 
      * Returns:
-     * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
+     * {Boolean} The input geometry intersects this one.
      */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.XYZ(this.name,
-                                            this.url,
-                                            this.getOptions());
-        }
-
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
-
-        return obj;
-    },    
-
-    /**
-     * Method: getUrl
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     *
-     * Returns:
-     * {String} A string with the layer's url and parameters and also the
-     *          passed-in bounds and appropriate tile size specified as
-     *          parameters
-     */
-    getURL: function (bounds) {
-        var res = this.map.getResolution();
-        var x = Math.round((bounds.left - this.maxExtent.left) 
-            / (res * this.tileSize.w));
-        var y = Math.round((this.maxExtent.top - bounds.top) 
-            / (res * this.tileSize.h));
-        var z = this.map.getZoom() + this.zoomOffset;
-
-        var url = this.url;
-        var s = '' + x + y + z;
-        if (url instanceof Array)
-        {
-            url = this.selectUrl(s, url);
-        }
-        
-        var path = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
-
-        return path;
-    },
-    
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-     
-    /* APIMethod: setMap
-     * When the layer is added to a map, then we can fetch our origin 
-     *    (if we don't have one.) 
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>}
-     */
-    setMap: function(map) {
-        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
-        if (!this.tileOrigin) { 
-            this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
-                                                this.maxExtent.bottom);
-        }                                       
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.XYZ"
-});
-
-
-/**
- * Class: OpenLayers.Layer.OSM
- * A class to access OpenStreetMap tiles. By default, uses the OpenStreetMap
- *    hosted tile.openstreetmap.org 'Mapnik' tileset. If you wish to use
- *    tiles at home / osmarender layer instead, you can pass a layer like:
- * 
- * (code)
- *     new OpenLayers.Layer.OSM("t at h", 
- *       "http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png"); 
- * (end)
- *
- * This layer defaults to Spherical Mercator.
- * 
- * Inherits from:
- *  - <OpenLayers.Layer.XYZ>
- */
-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
-     name: "OpenStreetMap",
-     attribution: "Data CC-By-SA by <a href='http://openstreetmap.org/'>OpenStreetMap</a>",
-     sphericalMercator: true,
-     url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
-     clone: function(obj) {
-         if (obj == null) {
-             obj = new OpenLayers.Layer.OSM(
-                 this.name, this.url, this.getOptions());
-         }
-         obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
-         return obj;
-     },
-     CLASS_NAME: "OpenLayers.Layer.OSM"
-});
-/* ======================================================================
-    OpenLayers/Rule.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Util.js
- * @requires OpenLayers/Style.js
- * @requires OpenLayers/Symbolizer/Point.js
- * @requires OpenLayers/Symbolizer/Line.js
- * @requires OpenLayers/Symbolizer/Polygon.js
- * @requires OpenLayers/Symbolizer/Text.js
- * @requires OpenLayers/Symbolizer/Raster.js
- */
-
-/**
- * Class: OpenLayers.Rule
- * This class represents an SLD Rule, as being used for rule-based SLD styling.
- */
-OpenLayers.Rule = OpenLayers.Class({
-    
-    /**
-     * Property: id
-     * {String} A unique id for this session.
-     */
-    id: null,
-    
-    /**
-     * APIProperty: name
-     * {String} name of this rule
-     */
-    name: null,
-    
-    /**
-     * Property: title
-     * {String} Title of this rule (set if included in SLD)
-     */
-    title: null,
-    
-    /**
-     * Property: description
-     * {String} Description of this rule (set if abstract is included in SLD)
-     */
-    description: null,
-
-    /**
-     * Property: context
-     * {Object} An optional object with properties that the rule should be
-     * evaluated against. If no context is specified, feature.attributes will
-     * be used.
-     */
-    context: null,
-    
-    /**
-     * Property: filter
-     * {<OpenLayers.Filter>} Optional filter for the rule.
-     */
-    filter: null,
-
-    /**
-     * Property: elseFilter
-     * {Boolean} Determines whether this rule is only to be applied only if
-     * no other rules match (ElseFilter according to the SLD specification). 
-     * Default is false.  For instances of OpenLayers.Rule, if elseFilter is
-     * false, the rule will always apply.  For subclasses, the else property is 
-     * ignored.
-     */
-    elseFilter: false,
-    
-    /**
-     * Property: symbolizer
-     * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
-     * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
-     * latter if useful if it is required to style e.g. vertices of a line
-     * with a point symbolizer. Note, however, that this is not implemented
-     * yet in OpenLayers, but it is the way how symbolizers are defined in
-     * SLD.
-     */
-    symbolizer: null,
-    
-    /**
-     * Property: symbolizers
-     * {Array} Collection of symbolizers associated with this rule.  If 
-     *     provided at construction, the symbolizers array has precedence
-     *     over the deprecated symbolizer property.  Note that multiple 
-     *     symbolizers are not currently supported by the vector renderers.
-     *     Rules with multiple symbolizers are currently only useful for
-     *     maintaining elements in an SLD document.
-     */
-    symbolizers: null,
-    
-    /**
-     * APIProperty: minScaleDenominator
-     * {Number} or {String} minimum scale at which to draw the feature.
-     * In the case of a String, this can be a combination of text and
-     * propertyNames in the form "literal ${propertyName}"
-     */
-    minScaleDenominator: null,
-
-    /**
-     * APIProperty: maxScaleDenominator
-     * {Number} or {String} maximum scale at which to draw the feature.
-     * In the case of a String, this can be a combination of text and
-     * propertyNames in the form "literal ${propertyName}"
-     */
-    maxScaleDenominator: null,
-    
-    /** 
-     * Constructor: OpenLayers.Rule
-     * Creates a Rule.
-     *
-     * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *           rule
-     * 
-     * Returns:
-     * {<OpenLayers.Rule>}
-     */
-    initialize: function(options) {
-        this.symbolizer = {};
-        OpenLayers.Util.extend(this, options);
-        if (this.symbolizers) {
-            delete this.symbolizer;
-        }
-        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
-    },
-
-    /** 
-     * APIMethod: destroy
-     * nullify references to prevent circular references and memory leaks
-     */
-    destroy: function() {
-        for (var i in this.symbolizer) {
-            this.symbolizer[i] = null;
-        }
-        this.symbolizer = null;
-        delete this.symbolizers;
-    },
-    
-    /**
-     * APIMethod: evaluate
-     * evaluates this rule for a specific feature
-     * 
-     * Parameters:
-     * feature - {<OpenLayers.Feature>} feature to apply the rule to.
-     * 
-     * Returns:
-     * {Boolean} true if the rule applies, false if it does not.
-     * This rule is the default rule and always returns true.
-     */
-    evaluate: function(feature) {
-        var context = this.getContext(feature);
-        var applies = true;
-
-        if (this.minScaleDenominator || this.maxScaleDenominator) {
-            var scale = feature.layer.map.getScale();
-        }
-        
-        // check if within minScale/maxScale bounds
-        if (this.minScaleDenominator) {
-            applies = scale >= OpenLayers.Style.createLiteral(
-                    this.minScaleDenominator, context);
-        }
-        if (applies && this.maxScaleDenominator) {
-            applies = scale < OpenLayers.Style.createLiteral(
-                    this.maxScaleDenominator, context);
-        }
-        
-        // check if optional filter applies
-        if(applies && this.filter) {
-            // feature id filters get the feature, others get the context
-            if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
-                applies = this.filter.evaluate(feature);
-            } else {
-                applies = this.filter.evaluate(context);
-            }
-        }
-
-        return applies;
-    },
-    
-    /**
-     * Method: getContext
-     * Gets the context for evaluating this rule
-     * 
-     * Paramters:
-     * feature - {<OpenLayers.Feature>} feature to take the context from if
-     *           none is specified.
-     */
-    getContext: function(feature) {
-        var context = this.context;
-        if (!context) {
-            context = feature.attributes || feature.data;
-        }
-        if (typeof this.context == "function") {
-            context = this.context(feature);
-        }
-        return context;
-    },
-    
-    /**
-     * APIMethod: clone
-     * Clones this rule.
-     * 
-     * Returns:
-     * {<OpenLayers.Rule>} Clone of this rule.
-     */
-    clone: function() {
-        var options = OpenLayers.Util.extend({}, this);
-        if (this.symbolizers) {
-            // clone symbolizers
-            var len = this.symbolizers.length;
-            options.symbolizers = new Array(len);
-            for (var i=0; i<len; ++i) {
-                options.symbolizers[i] = this.symbolizers[i].clone();
-            }
+    intersects: function(geometry) {
+        var intersect = false;
+        if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
+            intersect = this.equals(geometry);
         } else {
-            // clone symbolizer
-            options.symbolizer = {};
-            var value, type;
-            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;
-                }
-            }
+            intersect = geometry.intersects(this);
         }
-        // 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);
+        return intersect;
     },
-        
-    CLASS_NAME: "OpenLayers.Rule"
-});
-/* ======================================================================
-    OpenLayers/StyleMap.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Style.js
- * @requires OpenLayers/Feature/Vector.js
- */
- 
-/**
- * Class: OpenLayers.StyleMap
- */
-OpenLayers.StyleMap = OpenLayers.Class({
     
     /**
-     * Property: styles
-     * Hash of {<OpenLayers.Style>}, keyed by names of well known
-     * rendering intents (e.g. "default", "temporary", "select", "delete").
-     */
-    styles: null,
-    
-    /**
-     * Property: extendDefault
-     * {Boolean} if true, every render intent will extend the symbolizers
-     * specified for the "default" intent at rendering time. Otherwise, every
-     * rendering intent will be treated as a completely independent style.
-     */
-    extendDefault: true,
-    
-    /**
-     * Constructor: OpenLayers.StyleMap
+     * APIMethod: transform
+     * Translate the x,y properties of the point from source to dest.
      * 
      * Parameters:
-     * style   - {Object} Optional. Either a style hash, or a style object, or
-     *           a hash of style objects (style hashes) keyed by rendering
-     *           intent. If just one style hash or style object is passed,
-     *           this will be used for all known render intents (default,
-     *           select, temporary)
-     * options - {Object} optional hash of additional options for this
-     *           instance
-     */
-    initialize: function (style, options) {
-        this.styles = {
-            "default": new OpenLayers.Style(
-                OpenLayers.Feature.Vector.style["default"]),
-            "select": new OpenLayers.Style(
-                OpenLayers.Feature.Vector.style["select"]),
-            "temporary": new OpenLayers.Style(
-                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
-        // into parts of stylemap.
-        if(style instanceof OpenLayers.Style) {
-            // user passed a style object
-            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) {
-                    // user passed a hash of style objects
-                    this.styles[key] = style[key];
-                } else if(typeof style[key] == "object") {
-                    // user passsed a hash of style hashes
-                    this.styles[key] = new OpenLayers.Style(style[key]);
-                } else {
-                    // user passed a style hash (i.e. symbolizer)
-                    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;
-                }
-            }
-        }
-        OpenLayers.Util.extend(this, options);
-    },
-
-    /**
-     * Method: destroy
-     */
-    destroy: function() {
-        for(var key in this.styles) {
-            this.styles[key].destroy();
-        }
-        this.styles = null;
-    },
-    
-    /**
-     * Method: createSymbolizer
-     * Creates the symbolizer for a feature for a render intent.
+     * source - {<OpenLayers.Projection>} 
+     * dest - {<OpenLayers.Projection>}
      * 
-     * Parameters:
-     * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
-     *           of the intended style against.
-     * intent  - {String} The intent determines the symbolizer that will be
-     *           used to draw the feature. Well known intents are "default"
-     *           (for just drawing the features), "select" (for selected
-     *           features) and "temporary" (for drawing features).
-     * 
      * Returns:
-     * {Object} symbolizer hash
+     * {<OpenLayers.Geometry>} 
      */
-    createSymbolizer: function(feature, intent) {
-        if(!feature) {
-            feature = new OpenLayers.Feature.Vector();
-        }
-        if(!this.styles[intent]) {
-            intent = "default";
-        }
-        feature.renderIntent = intent;
-        var defaultSymbolizer = {};
-        if(this.extendDefault && intent != "default") {
-            defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
-        }
-        return OpenLayers.Util.extend(defaultSymbolizer,
-            this.styles[intent].createSymbolizer(feature));
+    transform: function(source, dest) {
+        if ((source && dest)) {
+            OpenLayers.Projection.transform(
+                this, source, dest); 
+            this.bounds = null;
+        }       
+        return this;
     },
-    
-    /**
-     * Method: addUniqueValueRules
-     * Convenience method to create comparison rules for unique values of a
-     * property. The rules will be added to the style object for a specified
-     * rendering intent. This method is a shortcut for creating something like
-     * the "unique value legends" familiar from well known desktop GIS systems
-     * 
-     * Parameters:
-     * renderIntent - {String} rendering intent to add the rules to
-     * property     - {String} values of feature attributes to create the
-     *                rules for
-     * symbolizers  - {Object} Hash of symbolizers, keyed by the desired
-     *                property values 
-     * context      - {Object} An optional object with properties that
-     *                symbolizers' property values should be evaluated
-     *                against. If no context is specified, feature.attributes
-     *                will be used
-     */
-    addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
-        var rules = [];
-        for (var value in symbolizers) {
-            rules.push(new OpenLayers.Rule({
-                symbolizer: symbolizers[value],
-                context: context,
-                filter: new OpenLayers.Filter.Comparison({
-                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
-                    property: property,
-                    value: value
-                })
-            }));
-        }
-        this.styles[renderIntent].addRules(rules);
-    },
 
-    CLASS_NAME: "OpenLayers.StyleMap"
-});
-/* ======================================================================
-    OpenLayers/Filter/Comparison.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Filter.js
- * @requires OpenLayers/Console.js
- */
-
-/**
- * Class: OpenLayers.Filter.Comparison
- * This class represents a comparison filter.
- * 
- * Inherits from
- * - <OpenLayers.Filter>
- */
-OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
-
     /**
-     * APIProperty: type
-     * {String} type: type of the comparison. This is one of
-     * - OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
-     * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
-     * - OpenLayers.Filter.Comparison.LESS_THAN                = "<";
-     * - OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
-     * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
-     * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
-     * - OpenLayers.Filter.Comparison.BETWEEN                  = "..";
-     * - OpenLayers.Filter.Comparison.LIKE                     = "~"; 
-     */
-    type: null,
-    
-    /**
-     * APIProperty: property
-     * {String}
-     * name of the context property to compare
-     */
-    property: null,
-    
-    /**
-     * APIProperty: value
-     * {Number} or {String}
-     * comparison value for binary comparisons. In the case of a String, this
-     * can be a combination of text and propertyNames in the form
-     * "literal ${propertyName}"
-     */
-    value: null,
-    
-    /**
-     * Property: matchCase
-     * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
-     *     comparisons.  The Filter Encoding 1.1 specification added a matchCase
-     *     attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
-     *     elements.  This property will be serialized with those elements only
-     *     if using the v1.1.0 filter format. However, when evaluating filters
-     *     here, the matchCase property will always be respected (for EQUAL_TO
-     *     and NOT_EQUAL_TO).  Default is true.
-     */
-    matchCase: true,
-    
-    /**
-     * APIProperty: lowerBoundary
-     * {Number} or {String}
-     * lower boundary for between comparisons. In the case of a String, this
-     * can be a combination of text and propertyNames in the form
-     * "literal ${propertyName}"
-     */
-    lowerBoundary: null,
-    
-    /**
-     * APIProperty: upperBoundary
-     * {Number} or {String}
-     * upper boundary for between comparisons. In the case of a String, this
-     * can be a combination of text and propertyNames in the form
-     * "literal ${propertyName}"
-     */
-    upperBoundary: null,
-
-    /** 
-     * Constructor: OpenLayers.Filter.Comparison
-     * Creates a comparison rule.
+     * APIMethod: getVertices
+     * Return a list of all points in this geometry.
      *
      * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *           rule
-     * 
-     * Returns:
-     * {<OpenLayers.Filter.Comparison>}
-     */
-    initialize: function(options) {
-        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
-    },
-
-    /**
-     * APIMethod: evaluate
-     * Evaluates this filter in a specific context.
-     * 
-     * Parameters:
-     * context - {Object} Context to use in evaluating the filter.  If a vector
-     *     feature is provided, the feature.attributes will be used as context.
-     * 
-     * Returns:
-     * {Boolean} The filter applies.
-     */
-    evaluate: function(context) {
-        if (context instanceof OpenLayers.Feature.Vector) {
-            context = context.attributes;
-        }
-        var result = false;
-        var got = context[this.property];
-        switch(this.type) {
-            case OpenLayers.Filter.Comparison.EQUAL_TO:
-                var exp = this.value;
-                if(!this.matchCase &&
-                   typeof got == "string" && typeof exp == "string") {
-                    result = (got.toUpperCase() == exp.toUpperCase());
-                } else {
-                    result = (got == exp);
-                }
-                break;
-            case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
-                var exp = this.value;
-                if(!this.matchCase &&
-                   typeof got == "string" && typeof exp == "string") {
-                    result = (got.toUpperCase() != exp.toUpperCase());
-                } else {
-                    result = (got != exp);
-                }
-                break;
-            case OpenLayers.Filter.Comparison.LESS_THAN:
-                result = got < this.value;
-                break;
-            case OpenLayers.Filter.Comparison.GREATER_THAN:
-                result = got > this.value;
-                break;
-            case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
-                result = got <= this.value;
-                break;
-            case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
-                result = got >= this.value;
-                break;
-            case OpenLayers.Filter.Comparison.BETWEEN:
-                result = (got >= this.lowerBoundary) &&
-                    (got <= this.upperBoundary);
-                break;
-            case OpenLayers.Filter.Comparison.LIKE:
-                var regexp = new RegExp(this.value, "gi");
-                result = regexp.test(got);
-                break;
-        }
-        return result;
-    },
-    
-    /**
-     * APIMethod: value2regex
-     * Converts the value of this rule into a regular expression string,
-     * according to the wildcard characters specified. This method has to
-     * be called after instantiation of this class, if the value is not a
-     * regular expression already.
-     * 
-     * Parameters:
-     * wildCard   - {<Char>} wildcard character in the above value, default
-     *              is "*"
-     * singleChar - {<Char>) single-character wildcard in the above value
-     *              default is "."
-     * escape     - {<Char>) escape character in the above value, default is
-     *              "!"
-     * 
-     * Returns:
-     * {String} regular expression string
-     */
-    value2regex: function(wildCard, singleChar, escapeChar) {
-        if (wildCard == ".") {
-            var msg = "'.' is an unsupported wildCard character for "+
-                    "OpenLayers.Filter.Comparison";
-            OpenLayers.Console.error(msg);
-            return null;
-        }
-        
-
-        // set UMN MapServer defaults for unspecified parameters
-        wildCard = wildCard ? wildCard : "*";
-        singleChar = singleChar ? singleChar : ".";
-        escapeChar = escapeChar ? escapeChar : "!";
-        
-        this.value = this.value.replace(
-                new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
-        this.value = this.value.replace(
-                new RegExp("\\"+singleChar, "g"), ".");
-        this.value = this.value.replace(
-                new RegExp("\\"+wildCard, "g"), ".*");
-        this.value = this.value.replace(
-                new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
-        this.value = this.value.replace(
-                new RegExp("\\\\\\.", "g"), "\\"+singleChar);
-        
-        return this.value;
-    },
-    
-    /**
-     * Method: regex2value
-     * Convert the value of this rule from a regular expression string into an
-     *     ogc literal string using a wildCard of *, a singleChar of ., and an
-     *     escape of !.  Leaves the <value> property unmodified.
-     * 
-     * Returns:
-     * {String} A string value.
-     */
-    regex2value: function() {
-        
-        var value = this.value;
-        
-        // replace ! with !!
-        value = value.replace(/!/g, "!!");
-
-        // replace \. with !. (watching out for \\.)
-        value = value.replace(/(\\)?\\\./g, function($0, $1) {
-            return $1 ? $0 : "!.";
-        });
-        
-        // replace \* with #* (watching out for \\*)
-        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
-            return $1 ? $0 : "!*";
-        });
-        
-        // replace \\ with \
-        value = value.replace(/\\\\/g, "\\");
-
-        // convert .* to * (the sequence #.* is not allowed)
-        value = value.replace(/\.\*/g, "*");
-        
-        return value;
-    },
-    
-    /**
-     * APIMethod: clone
-     * Clones this filter.
-     * 
-     * Returns:
-     * {<OpenLayers.Filter.Comparison>} Clone of this filter.
-     */
-    clone: function() {
-        return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
-    },
-    
-    CLASS_NAME: "OpenLayers.Filter.Comparison"
-});
-
-
-OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
-OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
-OpenLayers.Filter.Comparison.LESS_THAN                = "<";
-OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
-OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
-OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
-OpenLayers.Filter.Comparison.BETWEEN                  = "..";
-OpenLayers.Filter.Comparison.LIKE                     = "~";
-/* ======================================================================
-    OpenLayers/Filter/Logical.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Filter.js
- */
-
-/**
- * Class: OpenLayers.Filter.Logical
- * This class represents ogc:And, ogc:Or and ogc:Not rules.
- * 
- * Inherits from
- * - <OpenLayers.Filter>
- */
-OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
-
-    /**
-     * APIProperty: filters
-     * {Array(<OpenLayers.Filter>)} Child filters for this filter.
-     */
-    filters: null, 
-     
-    /**
-     * APIProperty: type
-     * {String} type of logical operator. Available types are:
-     * - OpenLayers.Filter.Logical.AND = "&&";
-     * - OpenLayers.Filter.Logical.OR  = "||";
-     * - OpenLayers.Filter.Logical.NOT = "!";
-     */
-    type: null,
-
-    /** 
-     * Constructor: OpenLayers.Filter.Logical
-     * Creates a logical filter (And, Or, Not).
+     * 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.
      *
-     * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *     filter.
-     * 
      * Returns:
-     * {<OpenLayers.Filter.Logical>}
+     * {Array} A list of all vertices in the geometry.
      */
-    initialize: function(options) {
-        this.filters = [];
-        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+    getVertices: function(nodes) {
+        return [this];
     },
-    
-    /** 
-     * APIMethod: destroy
-     * Remove reference to child filters.
-     */
-    destroy: function() {
-        this.filters = null;
-        OpenLayers.Filter.prototype.destroy.apply(this);
-    },
 
-    /**
-     * APIMethod: evaluate
-     * Evaluates this filter in a specific context.
-     * 
-     * Parameters:
-     * context - {Object} Context to use in evaluating the filter.  A vector
-     *     feature may also be provided to evaluate feature attributes in 
-     *     comparison filters or geometries in spatial filters.
-     * 
-     * Returns:
-     * {Boolean} The filter applies.
-     */
-    evaluate: function(context) {
-        switch(this.type) {
-            case OpenLayers.Filter.Logical.AND:
-                for (var i=0, len=this.filters.length; i<len; i++) {
-                    if (this.filters[i].evaluate(context) == false) {
-                        return false;
-                    }
-                }
-                return true;
-                
-            case OpenLayers.Filter.Logical.OR:
-                for (var i=0, len=this.filters.length; i<len; i++) {
-                    if (this.filters[i].evaluate(context) == true) {
-                        return true;
-                    }
-                }
-                return false;
-            
-            case OpenLayers.Filter.Logical.NOT:
-                return (!this.filters[0].evaluate(context));
-        }
-    },
-    
-    /**
-     * APIMethod: clone
-     * Clones this filter.
-     * 
-     * Returns:
-     * {<OpenLayers.Filter.Logical>} Clone of this filter.
-     */
-    clone: function() {
-        var filters = [];        
-        for(var i=0, len=this.filters.length; i<len; ++i) {
-            filters.push(this.filters[i].clone());
-        }
-        return new OpenLayers.Filter.Logical({
-            type: this.type,
-            filters: filters
-        });
-    },
-    
-    CLASS_NAME: "OpenLayers.Filter.Logical"
+    CLASS_NAME: "OpenLayers.Geometry.Point"
 });
-
-
-OpenLayers.Filter.Logical.AND = "&&";
-OpenLayers.Filter.Logical.OR  = "||";
-OpenLayers.Filter.Logical.NOT = "!";
 /* ======================================================================
-    OpenLayers/Filter/Spatial.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Filter.js
- * @requires OpenLayers/Console.js
- */
-
-/**
- * Class: OpenLayers.Filter.Spatial
- * This class represents a spatial filter.
- * Currently implemented: BBOX, DWithin and Intersects
- * 
- * Inherits from
- * - <OpenLayers.Filter>
- */
-OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
-
-    /**
-     * APIProperty: type
-     * {String} Type of spatial filter.
-     *
-     * The type should be one of:
-     * - OpenLayers.Filter.Spatial.BBOX
-     * - OpenLayers.Filter.Spatial.INTERSECTS
-     * - OpenLayers.Filter.Spatial.DWITHIN
-     * - OpenLayers.Filter.Spatial.WITHIN
-     * - OpenLayers.Filter.Spatial.CONTAINS
-     */
-    type: null,
-    
-    /**
-     * APIProperty: property
-     * {String} Name of the context property to compare.
-     */
-    property: null,
-    
-    /**
-     * APIProperty: value
-     * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
-     *     to be used by the filter.  Use bounds for BBOX filters and geometry
-     *     for INTERSECTS or DWITHIN filters.
-     */
-    value: null,
-
-    /**
-     * APIProperty: distance
-     * {Number} The distance to use in a DWithin spatial filter.
-     */
-    distance: null,
-
-    /**
-     * APIProperty: distanceUnits
-     * {String} The units to use for the distance, e.g. 'm'.
-     */
-    distanceUnits: null,
-    
-    /** 
-     * Constructor: OpenLayers.Filter.Spatial
-     * Creates a spatial filter.
-     *
-     * Parameters:
-     * options - {Object} An optional object with properties to set on the
-     *     filter.
-     * 
-     * Returns:
-     * {<OpenLayers.Filter.Spatial>}
-     */
-    initialize: function(options) {
-        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
-    },
-
-   /**
-    * Method: evaluate
-    * Evaluates this filter for a specific feature.
-    * 
-    * Parameters:
-    * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
-    * 
-    * Returns:
-    * {Boolean} The feature meets filter criteria.
-    */
-    evaluate: function(feature) {
-        var intersect = false;
-        switch(this.type) {
-            case OpenLayers.Filter.Spatial.BBOX:
-            case OpenLayers.Filter.Spatial.INTERSECTS:
-                if(feature.geometry) {
-                    var geom = this.value;
-                    if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
-                        geom = this.value.toGeometry();
-                    }
-                    if(feature.geometry.intersects(geom)) {
-                        intersect = true;
-                    }
-                }
-                break;
-            default:
-                OpenLayers.Console.error(
-                    OpenLayers.i18n("filterEvaluateNotImplemented"));
-                break;
-        }
-        return intersect;
-    },
-
-    /**
-     * APIMethod: clone
-     * Clones this filter.
-     * 
-     * Returns:
-     * {<OpenLayers.Filter.Spatial>} Clone of this filter.
-     */
-    clone: function() {
-        var options = OpenLayers.Util.applyDefaults({
-            value: this.value && this.value.clone && this.value.clone()
-        }, this);
-        return new OpenLayers.Filter.Spatial(options);
-    },
-    CLASS_NAME: "OpenLayers.Filter.Spatial"
-});
-
-OpenLayers.Filter.Spatial.BBOX = "BBOX";
-OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
-OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
-OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
-OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
-/* ======================================================================
     OpenLayers/Geometry/Collection.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -33926,12 +19370,19 @@
      */
     calculateBounds: function() {
         this.bounds = null;
-        if ( this.components && this.components.length > 0) {
-            this.setBounds(this.components[0].getBounds());
-            for (var i=1, len=this.components.length; i<len; i++) {
-                this.extendBounds(this.components[i].getBounds());
+        var bounds = new OpenLayers.Bounds();
+        var components = this.components;
+        if (components) {
+            for (var i=0, len=components.length; i<len; i++) {
+                bounds.extend(components[i].getBounds());
             }
         }
+        // to preserve old behavior, we only set bounds if non-null
+        // in the future, we could add bounds.isEmpty()
+        if (bounds.left != null && bounds.bottom != null && 
+            bounds.right != null && bounds.top != null) {
+            this.setBounds(bounds);
+        }
     },
 
     /**
@@ -34363,1248 +19814,10 @@
     CLASS_NAME: "OpenLayers.Geometry.Collection"
 });
 /* ======================================================================
-    OpenLayers/Geometry/Point.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Geometry.js
- */
-
-/**
- * Class: OpenLayers.Geometry.Point
- * Point geometry class. 
- * 
- * Inherits from:
- *  - <OpenLayers.Geometry> 
- */
-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
-
-    /** 
-     * APIProperty: x 
-     * {float} 
-     */
-    x: null,
-
-    /** 
-     * APIProperty: y 
-     * {float} 
-     */
-    y: null,
-
-    /**
-     * Constructor: OpenLayers.Geometry.Point
-     * Construct a point geometry.
-     *
-     * Parameters:
-     * x - {float} 
-     * y - {float}
-     * 
-     */
-    initialize: function(x, y) {
-        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
-        
-        this.x = parseFloat(x);
-        this.y = parseFloat(y);
-    },
-
-    /**
-     * APIMethod: clone
-     * 
-     * Returns:
-     * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
-     */
-    clone: function(obj) {
-        if (obj == null) {
-            obj = new OpenLayers.Geometry.Point(this.x, this.y);
-        }
-
-        // catch any randomly tagged-on properties
-        OpenLayers.Util.applyDefaults(obj, this);
-
-        return obj;
-    },
-
-    /** 
-     * Method: calculateBounds
-     * Create a new Bounds based on the lon/lat
-     */
-    calculateBounds: function () {
-        this.bounds = new OpenLayers.Bounds(this.x, this.y,
-                                            this.x, this.y);
-    },
-
-    /**
-     * 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 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 result;
-    },
-    
-    /** 
-     * 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) ||
-                      (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
-        }
-        return equals;
-    },
-    
-    /**
-     * Method: toShortString
-     *
-     * Returns:
-     * {String} Shortened String representation of Point object. 
-     *         (ex. <i>"5, 42"</i>)
-     */
-    toShortString: function() {
-        return (this.x + ", " + this.y);
-    },
-    
-    /**
-     * APIMethod: move
-     * 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} 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;
-        this.y = this.y + y;
-        this.clearBounds();
-    },
-
-    /**
-     * APIMethod: rotate
-     * Rotate a point around another.
-     *
-     * Parameters:
-     * angle - {Float} Rotation angle in degrees (measured counterclockwise
-     *                 from the positive x-axis)
-     * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
-     */
-    rotate: function(angle, origin) {
-        angle *= Math.PI / 180;
-        var radius = this.distanceTo(origin);
-        var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
-        this.x = origin.x + (radius * Math.cos(theta));
-        this.y = origin.y + (radius * Math.sin(theta));
-        this.clearBounds();
-    },
-    
-    /**
-     * 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
-     *     more useful on geometry collection subclasses.
-     *
-     * Parameters:
-     * scale - {Float} Ratio of the new distance from the origin to the old
-     *                 distance from the origin.  A scale of 2 doubles the
-     *                 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;
-    },
-    
-    /**
-     * APIMethod: intersects
-     * Determine if the input geometry intersects this one.
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
-     *
-     * Returns:
-     * {Boolean} The input geometry intersects this one.
-     */
-    intersects: function(geometry) {
-        var intersect = false;
-        if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
-            intersect = this.equals(geometry);
-        } else {
-            intersect = geometry.intersects(this);
-        }
-        return intersect;
-    },
-    
-    /**
-     * APIMethod: transform
-     * Translate the x,y properties of the point from source to dest.
-     * 
-     * Parameters:
-     * source - {<OpenLayers.Projection>} 
-     * dest - {<OpenLayers.Projection>}
-     * 
-     * Returns:
-     * {<OpenLayers.Geometry>} 
-     */
-    transform: function(source, dest) {
-        if ((source && dest)) {
-            OpenLayers.Projection.transform(
-                this, source, dest); 
-            this.bounds = null;
-        }       
-        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"
-});
-/* ======================================================================
-    OpenLayers/Layer/Vector.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Layer.js
- * @requires OpenLayers/Renderer.js
- * @requires OpenLayers/StyleMap.js
- * @requires OpenLayers/Feature/Vector.js
- * @requires OpenLayers/Console.js
- */
-
-/**
- * Class: OpenLayers.Layer.Vector
- * Instances of OpenLayers.Layer.Vector are used to render vector data from
- *     a variety of sources. Create a new vector layer with the
- *     <OpenLayers.Layer.Vector> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Layer>
- */
-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
-
-    /**
-     * Constant: EVENT_TYPES
-     * {Array(String)} Supported application event types.  Register a listener
-     *     for a particular event with the following syntax:
-     * (code)
-     * layer.events.register(type, obj, listener);
-     * (end)
-     *
-     * Listeners will be called with a reference to an event object.  The
-     *     properties of this event depends on exactly what happened.
-     *
-     * All event objects have at least the following properties:
-     * object - {Object} A reference to layer.events.object.
-     * element - {DOMElement} A reference to layer.events.element.
-     *
-     * Supported map event types (in addition to those from <OpenLayers.Layer>):
-     * 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.
-     *      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
-     *      object passed to listeners will have a *feature* property with a
-     *      reference to the added feature.
-     * 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
-     *      will receive an object with a *feature* property referencing the
-     *      feature to be removed.
-     * beforefeaturesremoved - Triggered before multiple features are removed. 
-     *      Listeners will receive an object with a *features* property
-     *      referencing the features to be removed.
-     * 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
-     *      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
-     *      will receive an object with a *feature* property referencing the
-     *      selected feature.
-     * 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 
-     *      be modified.  Listeners will receive an object with a *feature* 
-     *      property referencing the selected feature.
-     * 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.
-     *      Listeners will receive an object with a *feature* property referencing 
-     *      the modified feature.
-     * 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.
-     * sketchstarted - Triggered when a feature sketch bound for this layer
-     *      is started.  Listeners will receive an object with a *feature*
-     *      property referencing the new sketch feature and a *vertex* property
-     *      referencing the creation point.
-     * 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 and a *feature* property
-     *      referencing the sketch feature.
-     * 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",
-                  "featureadded", "featuresadded", "beforefeatureremoved",
-                  "beforefeaturesremoved", "featureremoved", "featuresremoved",
-                  "beforefeatureselected", "featureselected", "featureunselected", 
-                  "beforefeaturemodified", "featuremodified", "afterfeaturemodified",
-                  "vertexmodified", "sketchstarted", "sketchmodified",
-                  "sketchcomplete", "refresh"],
-
-    /**
-     * APIProperty: isBaseLayer
-     * {Boolean} The layer is a base layer.  Default is false.  Set this property
-     * in the layer options.
-     */
-    isBaseLayer: false,
-
-    /** 
-     * APIProperty: isFixed
-     * {Boolean} Whether the layer remains in one place while dragging the
-     * map.
-     */
-    isFixed: false,
-
-    /** 
-     * APIProperty: isVector
-     * {Boolean} Whether the layer is a vector layer.
-     */
-    isVector: true,
-    
-    /** 
-     * APIProperty: features
-     * {Array(<OpenLayers.Feature.Vector>)} 
-     */
-    features: null,
-    
-    /** 
-     * Property: filter
-     * {<OpenLayers.Filter>} The filter set in this layer,
-     *     a strategy launching read requests can combined
-     *     this filter with its own filter.
-     */
-    filter: null,
-    
-    /** 
-     * Property: selectedFeatures
-     * {Array(<OpenLayers.Feature.Vector>)} 
-     */
-    selectedFeatures: null,
-    
-    /**
-     * Property: unrenderedFeatures
-     * {Object} hash of features, keyed by feature.id, that the renderer
-     *     failed to draw
-     */
-    unrenderedFeatures: null,
-
-    /**
-     * APIProperty: reportError
-     * {Boolean} report friendly error message when loading of renderer
-     * fails.
-     */
-    reportError: true, 
-
-    /** 
-     * APIProperty: style
-     * {Object} Default style for the layer
-     */
-    style: null,
-    
-    /**
-     * Property: styleMap
-     * {<OpenLayers.StyleMap>}
-     */
-    styleMap: null,
-    
-    /**
-     * Property: strategies
-     * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
-     */
-    strategies: null,
-    
-    /**
-     * Property: protocol
-     * {<OpenLayers.Protocol>} Optional protocol for the layer.
-     */
-    protocol: null,
-    
-    /**
-     * Property: renderers
-     * {Array(String)} List of supported Renderer classes. Add to this list to
-     * add support for additional renderers. This list is ordered:
-     * the first renderer which returns true for the  'supported()'
-     * method will be used, if not defined in the 'renderer' option.
-     */
-    renderers: ['SVG', 'VML', 'Canvas'],
-    
-    /** 
-     * Property: renderer
-     * {<OpenLayers.Renderer>}
-     */
-    renderer: null,
-    
-    /**
-     * APIProperty: rendererOptions
-     * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
-     *     supported options.
-     */
-    rendererOptions: null,
-    
-    /** 
-     * APIProperty: geometryType
-     * {String} geometryType allows you to limit the types of geometries this
-     * layer supports. This should be set to something like
-     * "OpenLayers.Geometry.Point" to limit types.
-     */
-    geometryType: null,
-
-    /** 
-     * Property: drawn
-     * {Boolean} Whether the Vector Layer features have been drawn yet.
-     */
-    drawn: false,
-
-    /**
-     * Constructor: OpenLayers.Layer.Vector
-     * Create a new vector layer
-     *
-     * Parameters:
-     * name - {String} A name for the layer
-     * options - {Object} Optional object with non-default properties to set on
-     *           the layer.
-     *
-     * Returns:
-     * {<OpenLayers.Layer.Vector>} A new vector layer
-     */
-    initialize: function(name, options) {
-        
-        // concatenate events specific to vector with those from the base
-        this.EVENT_TYPES =
-            OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(
-            OpenLayers.Layer.prototype.EVENT_TYPES
-        );
-
-        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
-
-        // allow user-set renderer, otherwise assign one
-        if (!this.renderer || !this.renderer.supported()) {  
-            this.assignRenderer();
-        }
-
-        // if no valid renderer found, display error
-        if (!this.renderer || !this.renderer.supported()) {
-            this.renderer = null;
-            this.displayError();
-        } 
-
-        if (!this.styleMap) {
-            this.styleMap = new OpenLayers.StyleMap();
-        }
-
-        this.features = [];
-        this.selectedFeatures = [];
-        this.unrenderedFeatures = {};
-        
-        // Allow for custom layer behavior
-        if(this.strategies){
-            for(var i=0, len=this.strategies.length; i<len; i++) {
-                this.strategies[i].setLayer(this);
-            }
-        }
-
-    },
-
-    /**
-     * APIMethod: destroy
-     * Destroy this layer
-     */
-    destroy: function() {
-        if (this.strategies) {
-            var strategy, i, len;
-            for(i=0, len=this.strategies.length; i<len; i++) {
-                strategy = this.strategies[i];
-                if(strategy.autoDestroy) {
-                    strategy.destroy();
-                }
-            }
-            this.strategies = null;
-        }
-        if (this.protocol) {
-            if(this.protocol.autoDestroy) {
-                this.protocol.destroy();
-            }
-            this.protocol = null;
-        }
-        this.destroyFeatures();
-        this.features = null;
-        this.selectedFeatures = null;
-        this.unrenderedFeatures = null;
-        if (this.renderer) {
-            this.renderer.destroy();
-        }
-        this.renderer = null;
-        this.geometryType = null;
-        this.drawn = null;
-        OpenLayers.Layer.prototype.destroy.apply(this, arguments);  
-    },
-
-    /**
-     * Method: clone
-     * Create a clone of this layer.
-     * 
-     * Note: Features of the layer are also cloned.
-     *
-     * Returns:
-     * {<OpenLayers.Layer.Vector>} An exact clone of this layer
-     */
-    clone: function (obj) {
-        
-        if (obj == null) {
-            obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
-        }
-
-        //get all additions from superclasses
-        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
-
-        // copy/set any non-init, non-simple values here
-        var features = this.features;
-        var len = features.length;
-        var clonedFeatures = new Array(len);
-        for(var i=0; i<len; ++i) {
-            clonedFeatures[i] = features[i].clone();
-        }
-        obj.features = clonedFeatures;
-
-        return obj;
-    },    
-    
-    /**
-     * Method: refresh
-     * Ask the layer to request features again and redraw them.  Triggers
-     *     the refresh event if the layer is in range and visible.
-     *
-     * Parameters:
-     * obj - {Object} Optional object with properties for any listener of
-     *     the refresh event.
-     */
-    refresh: function(obj) {
-        if(this.calculateInRange() && this.visibility) {
-            this.events.triggerEvent("refresh", obj);
-        }
-    },
-
-    /** 
-     * Method: assignRenderer
-     * Iterates through the available renderer implementations and selects 
-     * and assigns the first one whose "supported()" function returns true.
-     */    
-    assignRenderer: function()  {
-        for (var i=0, len=this.renderers.length; i<len; i++) {
-            var rendererClass = this.renderers[i];
-            var renderer = (typeof rendererClass == "function") ?
-                rendererClass :
-                OpenLayers.Renderer[rendererClass];
-            if (renderer && renderer.prototype.supported()) {
-                this.renderer = new renderer(this.div, this.rendererOptions);
-                break;
-            }  
-        }  
-    },
-
-    /** 
-     * Method: displayError 
-     * Let the user know their browser isn't supported.
-     */
-    displayError: function() {
-        if (this.reportError) {
-            OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", 
-                                     {'renderers':this.renderers.join("\n")}));
-        }    
-    },
-
-    /** 
-     * Method: setMap
-     * The layer has been added to the map. 
-     * 
-     * If there is no renderer set, the layer can't be used. Remove it.
-     * Otherwise, give the renderer a reference to the map and set its size.
-     * 
-     * Parameters:
-     * map - {<OpenLayers.Map>} 
-     */
-    setMap: function(map) {        
-        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
-
-        if (!this.renderer) {
-            this.map.removeLayer(this);
-        } else {
-            this.renderer.map = this.map;
-            this.renderer.setSize(this.map.getSize());
-        }
-    },
-
-    /**
-     * Method: afterAdd
-     * Called at the end of the map.addLayer sequence.  At this point, the map
-     *     will have a base layer.  Any autoActivate strategies will be
-     *     activated here.
-     */
-    afterAdd: function() {
-        if(this.strategies) {
-            var strategy, i, len;
-            for(i=0, len=this.strategies.length; i<len; i++) {
-                strategy = this.strategies[i];
-                if(strategy.autoActivate) {
-                    strategy.activate();
-                }
-            }
-        }
-    },
-
-    /**
-     * Method: removeMap
-     * The layer has been removed from the map.
-     *
-     * Parameters:
-     * map - {<OpenLayers.Map>}
-     */
-    removeMap: function(map) {
-        this.drawn = false;
-        if(this.strategies) {
-            var strategy, i, len;
-            for(i=0, len=this.strategies.length; i<len; i++) {
-                strategy = this.strategies[i];
-                if(strategy.autoActivate) {
-                    strategy.deactivate();
-                }
-            }
-        }
-    },
-    
-    /**
-     * Method: onMapResize
-     * Notify the renderer of the change in size. 
-     * 
-     */
-    onMapResize: function() {
-        OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
-        this.renderer.setSize(this.map.getSize());
-    },
-
-    /**
-     * Method: moveTo
-     *  Reset the vector layer's div so that it once again is lined up with 
-     *   the map. Notify the renderer of the change of extent, and in the
-     *   case of a change of zoom level (resolution), have the 
-     *   renderer redraw features.
-     * 
-     *  If the layer has not yet been drawn, cycle through the layer's 
-     *   features and draw each one.
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>} 
-     * zoomChanged - {Boolean} 
-     * dragging - {Boolean} 
-     */
-    moveTo: function(bounds, zoomChanged, dragging) {
-        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
-        
-        var coordSysUnchanged = true;
-
-        if (!dragging) {
-            this.renderer.root.style.visibility = "hidden";
-            
-            this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
-            this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
-            var extent = this.map.getExtent();
-            coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
-            
-            this.renderer.root.style.visibility = "visible";
-
-            // Force a reflow on gecko based browsers to prevent jump/flicker.
-            // This seems to happen on only certain configurations; it was originally
-            // noticed in FF 2.0 and Linux.
-            if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {
-                this.div.scrollLeft = this.div.scrollLeft;
-            }
-            
-            if(!zoomChanged && coordSysUnchanged) {
-                for(var i in this.unrenderedFeatures) {
-                    var feature = this.unrenderedFeatures[i];
-                    this.drawFeature(feature);
-                }
-            }
-        }
-        
-        if (!this.drawn || zoomChanged || !coordSysUnchanged) {
-            this.drawn = true;
-            var feature;
-            for(var i=0, len=this.features.length; i<len; i++) {
-                this.renderer.locked = (i !== (len - 1));
-                feature = this.features[i];
-                this.drawFeature(feature);
-            }
-        }    
-    },
-    
-    /** 
-     * 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
-     * Add Features to the layer.
-     *
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} 
-     * options - {Object}
-     */
-    addFeatures: function(features, options) {
-        if (!(features instanceof Array)) {
-            features = [features];
-        }
-        
-        var notify = !options || !options.silent;
-        if(notify) {
-            var event = {features: features};
-            var ret = this.events.triggerEvent("beforefeaturesadded", event);
-            if(ret === false) {
-                return;
-            }
-            features = event.features;
-        }
-        
-        // Track successfully added features for featuresadded event, since
-        // beforefeatureadded can veto single features.
-        var featuresAdded = [];
-        for (var i=0, len=features.length; i<len; i++) {
-            if (i != (features.length - 1)) {
-                this.renderer.locked = true;
-            } else {
-                this.renderer.locked = false;
-            }    
-            var feature = features[i];
-            
-            if (this.geometryType &&
-              !(feature.geometry instanceof this.geometryType)) {
-                var throwStr = OpenLayers.i18n('componentShouldBe',
-                          {'geomType':this.geometryType.prototype.CLASS_NAME});
-                throw throwStr;
-              }
-
-            //give feature reference to its layer
-            feature.layer = this;
-
-            if (!feature.style && this.style) {
-                feature.style = OpenLayers.Util.extend({}, this.style);
-            }
-
-            if (notify) {
-                if(this.events.triggerEvent("beforefeatureadded",
-                                            {feature: feature}) === false) {
-                    continue;
-                };
-                this.preFeatureInsert(feature);
-            }
-
-            featuresAdded.push(feature);
-            this.features.push(feature);
-            this.drawFeature(feature);
-            
-            if (notify) {
-                this.events.triggerEvent("featureadded", {
-                    feature: feature
-                });
-                this.onFeatureInsert(feature);
-            }
-        }
-        
-        if(notify) {
-            this.events.triggerEvent("featuresadded", {features: featuresAdded});
-        }
-    },
-
-
-    /**
-     * 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>)} 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) {
-            return;
-        }
-        if (features === this.features) {
-            return this.removeAllFeatures(options);
-        }
-        if (!(features instanceof Array)) {
-            features = [features];
-        }
-        if (features === this.selectedFeatures) {
-            features = features.slice();
-        }
-
-        var notify = !options || !options.silent;
-        
-        if (notify) {
-            this.events.triggerEvent(
-                "beforefeaturesremoved", {features: features}
-            );
-        }
-
-        for (var i = features.length - 1; i >= 0; i--) {
-            // We remain locked so long as we're not at 0
-            // and the 'next' feature has a geometry. We do the geometry check
-            // because if all the features after the current one are 'null', we
-            // won't call eraseGeometry, so we break the 'renderer functions
-            // will always be called with locked=false *last*' rule. The end result
-            // is a possible gratiutious unlocking to save a loop through the rest 
-            // of the list checking the remaining features every time. So long as
-            // null geoms are rare, this is probably okay.    
-            if (i != 0 && features[i-1].geometry) {
-                this.renderer.locked = true;
-            } else {
-                this.renderer.locked = false;
-            }
-    
-            var feature = features[i];
-            delete this.unrenderedFeatures[feature.id];
-
-            if (notify) {
-                this.events.triggerEvent("beforefeatureremoved", {
-                    feature: feature
-                });
-            }
-
-            this.features = OpenLayers.Util.removeItem(this.features, feature);
-            // feature has no layer at this point
-            feature.layer = null;
-
-            if (feature.geometry) {
-                this.renderer.eraseFeatures(feature);
-            }
-                    
-            //in the case that this feature is one of the selected features, 
-            // remove it from that array as well.
-            if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
-                OpenLayers.Util.removeItem(this.selectedFeatures, feature);
-            }
-
-            if (notify) {
-                this.events.triggerEvent("featureremoved", {
-                    feature: feature
-                });
-            }
-        }
-
-        if (notify) {
-            this.events.triggerEvent("featuresremoved", {features: features});
-        }
-    },
-    
-    /** 
-     * APIMethod: removeAllFeatures
-     * Remove all features from the layer.
-     *
-     * Parameters:
-     * options - {Object} Optional properties for changing behavior of the
-     *     removal.
-     *
-     * Valid options:
-     * silent - {Boolean} Supress event triggering.  Default is false.
-     */
-    removeAllFeatures: function(options) {
-        var notify = !options || !options.silent;
-        var features = this.features;
-        if (notify) {
-            this.events.triggerEvent(
-                "beforefeaturesremoved", {features: features}
-            );
-        }
-        var feature;
-        for (var i = features.length-1; i >= 0; i--) {
-            feature = features[i];
-            if (notify) {
-                this.events.triggerEvent("beforefeatureremoved", {
-                    feature: feature
-                });
-            }
-            feature.layer = null;
-            if (notify) {
-                this.events.triggerEvent("featureremoved", {
-                    feature: feature
-                });
-            }
-        }
-        this.renderer.clear();
-        this.features = [];
-        this.unrenderedFeatures = {};
-        this.selectedFeatures = [];
-        if (notify) {
-            this.events.triggerEvent("featuresremoved", {features: features});
-        }
-    },
-
-    /**
-     * APIMethod: destroyFeatures
-     * Erase and destroy features on the layer.
-     *
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
-     *     features to destroy.  If not supplied, all features on the layer
-     *     will be destroyed.
-     * options - {Object}
-     */
-    destroyFeatures: function(features, options) {
-        var all = (features == undefined); // evaluates to true if
-                                           // features is null
-        if(all) {
-            features = this.features;
-        }
-        if(features) {
-            this.removeFeatures(features, options);
-            for(var i=features.length-1; i>=0; i--) {
-                features[i].destroy();
-            }
-        }
-    },
-
-    /**
-     * APIMethod: drawFeature
-     * Draw (or redraw) a feature on the layer.  If the optional style argument
-     * is included, this style will be used.  If no style is included, the
-     * 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 - {String | Object} Named render intent or full symbolizer object.
-     */
-    drawFeature: function(feature, style) {
-        // don't try to draw the feature with the renderer if the layer is not 
-        // drawn itself
-        if (!this.drawn) {
-            return
-        }
-        if (typeof style != "object") {
-            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);
-            }
-        }
-        
-        if (!this.renderer.drawFeature(feature, style)) {
-            this.unrenderedFeatures[feature.id] = feature;
-        } else {
-            delete this.unrenderedFeatures[feature.id];
-        };
-    },
-    
-    /**
-     * Method: eraseFeatures
-     * Erase features from the layer.
-     *
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} 
-     */
-    eraseFeatures: function(features) {
-        this.renderer.eraseFeatures(features);
-    },
-
-    /**
-     * Method: getFeatureFromEvent
-     * Given an event, return a feature if the event occurred over one.
-     * Otherwise, return null.
-     *
-     * Parameters:
-     * evt - {Event} 
-     *
-     * Returns:
-     * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
-     */
-    getFeatureFromEvent: function(evt) {
-        if (!this.renderer) {
-            OpenLayers.Console.error(OpenLayers.i18n("getFeatureError")); 
-            return null;
-        }    
-        var featureId = this.renderer.getFeatureIdFromEvent(evt);
-        return this.getFeatureById(featureId);
-    },
-
-    /**
-     * APIMethod: getFeatureBy
-     * Given a property value, return the feature if it exists in the features array
-     *
-     * Parameters:
-     * property - {String}
-     * value - {String}
-     *
-     * Returns:
-     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
-     * property value or null if there is no such feature.
-     */
-    getFeatureBy: function(property, value) {
-        //TBD - would it be more efficient to use a hash for this.features?
-        var feature = null;
-        for(var i=0, len=this.features.length; i<len; ++i) {
-            if(this.features[i][property] == value) {
-                feature = this.features[i];
-                break;
-            }
-        }
-        return feature;
-    },
-
-    /**
-     * APIMethod: getFeatureById
-     * Given a feature id, return the feature if it exists in the features array
-     *
-     * Parameters:
-     * featureId - {String}
-     *
-     * Returns:
-     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
-     * featureId or null if there is no such feature.
-     */
-    getFeatureById: function(featureId) {
-        return this.getFeatureBy('id', featureId);
-    },
-
-    /**
-     * APIMethod: getFeatureByFid
-     * Given a feature fid, return the feature if it exists in the features array
-     *
-     * Parameters:
-     * featureFid - {String}
-     *
-     * Returns:
-     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
-     * featureFid or null if there is no such feature.
-     */
-    getFeatureByFid: function(featureFid) {
-        return this.getFeatureBy('fid', featureFid);
-    },
-
-    /**
-     * Unselect the selected features
-     * i.e. clears the featureSelection array
-     * change the style back
-    clearSelection: function() {
-
-       var vectorLayer = this.map.vectorLayer;
-        for (var i = 0; i < this.map.featureSelection.length; i++) {
-            var featureSelection = this.map.featureSelection[i];
-            vectorLayer.drawFeature(featureSelection, vectorLayer.style);
-        }
-        this.map.featureSelection = [];
-    },
-     */
-
-
-    /**
-     * APIMethod: onFeatureInsert
-     * method called after a feature is inserted.
-     * Does nothing by default. Override this if you
-     * need to do something on feature updates.
-     *
-     * Paarameters: 
-     * feature - {<OpenLayers.Feature.Vector>} 
-     */
-    onFeatureInsert: function(feature) {
-    },
-    
-    /**
-     * APIMethod: preFeatureInsert
-     * method called before a feature is inserted.
-     * Does nothing by default. Override this if you
-     * need to do something when features are first added to the
-     * layer, but before they are drawn, such as adjust the style.
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>} 
-     */
-    preFeatureInsert: function(feature) {
-    },
-
-    /** 
-     * APIMethod: getDataExtent
-     * Calculates the max extent which includes all of the features.
-     * 
-     * Returns:
-     * {<OpenLayers.Bounds>}
-     */
-    getDataExtent: function () {
-        var maxExtent = null;
-        var features = this.features;
-        if(features && (features.length > 0)) {
-            maxExtent = new OpenLayers.Bounds();
-            var geometry = null;
-            for(var i=0, len=features.length; i<len; i++) {
-                geometry = features[i].geometry;
-                if (geometry) {
-                    maxExtent.extend(geometry.getBounds());
-                }
-            }
-        }
-        return maxExtent;
-    },
-
-    CLASS_NAME: "OpenLayers.Layer.Vector"
-});
-/* ======================================================================
     OpenLayers/Geometry/MultiPoint.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -35675,1046 +19888,10 @@
     CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
 });
 /* ======================================================================
-    OpenLayers/Handler/Point.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Handler.js
- * @requires OpenLayers/Geometry/Point.js
- */
-
-/**
- * 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', 'cancel', and 'modify'.  The modify callback is
- *     called with each change in the sketch and will receive the latest point
- *     drawn.  Create a new instance with the <OpenLayers.Handler.Point>
- *     constructor.
- * 
- * Inherits from:
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
-    
-    /**
-     * Property: point
-     * {<OpenLayers.Feature.Vector>} The currently drawn point
-     */
-    point: null,
-
-    /**
-     * Property: layer
-     * {<OpenLayers.Layer.Vector>} The temporary drawing layer
-     */
-    layer: null,
-    
-    /**
-     * APIProperty: multi
-     * {Boolean} Cast features to multi-part geometries before passing to the
-     *     layer.  Default is false.
-     */
-    multi: false,
-    
-    /**
-     * Property: drawing 
-     * {Boolean} A point is being drawn
-     */
-    drawing: false,
-    
-    /**
-     * Property: mouseDown
-     * {Boolean} The mouse is down
-     */
-    mouseDown: false,
-
-    /**
-     * Property: lastDown
-     * {<OpenLayers.Pixel>} Location of the last mouse down
-     */
-    lastDown: null,
-
-    /**
-     * Property: lastUp
-     * {<OpenLayers.Pixel>}
-     */
-    lastUp: null,
-
-    /**
-     * APIProperty: persist
-     * {Boolean} Leave the feature rendered until destroyFeature is called.
-     *     Default is false.  If set to true, the feature remains rendered until
-     *     destroyFeature is called, typically by deactivating the handler or
-     *     starting another drawing.
-     */
-    persist: false,
-
-    /**
-     * Property: layerOptions
-     * {Object} Any optional properties to be set on the sketch layer.
-     */
-    layerOptions: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Point
-     * Create a new point handler.
-     *
-     * Parameters:
-     * 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:
-     * create - Called when a sketch is first created.  Callback called with
-     *     the creation point geometry and sketch feature.
-     * modify - Called with each move of a vertex with the vertex (point)
-     *     geometry and the sketch feature.
-     * done - Called when the point drawing is finished.  The callback will
-     *     recieve a single argument, the point geometry.
-     * cancel - Called when the handler is deactivated while drawing.  The
-     *     cancel callback will receive a geometry.
-     */
-    initialize: function(control, callbacks, options) {
-        if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
-            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
-        }
-
-        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-    },
-    
-    /**
-     * APIMethod: activate
-     * turn on the handler
-     */
-    activate: function() {
-        if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
-            return false;
-        }
-        // create temporary vector layer for rendering geometry sketch
-        // TBD: this could be moved to initialize/destroy - setting visibility here
-        var options = OpenLayers.Util.extend({
-            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: OpenLayers.Function.True
-        }, this.layerOptions);
-        this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
-        this.map.addLayer(this.layer);
-        return true;
-    },
-    
-    /**
-     * Method: createFeature
-     * Add temporary features
-     *
-     * Parameters:
-     * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
-     */
-    createFeature: function(pixel) {
-        var lonlat = this.map.getLonLatFromPixel(pixel);
-        this.point = new OpenLayers.Feature.Vector(
-            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
-        );
-        this.callback("create", [this.point.geometry, this.point]);
-        this.point.geometry.clearBounds();
-        this.layer.addFeatures([this.point], {silent: true});
-    },
-
-    /**
-     * APIMethod: deactivate
-     * turn off the handler
-     */
-    deactivate: function() {
-        if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            return false;
-        }
-        // call the cancel callback if mid-drawing
-        if(this.drawing) {
-            this.cancel();
-        }
-        this.destroyFeature();
-        // If a layer's map property is set to null, it means that that layer
-        // isn't added to the map. Since we ourself added the layer to the map
-        // in activate(), we can assume that if this.layer.map is null it means
-        // that the layer has been destroyed (as a result of map.destroy() for
-        // example.
-        if (this.layer.map != null) {
-            this.layer.destroy(false);
-        }
-        this.layer = null;
-        return true;
-    },
-    
-    /**
-     * Method: destroyFeature
-     * Destroy the temporary geometries
-     */
-    destroyFeature: function() {
-        if(this.layer) {
-            this.layer.destroyFeatures();
-        }
-        this.point = null;
-    },
-
-    /**
-     * Method: finalize
-     * Finish the geometry and call the "done" callback.
-     *
-     * Parameters:
-     * cancel - {Boolean} Call cancel instead of done callback.  Default is
-     *     false.
-     */
-    finalize: function(cancel) {
-        var key = cancel ? "cancel" : "done";
-        this.drawing = false;
-        this.mouseDown = false;
-        this.lastDown = null;
-        this.lastUp = null;
-        this.callback(key, [this.geometryClone()]);
-        if(cancel || !this.persist) {
-            this.destroyFeature();
-        }
-    },
-
-    /**
-     * APIMethod: cancel
-     * Finish the geometry and call the "cancel" callback.
-     */
-    cancel: function() {
-        this.finalize(true);
-    },
-
-    /**
-     * Method: click
-     * Handle clicks.  Clicks are stopped from propagating to other listeners
-     *     on map.events or other dom elements.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    click: function(evt) {
-        OpenLayers.Event.stop(evt);
-        return false;
-    },
-
-    /**
-     * Method: dblclick
-     * Handle double-clicks.  Double-clicks are stopped from propagating to other
-     *     listeners on map.events or other dom elements.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    dblclick: function(evt) {
-        OpenLayers.Event.stop(evt);
-        return false;
-    },
-    
-    /**
-     * 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();
-        this.drawFeature();
-    },
-
-    /**
-     * Method: drawFeature
-     * Render features on the temporary layer.
-     */
-    drawFeature: function() {
-        this.layer.drawFeature(this.point, this.style);
-    },
-    
-    /**
-     * Method: getGeometry
-     * Return the sketch geometry.  If <multi> is true, this will return
-     *     a multi-part geometry.
-     *
-     * Returns:
-     * {<OpenLayers.Geometry.Point>}
-     */
-    getGeometry: function() {
-        var geometry = this.point && this.point.geometry;
-        if(geometry && this.multi) {
-            geometry = new OpenLayers.Geometry.MultiPoint([geometry]);
-        }
-        return geometry;
-    },
-
-    /**
-     * Method: geometryClone
-     * Return a clone of the relevant geometry.
-     *
-     * Returns:
-     * {<OpenLayers.Geometry>}
-     */
-    geometryClone: function() {
-        var geom = this.getGeometry();
-        return geom && geom.clone();
-    },
-  
-    /**
-     * Method: mousedown
-     * Handle mouse down.  Adjust the geometry and redraw.
-     * Return determines whether to propagate the event on the map.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousedown: function(evt) {
-        // check keyboard modifiers
-        if(!this.checkModifiers(evt)) {
-            return true;
-        }
-        // ignore double-clicks
-        if(this.lastDown && this.lastDown.equals(evt.xy)) {
-            return true;
-        }
-        this.drawing = true;
-        if(this.lastDown == null) {
-            if(this.persist) {
-                this.destroyFeature();
-            }
-            this.createFeature(evt.xy);
-        } else {
-            this.modifyFeature(evt.xy);
-        }
-        this.lastDown = evt.xy;
-        return false;
-    },
-
-    /**
-     * Method: mousemove
-     * Handle mouse move.  Adjust the geometry and redraw.
-     * Return determines whether to propagate the event on the map.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousemove: function (evt) {
-        if(this.drawing) {
-            this.modifyFeature(evt.xy);
-        }
-        return true;
-    },
-
-    /**
-     * Method: mouseup
-     * Handle mouse up.  Send the latest point in the geometry to the control.
-     * Return determines whether to propagate the event on the map.
-     *
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mouseup: function (evt) {
-        if(this.drawing) {
-            this.finalize();
-            return false;
-        } else {
-            return true;
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Point"
-});
-/* ======================================================================
-    OpenLayers/Protocol/HTTP.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Protocol.js
- * @requires OpenLayers/Feature/Vector.js
- * @requires OpenLayers/Filter/Spatial.js
- * @requires OpenLayers/Filter/Comparison.js
- * @requires OpenLayers/Filter/Logical.js
- * @requires OpenLayers/Request/XMLHttpRequest.js
- */
-
-/**
- * Class: OpenLayers.Protocol.HTTP
- * A basic HTTP protocol for vector layers.  Create a new instance with the
- *     <OpenLayers.Protocol.HTTP> constructor.
- *
- * Inherits from:
- *  - <OpenLayers.Protocol>
- */
-OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
-
-    /**
-     * Property: url
-     * {String} Service URL, read-only, set through the options
-     *     passed to constructor.
-     */
-    url: null,
-
-    /**
-     * Property: headers
-     * {Object} HTTP request headers, read-only, set through the options
-     *     passed to the constructor,
-     *     Example: {'Content-Type': 'plain/text'}
-     */
-    headers: null,
-
-    /**
-     * Property: params
-     * {Object} Parameters of GET requests, read-only, set through the options
-     *     passed to the constructor,
-     *     Example: {'bbox': '5,5,5,5'}
-     */
-    params: null,
-    
-    /**
-     * Property: callback
-     * {Object} Function to be called when the <read>, <create>,
-     *     <update>, <delete> or <commit> operation completes, read-only,
-     *     set through the options passed to the constructor.
-     */
-    callback: null,
-
-    /**
-     * Property: scope
-     * {Object} Callback execution scope, read-only, set through the
-     *     options passed to the constructor.
-     */
-    scope: null,
-
-    /**
-     * Property: readWithPOST
-     * {Boolean} true if read operations are done with POST requests
-     *     instead of GET, defaults to false.
-     */
-    readWithPOST: false,
-
-    /**
-     * Property: wildcarded.
-     * {Boolean} If true percent signs are added around values
-     *     read from LIKE filters, for example if the protocol
-     *     read method is passed a LIKE filter whose property
-     *     is "foo" and whose value is "bar" the string
-     *     "foo__ilike=%bar%" will be sent in the query string;
-     *     defaults to false.
-     */
-    wildcarded: false,
-
-    /**
-     * Constructor: OpenLayers.Protocol.HTTP
-     * A class for giving layers generic HTTP protocol.
-     *
-     * Parameters:
-     * options - {Object} Optional object whose properties will be set on the
-     *     instance.
-     *
-     * Valid options include:
-     * url - {String}
-     * headers - {Object} 
-     * params - {Object}
-     * format - {<OpenLayers.Format>}
-     * callback - {Function}
-     * scope - {Object}
-     */
-    initialize: function(options) {
-        options = options || {};
-        this.params = {};
-        this.headers = {};
-        OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
-    },
-    
-    /**
-     * APIMethod: destroy
-     * Clean up the protocol.
-     */
-    destroy: function() {
-        this.params = null;
-        this.headers = null;
-        OpenLayers.Protocol.prototype.destroy.apply(this);
-    },
-   
-    /**
-     * APIMethod: read
-     * Construct a request for reading new features.
-     *
-     * Parameters:
-     * options - {Object} Optional object for configuring the request.
-     *     This object is modified and should not be reused.
-     *
-     * Valid options:
-     * url - {String} Url for the request.
-     * params - {Object} Parameters to get serialized as a query string.
-     * headers - {Object} Headers to be set on the request.
-     * filter - {<OpenLayers.Filter>} Filter to get serialized as a
-     *     query string.
-     * readWithPOST - {Boolean} If the request should be done with POST.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
-     *     references the HTTP request, this object is also passed to the
-     *     callback function when the request completes, its "features" property
-     *     is then populated with the the features received from the server.
-     */
-    read: function(options) {
-        OpenLayers.Protocol.prototype.read.apply(this, arguments);
-        options = OpenLayers.Util.applyDefaults(options, this.options);
-        options.params = OpenLayers.Util.applyDefaults(
-            options.params, this.options.params);
-        if(options.filter) {
-            options.params = this.filterToParams(
-                options.filter, options.params);
-        }
-        var readWithPOST = (options.readWithPOST !== undefined) ?
-                           options.readWithPOST : this.readWithPOST;
-        var resp = new OpenLayers.Protocol.Response({requestType: "read"});
-        if(readWithPOST) {
-            resp.priv = OpenLayers.Request.POST({
-                url: options.url,
-                callback: this.createCallback(this.handleRead, resp, options),
-                data: OpenLayers.Util.getParameterString(options.params),
-                headers: {
-                    "Content-Type": "application/x-www-form-urlencoded"
-                }
-            });
-        } else {
-            resp.priv = OpenLayers.Request.GET({
-                url: options.url,
-                callback: this.createCallback(this.handleRead, resp, options),
-                params: options.params,
-                headers: options.headers
-            });
-        }
-        return resp;
-    },
-
-    /**
-     * Method: handleRead
-     * Individual callbacks are created for read, create and update, should
-     *     a subclass need to override each one separately.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
-     *     the user callback.
-     * options - {Object} The user options passed to the read call.
-     */
-    handleRead: function(resp, options) {
-        this.handleResponse(resp, options);
-    },
-
-    /**
-     * Method: filterToParams
-     * Convert an <OpenLayers.Filter> object to parameters.
-     *
-     * Parameters:
-     * filter - {OpenLayers.Filter} filter to convert.
-     * params - {Object} The parameters object.
-     *
-     * Returns:
-     * {Object} The resulting parameters object.
-     */
-    filterToParams: function(filter, params) {
-        params = params || {};
-        var className = filter.CLASS_NAME;
-        var filterType = className.substring(className.lastIndexOf(".") + 1);
-        switch(filterType) {
-            case "Spatial":
-                switch(filter.type) {
-                    case OpenLayers.Filter.Spatial.BBOX:
-                        params.bbox = filter.value.toArray();
-                        break;
-                    case OpenLayers.Filter.Spatial.DWITHIN:
-                        params.tolerance = filter.distance;
-                        // no break here
-                    case OpenLayers.Filter.Spatial.WITHIN:
-                        params.lon = filter.value.x;
-                        params.lat = filter.value.y;
-                        break;
-                    default:
-                        OpenLayers.Console.warn(
-                            "Unknown spatial filter type " + filter.type);
-                }
-                break;
-            case "Comparison":
-                var op = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR[filter.type];
-                if(op !== undefined) {
-                    var value = filter.value;
-                    if(filter.type == OpenLayers.Filter.Comparison.LIKE) {
-                        value = this.regex2value(value);
-                        if(this.wildcarded) {
-                            value = "%" + value + "%";
-                        }
-                    }
-                    params[filter.property + "__" + op] = value;
-                    params.queryable = params.queryable || [];
-                    params.queryable.push(filter.property);
-                } else {
-                    OpenLayers.Console.warn(
-                        "Unknown comparison filter type " + filter.type);
-                }
-                break;
-            case "Logical":
-                if(filter.type === OpenLayers.Filter.Logical.AND) {
-                    for(var i=0,len=filter.filters.length; i<len; i++) {
-                        params = this.filterToParams(filter.filters[i], params);
-                    }
-                } else {
-                    OpenLayers.Console.warn(
-                        "Unsupported logical filter type " + filter.type);
-                }
-                break;
-            default:
-                OpenLayers.Console.warn("Unknown filter type " + filterType);
-        }
-        return params;
-    },
-
-    /**
-     * Method: regex2value
-     * Convert the value from a regular expression string to a LIKE/ILIKE
-     * string known to the web service.
-     *
-     * Parameters:
-     * value - {String} The regex string.
-     *
-     * Returns:
-     * {String} The converted string.
-     */
-    regex2value: function(value) {
-
-        // highly sensitive!! Do not change this without running the
-        // Protocol/HTTP.html unit tests
-
-        // convert % to \%
-        value = value.replace(/%/g, "\\%");
-
-        // convert \\. to \\_ (\\.* occurences converted later)
-        value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
-            return $1 ? $0 : "\\\\_";
-        });
-
-        // convert \\.* to \\%
-        value = value.replace(/\\\\\.\*/g, "\\\\%");
-
-        // convert . to _ (\. and .* occurences converted later)
-        value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
-            return $1 || $2 ? $0 : "_";
-        });
-
-        // convert .* to % (\.* occurnces converted later)
-        value = value.replace(/(\\)?\.\*/g, function($0, $1) {
-            return $1 ? $0 : "%";
-        });
-
-        // convert \. to .
-        value = value.replace(/\\\./g, ".");
-
-        // replace \* with * (watching out for \\*)
-        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
-            return $1 ? $0 : "*";
-        });
-
-        return value;
-    },
-
-    /**
-     * APIMethod: create
-     * Construct a request for writing newly created features.
-     *
-     * Parameters:
-     * features - {Array({<OpenLayers.Feature.Vector>})} or
-     *     {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *     This object is modified and should not be reused.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     *     object, whose "priv" property references the HTTP request, this 
-     *     object is also passed to the callback function when the request
-     *     completes, its "features" property is then populated with the
-     *     the features received from the server.
-     */
-    create: function(features, options) {
-        options = OpenLayers.Util.applyDefaults(options, this.options);
-
-        var resp = new OpenLayers.Protocol.Response({
-            reqFeatures: features,
-            requestType: "create"
-        });
-
-        resp.priv = OpenLayers.Request.POST({
-            url: options.url,
-            callback: this.createCallback(this.handleCreate, resp, options),
-            headers: options.headers,
-            data: this.format.write(features)
-        });
-
-        return resp;
-    },
-
-    /**
-     * Method: handleCreate
-     * Called the the request issued by <create> is complete.  May be overridden
-     *     by subclasses.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
-     *     any user callback.
-     * options - {Object} The user options passed to the create call.
-     */
-    handleCreate: function(resp, options) {
-        this.handleResponse(resp, options);
-    },
-
-    /**
-     * APIMethod: update
-     * Construct a request updating modified feature.
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *     This object is modified and should not be reused.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     *     object, whose "priv" property references the HTTP request, this 
-     *     object is also passed to the callback function when the request
-     *     completes, its "features" property is then populated with the
-     *     the feature received from the server.
-     */
-    update: function(feature, options) {
-        options = options || {};
-        var url = options.url ||
-                  feature.url ||
-                  this.options.url + "/" + feature.fid;
-        options = OpenLayers.Util.applyDefaults(options, this.options);
-
-        var resp = new OpenLayers.Protocol.Response({
-            reqFeatures: feature,
-            requestType: "update"
-        });
-
-        resp.priv = OpenLayers.Request.PUT({
-            url: url,
-            callback: this.createCallback(this.handleUpdate, resp, options),
-            headers: options.headers,
-            data: this.format.write(feature)
-        });
-
-        return resp;
-    },
-
-    /**
-     * Method: handleUpdate
-     * Called the the request issued by <update> is complete.  May be overridden
-     *     by subclasses.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
-     *     any user callback.
-     * options - {Object} The user options passed to the update call.
-     */
-    handleUpdate: function(resp, options) {
-        this.handleResponse(resp, options);
-    },
-
-    /**
-     * APIMethod: delete
-     * Construct a request deleting a removed feature.
-     *
-     * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>}
-     * options - {Object} Optional object for configuring the request.
-     *     This object is modified and should not be reused.
-     *
-     * Returns:
-     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
-     *     object, whose "priv" property references the HTTP request, this 
-     *     object is also passed to the callback function when the request
-     *     completes.
-     */
-    "delete": function(feature, options) {
-        options = options || {};
-        var url = options.url ||
-                  feature.url ||
-                  this.options.url + "/" + feature.fid;
-        options = OpenLayers.Util.applyDefaults(options, this.options);
-
-        var resp = new OpenLayers.Protocol.Response({
-            reqFeatures: feature,
-            requestType: "delete"
-        });
-
-        resp.priv = OpenLayers.Request.DELETE({
-            url: url,
-            callback: this.createCallback(this.handleDelete, resp, options),
-            headers: options.headers
-        });
-
-        return resp;
-    },
-
-    /**
-     * Method: handleDelete
-     * Called the the request issued by <delete> is complete.  May be overridden
-     *     by subclasses.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
-     *     any user callback.
-     * options - {Object} The user options passed to the delete call.
-     */
-    handleDelete: function(resp, options) {
-        this.handleResponse(resp, options);
-    },
-
-    /**
-     * Method: handleResponse
-     * Called by CRUD specific handlers.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
-     *     any user callback.
-     * options - {Object} The user options passed to the create, read, update,
-     *     or delete call.
-     */
-    handleResponse: function(resp, options) {
-        var request = resp.priv;
-        if(options.callback) {
-            if(request.status >= 200 && request.status < 300) {
-                // success
-                if(resp.requestType != "delete") {
-                    resp.features = this.parseFeatures(request);
-                }
-                resp.code = OpenLayers.Protocol.Response.SUCCESS;
-            } else {
-                // failure
-                resp.code = OpenLayers.Protocol.Response.FAILURE;
-            }
-            options.callback.call(options.scope, resp);
-        }
-    },
-
-    /**
-     * Method: parseFeatures
-     * Read HTTP response body and return features.
-     *
-     * Parameters:
-     * request - {XMLHttpRequest} The request object
-     *
-     * Returns:
-     * {Array({<OpenLayers.Feature.Vector>})} or
-     *     {<OpenLayers.Feature.Vector>} Array of features or a single feature.
-     */
-    parseFeatures: function(request) {
-        var doc = request.responseXML;
-        if (!doc || !doc.documentElement) {
-            doc = request.responseText;
-        }
-        if (!doc || doc.length <= 0) {
-            return null;
-        }
-        return this.format.read(doc);
-    },
-
-    /**
-     * APIMethod: commit
-     * Iterate over each feature and take action based on the feature state.
-     *     Possible actions are create, update and delete.
-     *
-     * Parameters:
-     * features - {Array({<OpenLayers.Feature.Vector>})}
-     * options - {Object} Optional object for setting up intermediate commit
-     *     callbacks.
-     *
-     * Valid options:
-     * create - {Object} Optional object to be passed to the <create> method.
-     * update - {Object} Optional object to be passed to the <update> method.
-     * delete - {Object} Optional object to be passed to the <delete> method.
-     * callback - {Function} Optional function to be called when the commit
-     *     is complete.
-     * scope - {Object} Optional object to be set as the scope of the callback.
-     *
-     * Returns:
-     * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
-     *     one per request made to the server, each object's "priv" property
-     *     references the corresponding HTTP request.
-     */
-    commit: function(features, options) {
-        options = OpenLayers.Util.applyDefaults(options, this.options);
-        var resp = [], nResponses = 0;
-        
-        // Divide up features before issuing any requests.  This properly
-        // counts requests in the event that any responses come in before
-        // all requests have been issued.
-        var types = {};
-        types[OpenLayers.State.INSERT] = [];
-        types[OpenLayers.State.UPDATE] = [];
-        types[OpenLayers.State.DELETE] = [];
-        var feature, list, requestFeatures = [];
-        for(var i=0, len=features.length; i<len; ++i) {
-            feature = features[i];
-            list = types[feature.state];
-            if(list) {
-                list.push(feature);
-                requestFeatures.push(feature); 
-            }
-        }
-        // tally up number of requests
-        var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
-            types[OpenLayers.State.UPDATE].length +
-            types[OpenLayers.State.DELETE].length;
-        
-        // This response will be sent to the final callback after all the others
-        // have been fired.
-        var success = true;
-        var finalResponse = new OpenLayers.Protocol.Response({
-            reqFeatures: requestFeatures        
-        });
-        
-        function insertCallback(response) {
-            var len = response.features ? response.features.length : 0;
-            var fids = new Array(len);
-            for(var i=0; i<len; ++i) {
-                fids[i] = response.features[i].fid;
-            }   
-            finalResponse.insertIds = fids;
-            callback.apply(this, [response]);
-        }
- 
-        function callback(response) {
-            this.callUserCallback(response, options);
-            success = success && response.success();
-            nResponses++;
-            if (nResponses >= nRequests) {
-                if (options.callback) {
-                    finalResponse.code = success ? 
-                        OpenLayers.Protocol.Response.SUCCESS :
-                        OpenLayers.Protocol.Response.FAILURE;
-                    options.callback.apply(options.scope, [finalResponse]);
-                }    
-            }
-        }
-
-        // start issuing requests
-        var queue = types[OpenLayers.State.INSERT];
-        if(queue.length > 0) {
-            resp.push(this.create(
-                queue, OpenLayers.Util.applyDefaults(
-                    {callback: insertCallback, scope: this}, options.create
-                )
-            ));
-        }
-        queue = types[OpenLayers.State.UPDATE];
-        for(var i=queue.length-1; i>=0; --i) {
-            resp.push(this.update(
-                queue[i], OpenLayers.Util.applyDefaults(
-                    {callback: callback, scope: this}, options.update
-                ))
-            );
-        }
-        queue = types[OpenLayers.State.DELETE];
-        for(var i=queue.length-1; i>=0; --i) {
-            resp.push(this["delete"](
-                queue[i], OpenLayers.Util.applyDefaults(
-                    {callback: callback, scope: this}, options["delete"]
-                ))
-            );
-        }
-        return resp;
-    },
-
-    /**
-     * APIMethod: abort
-     * Abort an ongoing request, the response object passed to
-     * this method must come from this HTTP protocol (as a result
-     * of a create, read, update, delete or commit operation).
-     *
-     * Parameters:
-     * response - {<OpenLayers.Protocol.Response>}
-     */
-    abort: function(response) {
-        if (response) {
-            response.priv.abort();
-        }
-    },
-
-    /**
-     * Method: callUserCallback
-     * This method is used from within the commit method each time an
-     *     an HTTP response is received from the server, it is responsible
-     *     for calling the user-supplied callbacks.
-     *
-     * Parameters:
-     * resp - {<OpenLayers.Protocol.Response>}
-     * options - {Object} The map of options passed to the commit call.
-     */
-    callUserCallback: function(resp, options) {
-        var opt = options[resp.requestType];
-        if(opt && opt.callback) {
-            opt.callback.call(opt.scope, resp);
-        }
-    },
-
-    CLASS_NAME: "OpenLayers.Protocol.HTTP" 
-});
-
-/**
- * Property: OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR
- * {Object} A private class-level property mapping the
- *     OpenLayers.Filter.Comparison types to the operation
- *     strings of the protocol.
- */
-(function() {
-    var o = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR = {};
-    o[OpenLayers.Filter.Comparison.EQUAL_TO]                 = "eq";
-    o[OpenLayers.Filter.Comparison.NOT_EQUAL_TO]             = "ne";
-    o[OpenLayers.Filter.Comparison.LESS_THAN]                = "lt";
-    o[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO]    = "lte";
-    o[OpenLayers.Filter.Comparison.GREATER_THAN]             = "gt";
-    o[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
-    o[OpenLayers.Filter.Comparison.LIKE]                     = "ilike";
-})();
-
-/* ======================================================================
     OpenLayers/Geometry/Curve.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -36811,7 +19988,7 @@
     OpenLayers/Geometry/LineString.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -37360,14 +20537,372 @@
         }
         return best;
     },
+    
+    /**
+     * APIMethod: simplify
+     * This function will return a simplified LineString.
+     * Simplification is based on the Douglas-Peucker algorithm.
+     *
+     *
+     * Parameters:
+     * tolerance - {number} threshhold for simplification in map units
+     *
+     * Returns:
+     * {OpenLayers.Geometry.LineString} the simplified LineString
+     */
+    simplify: function(tolerance){
+        if (this && this !== null) {
+            var points = this.getVertices();
+            if (points.length < 3) {
+                return this;
+            }
+    
+            var compareNumbers = function(a, b){
+                return (a-b);
+            };
+    
+            /**
+             * Private function doing the Douglas-Peucker reduction
+             */
+            var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){
+                var maxDistance = 0;
+                var indexFarthest = 0;
+    
+                for (var index = firstPoint, distance; index < lastPoint; index++) {
+                    distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
+                    if (distance > maxDistance) {
+                        maxDistance = distance;
+                        indexFarthest = index;
+                    }
+                }
+    
+                if (maxDistance > tolerance && indexFarthest != firstPoint) {
+                    //Add the largest point that exceeds the tolerance
+                    pointIndexsToKeep.push(indexFarthest);
+                    douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
+                    douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
+                }
+            };
+    
+            /**
+             * Private function calculating the perpendicular distance
+             * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower
+             */
+            var perpendicularDistance = function(point1, point2, point){
+                //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)|   *Area of triangle
+                //Base = v((x1-x2)²+(x1-x2)²)                               *Base of Triangle*
+                //Area = .5*Base*H                                          *Solve for height
+                //Height = Area/.5/Base
+    
+                var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));
+                var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
+                var height = area / bottom * 2;
+    
+                return height;
+            };
+    
+            var firstPoint = 0;
+            var lastPoint = points.length - 1;
+            var pointIndexsToKeep = [];
+    
+            //Add the first and last index to the keepers
+            pointIndexsToKeep.push(firstPoint);
+            pointIndexsToKeep.push(lastPoint);
+    
+            //The first and the last point cannot be the same
+            while (points[firstPoint].equals(points[lastPoint])) {
+                lastPoint--;
+                //Addition: the first point not equal to first point in the LineString is kept as well
+                pointIndexsToKeep.push(lastPoint);
+            }
+    
+            douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
+            var returnPoints = [];
+            pointIndexsToKeep.sort(compareNumbers);
+            for (var index = 0; index < pointIndexsToKeep.length; index++) {
+                returnPoints.push(points[pointIndexsToKeep[index]]);
+            }
+            return new OpenLayers.Geometry.LineString(returnPoints);
+    
+        }
+        else {
+            return this;
+        }
+    },
 
     CLASS_NAME: "OpenLayers.Geometry.LineString"
 });
 /* ======================================================================
+    OpenLayers/Geometry/MultiLineString.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Geometry/Collection.js
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.MultiLineString
+ * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
+ * components.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Geometry.Collection>
+ *  - <OpenLayers.Geometry> 
+ */
+OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
+  OpenLayers.Geometry.Collection, {
+
+    /**
+     * Property: componentTypes
+     * {Array(String)} An array of class names representing the types of
+     * components that the collection can include.  A null value means the
+     * component types are not restricted.
+     */
+    componentTypes: ["OpenLayers.Geometry.LineString"],
+
+    /**
+     * Constructor: OpenLayers.Geometry.MultiLineString
+     * Constructor for a MultiLineString Geometry.
+     *
+     * Parameters: 
+     * components - {Array(<OpenLayers.Geometry.LineString>)} 
+     *
+     */
+    initialize: function(components) {
+        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, 
+                                                                  arguments);        
+    },
+    
+    /**
+     * 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(geometry, options) {
+        var results = null;
+        var mutual = options && options.mutual;
+        var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
+        var sourceParts = [];
+        var targetParts = [geometry];
+        for(var i=0, len=this.components.length; i<len; ++i) {
+            sourceLine = this.components[i];
+            sourceSplit = false;
+            for(var j=0; j < targetParts.length; ++j) { 
+                splits = sourceLine.split(targetParts[j], options);
+                if(splits) {
+                    if(mutual) {
+                        sourceLines = splits[0];
+                        for(var k=0, klen=sourceLines.length; k<klen; ++k) {
+                            if(k===0 && sourceParts.length) {
+                                sourceParts[sourceParts.length-1].addComponent(
+                                    sourceLines[k]
+                                );
+                            } else {
+                                sourceParts.push(
+                                    new OpenLayers.Geometry.MultiLineString([
+                                        sourceLines[k]
+                                    ])
+                                );
+                            }
+                        }
+                        sourceSplit = true;
+                        splits = splits[1];
+                    }
+                    if(splits.length) {
+                        // splice in new target parts
+                        splits.unshift(j, 1);
+                        Array.prototype.splice.apply(targetParts, splits);
+                        break;
+                    }
+                }
+            }
+            if(!sourceSplit) {
+                // source line was not hit
+                if(sourceParts.length) {
+                    // add line to existing multi
+                    sourceParts[sourceParts.length-1].addComponent(
+                        sourceLine.clone()
+                    );
+                } else {
+                    // create a fresh multi
+                    sourceParts = [
+                        new OpenLayers.Geometry.MultiLineString(
+                            sourceLine.clone()
+                        )
+                    ];
+                }
+            }
+        }
+        if(sourceParts && sourceParts.length > 1) {
+            sourceSplit = true;
+        } else {
+            sourceParts = [];
+        }
+        if(targetParts && targetParts.length > 1) {
+            targetSplit = true;
+        } else {
+            targetParts = [];
+        }
+        if(sourceSplit || targetSplit) {
+            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) {
+        var results = null;
+        var mutual = options && options.mutual;
+        var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
+        if(geometry instanceof OpenLayers.Geometry.LineString) {
+            targetParts = [];
+            sourceParts = [geometry];
+            for(var i=0, len=this.components.length; i<len; ++i) {
+                targetSplit = false;
+                targetLine = this.components[i];
+                for(var j=0; j<sourceParts.length; ++j) {
+                    splits = sourceParts[j].split(targetLine, options);
+                    if(splits) {
+                        if(mutual) {
+                            sourceLines = splits[0];
+                            if(sourceLines.length) {
+                                // splice in new source parts
+                                sourceLines.unshift(j, 1);
+                                Array.prototype.splice.apply(sourceParts, sourceLines);
+                                j += sourceLines.length - 2;
+                            }
+                            splits = splits[1];
+                            if(splits.length === 0) {
+                                splits = [targetLine.clone()];
+                            }
+                        }
+                        for(var k=0, klen=splits.length; k<klen; ++k) {
+                            if(k===0 && targetParts.length) {
+                                targetParts[targetParts.length-1].addComponent(
+                                    splits[k]
+                                );
+                            } else {
+                                targetParts.push(
+                                    new OpenLayers.Geometry.MultiLineString([
+                                        splits[k]
+                                    ])
+                                );
+                            }
+                        }
+                        targetSplit = true;                    
+                    }
+                }
+                if(!targetSplit) {
+                    // target component was not hit
+                    if(targetParts.length) {
+                        // add it to any existing multi-line
+                        targetParts[targetParts.length-1].addComponent(
+                            targetLine.clone()
+                        );
+                    } else {
+                        // or start with a fresh multi-line
+                        targetParts = [
+                            new OpenLayers.Geometry.MultiLineString([
+                                targetLine.clone()
+                            ])
+                        ];
+                    }
+                    
+                }
+            }
+        } else {
+            results = geometry.split(this);
+        }
+        if(sourceParts && sourceParts.length > 1) {
+            sourceSplit = true;
+        } else {
+            sourceParts = [];
+        }
+        if(targetParts && targetParts.length > 1) {
+            targetSplit = true;
+        } else {
+            targetParts = [];
+        }
+        if(sourceSplit || targetSplit) {
+            if(mutual) {
+                results = [sourceParts, targetParts];
+            } else {
+                results = targetParts;
+            }
+        }
+        return results;
+    },
+
+    CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
+});
+/* ======================================================================
     OpenLayers/Geometry/LinearRing.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -37785,582 +21320,10 @@
     CLASS_NAME: "OpenLayers.Geometry.LinearRing"
 });
 /* ======================================================================
-    OpenLayers/Geometry/MultiLineString.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Geometry/Collection.js
- * @requires OpenLayers/Geometry/LineString.js
- */
-
-/**
- * Class: OpenLayers.Geometry.MultiLineString
- * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
- * components.
- * 
- * Inherits from:
- *  - <OpenLayers.Geometry.Collection>
- *  - <OpenLayers.Geometry> 
- */
-OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
-  OpenLayers.Geometry.Collection, {
-
-    /**
-     * Property: componentTypes
-     * {Array(String)} An array of class names representing the types of
-     * components that the collection can include.  A null value means the
-     * component types are not restricted.
-     */
-    componentTypes: ["OpenLayers.Geometry.LineString"],
-
-    /**
-     * Constructor: OpenLayers.Geometry.MultiLineString
-     * Constructor for a MultiLineString Geometry.
-     *
-     * Parameters: 
-     * components - {Array(<OpenLayers.Geometry.LineString>)} 
-     *
-     */
-    initialize: function(components) {
-        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, 
-                                                                  arguments);        
-    },
-    
-    /**
-     * 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(geometry, options) {
-        var results = null;
-        var mutual = options && options.mutual;
-        var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
-        var sourceParts = [];
-        var targetParts = [geometry];
-        for(var i=0, len=this.components.length; i<len; ++i) {
-            sourceLine = this.components[i];
-            sourceSplit = false;
-            for(var j=0; j < targetParts.length; ++j) { 
-                splits = sourceLine.split(targetParts[j], options);
-                if(splits) {
-                    if(mutual) {
-                        sourceLines = splits[0];
-                        for(var k=0, klen=sourceLines.length; k<klen; ++k) {
-                            if(k===0 && sourceParts.length) {
-                                sourceParts[sourceParts.length-1].addComponent(
-                                    sourceLines[k]
-                                );
-                            } else {
-                                sourceParts.push(
-                                    new OpenLayers.Geometry.MultiLineString([
-                                        sourceLines[k]
-                                    ])
-                                );
-                            }
-                        }
-                        sourceSplit = true;
-                        splits = splits[1];
-                    }
-                    if(splits.length) {
-                        // splice in new target parts
-                        splits.unshift(j, 1);
-                        Array.prototype.splice.apply(targetParts, splits);
-                        break;
-                    }
-                }
-            }
-            if(!sourceSplit) {
-                // source line was not hit
-                if(sourceParts.length) {
-                    // add line to existing multi
-                    sourceParts[sourceParts.length-1].addComponent(
-                        sourceLine.clone()
-                    );
-                } else {
-                    // create a fresh multi
-                    sourceParts = [
-                        new OpenLayers.Geometry.MultiLineString(
-                            sourceLine.clone()
-                        )
-                    ];
-                }
-            }
-        }
-        if(sourceParts && sourceParts.length > 1) {
-            sourceSplit = true;
-        } else {
-            sourceParts = [];
-        }
-        if(targetParts && targetParts.length > 1) {
-            targetSplit = true;
-        } else {
-            targetParts = [];
-        }
-        if(sourceSplit || targetSplit) {
-            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) {
-        var results = null;
-        var mutual = options && options.mutual;
-        var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
-        if(geometry instanceof OpenLayers.Geometry.LineString) {
-            targetParts = [];
-            sourceParts = [geometry];
-            for(var i=0, len=this.components.length; i<len; ++i) {
-                targetSplit = false;
-                targetLine = this.components[i];
-                for(var j=0; j<sourceParts.length; ++j) {
-                    splits = sourceParts[j].split(targetLine, options);
-                    if(splits) {
-                        if(mutual) {
-                            sourceLines = splits[0];
-                            if(sourceLines.length) {
-                                // splice in new source parts
-                                sourceLines.unshift(j, 1);
-                                Array.prototype.splice.apply(sourceParts, sourceLines);
-                                j += sourceLines.length - 2;
-                            }
-                            splits = splits[1];
-                            if(splits.length === 0) {
-                                splits = [targetLine.clone()];
-                            }
-                        }
-                        for(var k=0, klen=splits.length; k<klen; ++k) {
-                            if(k===0 && targetParts.length) {
-                                targetParts[targetParts.length-1].addComponent(
-                                    splits[k]
-                                );
-                            } else {
-                                targetParts.push(
-                                    new OpenLayers.Geometry.MultiLineString([
-                                        splits[k]
-                                    ])
-                                );
-                            }
-                        }
-                        targetSplit = true;                    
-                    }
-                }
-                if(!targetSplit) {
-                    // target component was not hit
-                    if(targetParts.length) {
-                        // add it to any existing multi-line
-                        targetParts[targetParts.length-1].addComponent(
-                            targetLine.clone()
-                        );
-                    } else {
-                        // or start with a fresh multi-line
-                        targetParts = [
-                            new OpenLayers.Geometry.MultiLineString([
-                                targetLine.clone()
-                            ])
-                        ];
-                    }
-                    
-                }
-            }
-        } else {
-            results = geometry.split(this);
-        }
-        if(sourceParts && sourceParts.length > 1) {
-            sourceSplit = true;
-        } else {
-            sourceParts = [];
-        }
-        if(targetParts && targetParts.length > 1) {
-            targetSplit = true;
-        } else {
-            targetParts = [];
-        }
-        if(sourceSplit || targetSplit) {
-            if(mutual) {
-                results = [sourceParts, targetParts];
-            } else {
-                results = targetParts;
-            }
-        }
-        return results;
-    },
-
-    CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
-});
-/* ======================================================================
-    OpenLayers/Handler/Path.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Handler/Point.js
- * @requires OpenLayers/Geometry/Point.js
- * @requires OpenLayers/Geometry/LineString.js
- */
-
-/**
- * Class: OpenLayers.Handler.Path
- * Handler to draw a path on the map.  Path is displayed on mouse down,
- * moves on mouse move, and is finished on mouse up.
- *
- * Inherits from:
- *  - <OpenLayers.Handler.Point>
- */
-OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
-    
-    /**
-     * Property: line
-     * {<OpenLayers.Feature.Vector>}
-     */
-    line: null,
-    
-    /**
-     * Property: freehand
-     * {Boolean} In freehand mode, the handler starts the path on mouse down,
-     * adds a point for every mouse move, and finishes the path on mouse up.
-     * Outside of freehand mode, a point is added to the path on every mouse
-     * click and double-click finishes the path.
-     */
-    freehand: false,
-    
-    /**
-     * Property: freehandToggle
-     * {String} If set, freehandToggle is checked on mouse events and will set
-     * the freehand mode to the opposite of this.freehand.  To disallow
-     * toggling between freehand and non-freehand mode, set freehandToggle to
-     * null.  Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
-     */
-    freehandToggle: 'shiftKey',
-
-    /**
-     * Constructor: OpenLayers.Handler.Path
-     * Create a new path hander
-     *
-     * Parameters:
-     * 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:
-     * create - Called when a sketch is first created.  Callback called with
-     *     the creation point geometry and sketch feature.
-     * modify - Called with each move of a vertex with the vertex (point)
-     *     geometry and the sketch feature.
-     * point - Called as each point is added.  Receives the new point geometry.
-     * done - Called when the point drawing is finished.  The callback will
-     *     recieve a single argument, the linestring geometry.
-     * 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);
-    },
-        
-    /**
-     * Method: createFeature
-     * Add temporary geometries
-     *
-     * Parameters:
-     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
-     *     feature.
-     */
-    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.geometry])
-        );
-        this.callback("create", [this.point.geometry, this.getSketch()]);
-        this.point.geometry.clearBounds();
-        this.layer.addFeatures([this.line, this.point], {silent: true});
-    },
-        
-    /**
-     * Method: destroyFeature
-     * Destroy temporary geometries
-     */
-    destroyFeature: function() {
-        OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);
-        this.line = null;
-    },
-
-    /**
-     * Method: removePoint
-     * Destroy the temporary point.
-     */
-    removePoint: function() {
-        if(this.point) {
-            this.layer.removeFeatures([this.point]);
-        }
-    },
-    
-    /**
-     * 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(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();
-    },
-    
-    /**
-     * Method: freehandMode
-     * Determine whether to behave in freehand mode or not.
-     *
-     * Returns:
-     * {Boolean}
-     */
-    freehandMode: function(evt) {
-        return (this.freehandToggle && evt[this.freehandToggle]) ?
-                    !this.freehand : this.freehand;
-    },
-
-    /**
-     * Method: modifyFeature
-     * Modify the existing geometry given the new point
-     *
-     * Parameters:
-     * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
-     *     point.
-     */
-    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.
-     */
-    drawFeature: function() {
-        this.layer.drawFeature(this.line, this.style);
-        this.layer.drawFeature(this.point, this.style);
-    },
-
-    /**
-     * 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.
-     *
-     * Returns:
-     * {<OpenLayers.Geometry.LineString>}
-     */
-    getGeometry: function() {
-        var geometry = this.line && this.line.geometry;
-        if(geometry && this.multi) {
-            geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
-        }
-        return geometry;
-    },
-
-    /**
-     * Method: mousedown
-     * Handle mouse down.  Add a new point to the geometry and
-     * render it. Return determines whether to propagate the event on the map.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousedown: function(evt) {
-        // ignore double-clicks
-        if (this.lastDown && this.lastDown.equals(evt.xy)) {
-            return false;
-        }
-        if(this.lastDown == null) {
-            if(this.persist) {
-                this.destroyFeature();
-            }
-            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;
-        this.drawing = true;
-        return false;
-    },
-
-    /**
-     * Method: mousemove
-     * Handle mouse move.  Adjust the geometry and redraw.
-     * Return determines whether to propagate the event on the map.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mousemove: function (evt) {
-        if(this.drawing) { 
-            if(this.mouseDown && this.freehandMode(evt)) {
-                this.addPoint(evt.xy);
-            } else {
-                this.modifyFeature(evt.xy);
-            }
-        }
-        return true;
-    },
-    
-    /**
-     * Method: mouseup
-     * Handle mouse up.  Send the latest point in the geometry to
-     * the control. Return determines whether to propagate the event on the map.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    mouseup: function (evt) {
-        this.mouseDown = false;
-        if(this.drawing) {
-            if(this.freehandMode(evt)) {
-                this.removePoint();
-                this.finalize();
-            } else {
-                if(this.lastUp == null) {
-                   this.addPoint(evt.xy);
-                }
-                this.lastUp = evt.xy;
-            }
-            return false;
-        }
-        return true;
-    },
-  
-    /**
-     * Method: dblclick 
-     * Handle double-clicks.  Finish the geometry and send it back
-     * to the control.
-     * 
-     * Parameters:
-     * evt - {Event} The browser event
-     *
-     * Returns: 
-     * {Boolean} Allow event propagation
-     */
-    dblclick: function(evt) {
-        if(!this.freehandMode(evt)) {
-            var index = this.line.geometry.components.length - 1;
-            this.line.geometry.removeComponent(this.line.geometry.components[index]);
-            this.removePoint();
-            this.finalize();
-        }
-        return false;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Path"
-});
-/* ======================================================================
     OpenLayers/Geometry/Polygon.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -38623,7 +21586,7 @@
     OpenLayers/Geometry/MultiPolygon.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -38670,158 +21633,10 @@
     CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
 });
 /* ======================================================================
-    OpenLayers/Handler/Polygon.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Handler/Path.js
- * @requires OpenLayers/Geometry/Polygon.js
- */
-
-/**
- * Class: OpenLayers.Handler.Polygon
- * Handler to draw a polygon on the map.  Polygon is displayed on mouse down,
- * moves on mouse move, and is finished on mouse up.
- *
- * Inherits from:
- *  - <OpenLayers.Handler.Path>
- *  - <OpenLayers.Handler>
- */
-OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
-    
-    /**
-     * Parameter: polygon
-     * {<OpenLayers.Feature.Vector>}
-     */
-    polygon: null,
-
-    /**
-     * Constructor: OpenLayers.Handler.Polygon
-     * Create a Polygon Handler.
-     *
-     * Parameters:
-     * 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:
-     * create - Called when a sketch is first created.  Callback called with
-     *     the creation point geometry and sketch feature.
-     * modify - Called with each move of a vertex with the vertex (point)
-     *     geometry and the sketch feature.
-     * point - Called as each point is added.  Receives the new point geometry.
-     * done - Called when the point drawing is finished.  The callback will
-     *     recieve a single argument, the polygon geometry.
-     * 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);
-    },
-    
-    /**
-     * Method: createFeature
-     * Add temporary geometries
-     *
-     * Parameters:
-     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
-     *     feature.
-     */
-    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.geometry])
-        );
-        this.callback("create", [this.point.geometry, this.getSketch()]);
-        this.point.geometry.clearBounds();
-        this.layer.addFeatures([this.polygon, this.point], {silent: true});
-    },
-
-    /**
-     * Method: destroyFeature
-     * Destroy temporary geometries
-     */
-    destroyFeature: function() {
-        OpenLayers.Handler.Path.prototype.destroyFeature.apply(this);
-        this.polygon = null;
-    },
-
-    /**
-     * Method: drawFeature
-     * Render geometries on the temporary layer.
-     */
-    drawFeature: function() {
-        this.layer.drawFeature(this.polygon, this.style);
-        this.layer.drawFeature(this.point, this.style);
-    },
-    
-    /**
-     * 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.
-     *
-     * Returns:
-     * {<OpenLayers.Geometry.Polygon>}
-     */
-    getGeometry: function() {
-        var geometry = this.polygon && this.polygon.geometry;
-        if(geometry && this.multi) {
-            geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
-        }
-        return geometry;
-    },
-
-    /**
-     * Method: dblclick
-     * Handle double-clicks.  Finish the geometry and send it back
-     * to the control.
-     * 
-     * Parameters:
-     * evt - {Event} 
-     */
-    dblclick: function(evt) {
-        if(!this.freehandMode(evt)) {
-            // remove the penultimate point
-            var index = this.line.geometry.components.length - 2;
-            this.line.geometry.removeComponent(this.line.geometry.components[index]);
-            this.removePoint();
-            this.finalize();
-        }
-        return false;
-    },
-
-    CLASS_NAME: "OpenLayers.Handler.Polygon"
-});
-/* ======================================================================
     OpenLayers/Format/GML.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -38836,6 +21651,7 @@
  * @requires OpenLayers/Geometry/Polygon.js
  * @requires OpenLayers/Geometry/MultiPolygon.js
  * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
  */
 
 /**
@@ -39749,7 +22565,7 @@
     OpenLayers/Format/GML/Base.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -40328,207 +23144,10 @@
 
 });
 /* ======================================================================
-    OpenLayers/Format/GML/v2.js
-   ====================================================================== */
-
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
- * full list of contributors). Published under the Clear BSD license.  
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-/**
- * @requires OpenLayers/Format/GML/Base.js
- */
-
-/**
- * Class: OpenLayers.Format.GML.v2
- * Parses GML version 2.
- *
- * Inherits from:
- *  - <OpenLayers.Format.GML.Base>
- */
-OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
-    
-    /**
-     * Property: schemaLocation
-     * {String} Schema location for a particular minor version.
-     */
-    schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
-
-    /**
-     * Constructor: OpenLayers.Format.GML.v2
-     * Create a parser for GML v2.
-     *
-     * Parameters:
-     * options - {Object} An optional object whose properties will be set on
-     *     this instance.
-     *
-     * Valid options properties:
-     * featureType - {String} Local (without prefix) feature typeName (required).
-     * featureNS - {String} Feature namespace (required).
-     * geometryName - {String} Geometry element name.
-     */
-    initialize: function(options) {
-        OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
-    },
-
-    /**
-     * Property: readers
-     * Contains public functions, grouped by namespace prefix, that will
-     *     be applied when a namespaced node is found matching the function
-     *     name.  The function will be applied in the scope of this parser
-     *     with two arguments: the node being read and a context object passed
-     *     from the parent.
-     */
-    readers: {
-        "gml": OpenLayers.Util.applyDefaults({
-            "outerBoundaryIs": function(node, container) {
-                var obj = {};
-                this.readChildNodes(node, obj);
-                container.outer = obj.components[0];
-            },
-            "innerBoundaryIs": function(node, container) {
-                var obj = {};
-                this.readChildNodes(node, obj);
-                container.inner.push(obj.components[0]);
-            },
-            "Box": function(node, container) {
-                var obj = {};
-                this.readChildNodes(node, obj);
-                if(!container.components) {
-                    container.components = [];
-                }
-                var min = obj.points[0];
-                var max = obj.points[1];
-                container.components.push(
-                    new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
-                );
-            }
-        }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
-        "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
-        "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
-    },
-
-    /**
-     * Method: write
-     *
-     * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
-     *     An array of features or a single feature.
-     *
-     * Returns:
-     * {String} Given an array of features, a doc with a gml:featureMembers
-     *     element will be returned.  Given a single feature, a doc with a
-     *     gml:featureMember element will be returned.
-     */
-    write: function(features) {
-        var name;
-        if(features instanceof Array) {
-            // GML2 only has abstract feature collections
-            // wfs provides a feature collection from a well-known schema
-            name = "wfs:FeatureCollection";
-        } else {
-            name = "gml:featureMember";
-        }
-        var root = this.writeNode(name, features);
-        this.setAttributeNS(
-            root, this.namespaces["xsi"],
-            "xsi:schemaLocation", this.schemaLocation
-        );
-
-        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
-    },
-
-    /**
-     * Property: writers
-     * As a compliment to the readers property, this structure contains public
-     *     writing functions grouped by namespace alias and named like the
-     *     node names they produce.
-     */
-    writers: {
-        "gml": OpenLayers.Util.applyDefaults({
-            "Point": function(geometry) {
-                var node = this.createElementNSPlus("gml:Point");
-                this.writeNode("coordinates", [geometry], node);
-                return node;
-            },
-            "coordinates": function(points) {
-                var numPoints = points.length;
-                var parts = new Array(numPoints);
-                var point;
-                for(var i=0; i<numPoints; ++i) {
-                    point = points[i];
-                    if(this.xy) {
-                        parts[i] = point.x + "," + point.y;
-                    } else {
-                        parts[i] = point.y + "," + point.x;
-                    }
-                    if(point.z != undefined) { // allow null or undefined
-                        parts[i] += "," + point.z;
-                    }
-                }
-                return this.createElementNSPlus("gml:coordinates", {
-                    attributes: {
-                        decimal: ".", cs: ",", ts: " "
-                    },
-                    value: (numPoints == 1) ? parts[0] : parts.join(" ")
-                });
-            },
-            "LineString": function(geometry) {
-                var node = this.createElementNSPlus("gml:LineString");
-                this.writeNode("coordinates", geometry.components, node);
-                return node;
-            },
-            "Polygon": function(geometry) {
-                var node = this.createElementNSPlus("gml:Polygon");
-                this.writeNode("outerBoundaryIs", geometry.components[0], node);
-                for(var i=1; i<geometry.components.length; ++i) {
-                    this.writeNode(
-                        "innerBoundaryIs", geometry.components[i], node
-                    );
-                }
-                return node;
-            },
-            "outerBoundaryIs": function(ring) {
-                var node = this.createElementNSPlus("gml:outerBoundaryIs");
-                this.writeNode("LinearRing", ring, node);
-                return node;
-            },
-            "innerBoundaryIs": function(ring) {
-                var node = this.createElementNSPlus("gml:innerBoundaryIs");
-                this.writeNode("LinearRing", ring, node);
-                return node;
-            },
-            "LinearRing": function(ring) {
-                var node = this.createElementNSPlus("gml:LinearRing");
-                this.writeNode("coordinates", ring.components, node);
-                return node;
-            },
-            "Box": function(bounds) {
-                var node = this.createElementNSPlus("gml:Box");
-                this.writeNode("coordinates", [
-                    {x: bounds.left, y: bounds.bottom},
-                    {x: bounds.right, y: bounds.top}
-                ], node);
-                // srsName attribute is optional for gml:Box
-                if(this.srsName) {
-                    node.setAttribute("srsName", this.srsName);
-                }
-                return node;
-            }
-        }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
-        "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
-        "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
-    },
-    
-    CLASS_NAME: "OpenLayers.Format.GML.v2" 
-
-});
-/* ======================================================================
     OpenLayers/Format/GML/v3.js
    ====================================================================== */
 
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for 
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
  * full list of contributors). Published under the Clear BSD license.  
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -40990,3 +23609,22975 @@
     CLASS_NAME: "OpenLayers.Format.GML.v3" 
 
 });
+/* ======================================================================
+    OpenLayers/Format/Filter/v1_1_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/Filter/v1.js
+ * @requires OpenLayers/Format/GML/v3.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1_1_0
+ * Write ogc:Filter version 1.1.0.
+ *
+ * Differences from the v1.0.0 parser:
+ *  - uses GML v3 instead of GML v2
+ *  - reads matchCase attribute on ogc:PropertyIsEqual and
+ *        ogc:PropertyIsNotEqualelements.
+ *  - writes matchCase attribute from comparison filters of type EQUAL_TO and
+ *        type NOT_EQUAL_TO.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.Filter.v1>
+ */
+OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class(
+    OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, {
+    
+    /**
+     * Constant: VERSION
+     * {String} 1.1.0
+     */
+    VERSION: "1.1.0",
+    
+    /**
+     * Property: schemaLocation
+     * {String} http://www.opengis.net/ogc/filter/1.1.0/filter.xsd
+     */
+    schemaLocation: "http://www.opengis.net/ogc/filter/1.1.0/filter.xsd",
+
+    /**
+     * Constructor: OpenLayers.Format.Filter.v1_1_0
+     * Instances of this class are not created directly.  Use the
+     *     <OpenLayers.Format.Filter> constructor instead.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.GML.v3.prototype.initialize.apply(
+            this, [options]
+        );
+    },
+
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "ogc": OpenLayers.Util.applyDefaults({
+            "PropertyIsEqualTo": function(node, obj) {
+                var matchCase = node.getAttribute("matchCase");
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                    matchCase: !(matchCase === "false" || matchCase === "0")
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsNotEqualTo": function(node, obj) {
+                var matchCase = node.getAttribute("matchCase");
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+                    matchCase: !(matchCase === "false" || matchCase === "0")
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsLike": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LIKE
+                });
+                this.readChildNodes(node, filter);
+                var wildCard = node.getAttribute("wildCard");
+                var singleChar = node.getAttribute("singleChar");
+                var esc = node.getAttribute("escapeChar");
+                filter.value2regex(wildCard, singleChar, esc);
+                obj.filters.push(filter);
+            }
+        }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
+        "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
+        "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"]        
+    },
+
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "ogc": OpenLayers.Util.applyDefaults({
+            "PropertyIsEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", {
+                    attributes: {matchCase: filter.matchCase}
+                });
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsNotEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", {
+                    attributes: {matchCase: filter.matchCase}
+                });
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsLike": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
+                    attributes: {
+                        wildCard: "*", singleChar: ".", escapeChar: "!"
+                    }
+                });
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                // convert regex string to ogc string
+                this.writeNode("Literal", filter.regex2value(), node);
+                return node;
+            },
+            "BBOX": function(filter) {
+                var node = this.createElementNSPlus("ogc:BBOX");
+                this.writeNode("PropertyName", filter, node);
+                var box = this.writeNode("gml:Envelope", filter.value);
+                if(filter.projection) {
+                    box.setAttribute("srsName", filter.projection);
+                }
+                node.appendChild(box); 
+                return node;
+            }}, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
+            
+        "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
+        "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"]
+    },
+
+    /**
+     * Method: writeSpatial
+     *
+     * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter.Spatial>} The filter.
+     * name - {String} Name of the generated XML element.
+     *
+     * Returns:
+     * {DOMElement} The created XML element.
+     */
+    writeSpatial: function(filter, name) {
+        var node = this.createElementNSPlus("ogc:"+name);
+        this.writeNode("PropertyName", filter, node);
+        var child;
+        if(filter.value instanceof OpenLayers.Geometry) {
+            child = this.writeNode("feature:_geometry", filter.value).firstChild;
+        } else {
+            child = this.writeNode("gml:Envelope", filter.value);
+        }
+        if(filter.projection) {
+            child.setAttribute("srsName", filter.projection);
+        }
+        node.appendChild(child);
+        return node;
+    },
+
+    CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0" 
+
+});
+/* ======================================================================
+    OpenLayers/Format/WFST/v1_1_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/WFST/v1.js
+ * @requires OpenLayers/Format/Filter/v1_1_0.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFST.v1_1_0
+ * A format for creating WFS v1.1.0 transactions.  Create a new instance with the
+ *     <OpenLayers.Format.WFST.v1_1_0> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.Filter.v1_1_0>
+ *  - <OpenLayers.Format.WFST.v1>
+ */
+OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(
+    OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {
+    
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: "1.1.0",
+    
+    /**
+     * Property: schemaLocations
+     * {Object} Properties are namespace aliases, values are schema locations.
+     */
+    schemaLocations: {
+        "wfs": "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"
+    },
+    
+    /**
+     * Constructor: OpenLayers.Format.WFST.v1_1_0
+     * A class for parsing and generating WFS v1.1.0 transactions.
+     *
+     * To read additional information like hit count (numberOfFeatures) from
+     * the  FeatureCollection, call the <OpenLayers.Format.WFST.v1.read> method
+     * with {output: "object"} as 2nd argument. Note that it is possible to
+     * just request the hit count from a WFS 1.1.0 server with the
+     * resultType="hits" request parameter.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options properties:
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (optional).
+     * featurePrefix - {String} Feature namespace alias (optional - only used
+     *     if featureNS is provided).  Default is 'feature'.
+     * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);
+        OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "wfs": OpenLayers.Util.applyDefaults({
+            "FeatureCollection": function(node, obj) {
+                obj.numberOfFeatures = parseInt(node.getAttribute(
+                    "numberOfFeatures"));
+                OpenLayers.Format.WFST.v1.prototype.readers["wfs"]["FeatureCollection"].apply(
+                    this, arguments);
+            },
+            "TransactionResponse": function(node, obj) {
+                obj.insertIds = [];
+                obj.success = false;
+                this.readChildNodes(node, obj);
+            },
+            "TransactionSummary": function(node, obj) {
+                // this is a limited test of success
+                obj.success = true;
+            },
+            "InsertResults": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "Feature": function(node, container) {
+                var obj = {fids: []};
+                this.readChildNodes(node, obj);
+                container.insertIds.push(obj.fids[0]);
+            }
+        }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
+        "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
+        "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"],
+        "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.readers["ogc"]
+    },
+
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "wfs": OpenLayers.Util.applyDefaults({
+            "GetFeature": function(options) {
+                var node = OpenLayers.Format.WFST.v1.prototype.writers["wfs"]["GetFeature"].apply(this, arguments);
+                options && options.resultType && this.setAttributes(node, {
+                    resultType: options.resultType
+                });
+                return node;
+            },
+            "Query": function(options) {
+                options = OpenLayers.Util.extend({
+                    featureNS: this.featureNS,
+                    featurePrefix: this.featurePrefix,
+                    featureType: this.featureType,
+                    srsName: this.srsName
+                }, options);
+                var node = this.createElementNSPlus("wfs:Query", {
+                    attributes: {
+                        typeName: (options.featureNS ? options.featurePrefix + ":" : "") +
+                            options.featureType,
+                        srsName: options.srsName
+                    }
+                });
+                if(options.featureNS) {
+                    node.setAttribute("xmlns:" + options.featurePrefix, options.featureNS);
+                }
+                if(options.propertyNames) {
+                    for(var i=0,len = options.propertyNames.length; i<len; i++) {
+                        this.writeNode(
+                            "wfs:PropertyName", 
+                            {property: options.propertyNames[i]},
+                            node
+                        );
+                    }
+                }
+                if(options.filter) {
+                    this.setFilterProperty(options.filter);
+                    this.writeNode("ogc:Filter", options.filter, node);
+                }
+                return node;
+            },
+            "PropertyName": function(obj) {
+                return this.createElementNSPlus("wfs:PropertyName", {
+                    value: obj.property
+                });
+            }            
+        }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
+        "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
+        "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"],
+        "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"]
+    },
+
+    CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0" 
+});
+/* ======================================================================
+    OpenLayers/Strategy.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy
+ * Abstract vector layer strategy class.  Not to be instantiated directly.  Use
+ *     one of the strategy subclasses instead.
+ */
+OpenLayers.Strategy = OpenLayers.Class({
+    
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
+     */
+    layer: null,
+    
+    /**
+     * Property: options
+     * {Object} Any options sent to the constructor.
+     */
+    options: null,
+
+    /** 
+     * Property: active 
+     * {Boolean} The control is active.
+     */
+    active: null,
+
+    /**
+     * Property: autoActivate
+     * {Boolean} The creator of the strategy can set autoActivate to false
+     *      to fully control when the protocol is activated and deactivated.
+     *      Defaults to true.
+     */
+    autoActivate: true,
+
+    /**
+     * Property: autoDestroy
+     * {Boolean} The creator of the strategy can set autoDestroy to false
+     *      to fully control when the strategy is destroyed. Defaults to
+     *      true.
+     */
+    autoDestroy: true,
+
+    /**
+     * Constructor: OpenLayers.Strategy
+     * Abstract class for vector strategies.  Create instances of a subclass.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Util.extend(this, options);
+        this.options = options;
+        // set the active property here, so that user cannot override it
+        this.active = false;
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Clean up the strategy.
+     */
+    destroy: function() {
+        this.deactivate();
+        this.layer = null;
+        this.options = null;
+    },
+
+    /**
+     * Method: setLayer
+     * Called to set the <layer> property.
+     *
+     * Parameters:
+     * {<OpenLayers.Layer.Vector>}
+     */
+    setLayer: function(layer) {
+        this.layer = layer;
+    },
+    
+    /**
+     * Method: activate
+     * Activate the strategy.  Register any listeners, do appropriate setup.
+     *
+     * Returns:
+     * {Boolean} True if the strategy was successfully activated or false if
+     *      the strategy was already active.
+     */
+    activate: function() {
+        if (!this.active) {
+            this.active = true;
+            return true;
+        }
+        return false;
+    },
+    
+    /**
+     * Method: deactivate
+     * Deactivate the strategy.  Unregister any listeners, do appropriate
+     *     tear-down.
+     *
+     * Returns:
+     * {Boolean} True if the strategy was successfully deactivated or false if
+     *      the strategy was already inactive.
+     */
+    deactivate: function() {
+        if (this.active) {
+            this.active = false;
+            return true;
+        }
+        return false;
+    },
+   
+    CLASS_NAME: "OpenLayers.Strategy" 
+});
+/* ======================================================================
+    OpenLayers/Strategy/Filter.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ * @requires OpenLayers/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Filter
+ * Strategy for limiting features that get added to a layer by 
+ *     evaluating a filter.  The strategy maintains a cache of
+ *     all features until removeFeatures is called on the layer.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * APIProperty: filter
+     * {<OpenLayers.Filter>}  Filter for limiting features sent to the layer.
+     *     Use the <setFilter> method to update this filter after construction.
+     */
+    filter: null,
+    
+    /**
+     * Property: cache
+     * {Array(<OpenLayers.Feature.Vector>)} List of currently cached
+     *     features.
+     */
+    cache: null,
+    
+    /**
+     * Property: caching
+     * {Boolean} The filter is currently caching features.
+     */
+    caching: false,
+    
+    /**
+     * Constructor: OpenLayers.Strategy.Filter
+     * Create a new filter strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * APIMethod: activate
+     * Activate the strategy.  Register any listeners, do appropriate setup.
+     *     By default, this strategy automatically activates itself when a layer
+     *     is added to a map.
+     *
+     * Returns:
+     * {Boolean} True if the strategy was successfully activated or false if
+     *      the strategy was already active.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
+        if (activated) {
+            this.cache = [];
+            this.layer.events.on({
+                "beforefeaturesadded": this.handleAdd,
+                "beforefeaturesremoved": this.handleRemove,
+                scope: this
+            });
+        }
+        return activated;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Deactivate the strategy.  Clear the feature cache.
+     *
+     * Returns:
+     * {Boolean} True if the strategy was successfully deactivated or false if
+     *      the strategy was already inactive.
+     */
+    deactivate: function() {
+        this.cache = null;
+        if (this.layer && this.layer.events) {
+            this.layer.events.un({
+                "beforefeaturesadded": this.handleAdd,
+                "beforefeaturesremoved": this.handleRemove,
+                scope: this
+            });            
+        }
+        return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
+    },
+    
+    /**
+     * Method: handleAdd
+     */
+    handleAdd: function(event) {
+        if (!this.caching && this.filter) {
+            var features = event.features;
+            event.features = [];
+            var feature;
+            for (var i=0, ii=features.length; i<ii; ++i) {
+                feature = features[i];
+                if (this.filter.evaluate(feature)) {
+                    event.features.push(feature);
+                } else {
+                    this.cache.push(feature);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method: handleRemove
+     */
+    handleRemove: function(event) {
+        if (!this.caching) {
+            this.cache = [];
+        }
+    },
+
+    /** 
+     * APIMethod: setFilter
+     * Update the filter for this strategy.  This will re-evaluate
+     *     any features on the layer and in the cache.  Only features
+     *     for which filter.evalute(feature) returns true will be
+     *     added to the layer.  Others will be cached by the strategy.
+     *
+     * Parameters:
+     * filter - <OpenLayers.Filter> A filter for evaluating features.
+     */
+    setFilter: function(filter) {
+        this.filter = filter;
+        var previousCache = this.cache;
+        this.cache = [];
+        // look through layer for features to remove from layer
+        this.handleAdd({features: this.layer.features});
+        // cache now contains features to remove from layer
+        if (this.cache.length > 0) {
+            this.caching = true;
+            this.layer.removeFeatures(this.cache.slice(), {silent: true});
+            this.caching = false;
+        }
+        // now look through previous cache for features to add to layer
+        if (previousCache.length > 0) {
+            var event = {features: previousCache};
+            this.handleAdd(event);
+            // event has features to add to layer
+            this.caching = true;
+            this.layer.addFeatures(event.features, {silent: true});
+            this.caching = false;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Strategy.Filter"
+
+});
+/* ======================================================================
+    OpenLayers/Format/WFSCapabilities.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/XML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFSCapabilities
+ * Read WFS Capabilities.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML, {
+    
+    /**
+     * APIProperty: defaultVersion
+     * {String} Version number to assume if none found.  Default is "1.1.0".
+     */
+    defaultVersion: "1.1.0",
+    
+    /**
+     * APIProperty: version
+     * {String} Specify a version string if one is known.
+     */
+    version: null,
+
+    /**
+     * Constructor: OpenLayers.Format.WFSCapabilities
+     * Create a new parser for WFS capabilities.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+        this.options = options;
+    },
+
+    /**
+     * APIMethod: read
+     * Read capabilities data from a string, and return a list of layers. 
+     * 
+     * Parameters: 
+     * data - {String} or {DOMElement} data to read/parse.
+     *
+     * Returns:
+     * {Array} List of named layers.
+     */
+    read: function(data) {
+        if(typeof data == "string") {
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+        }
+        var root = data.documentElement;
+        var version = this.version;
+        if(!version) {
+            version = root.getAttribute("version");
+            if(!version) {
+                version = this.defaultVersion;
+            }
+        }
+        var constr = OpenLayers.Format.WFSCapabilities[
+            "v" + version.replace(/\./g, "_")
+        ];
+        if(!constr) {
+            throw "Can't find a WFS capabilities parser for version " + version;
+        }
+        var parser = new constr(this.options);
+        var capabilities = parser.read(data);
+        capabilities.version = version;
+        return capabilities;
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.WFSCapabilities" 
+
+});
+/* ======================================================================
+    OpenLayers/Format/WFSCapabilities/v1.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/WFSCapabilities.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFSCapabilities.v1
+ * Abstract class not to be instantiated directly.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(
+    OpenLayers.Format.WFSCapabilities, {
+    
+    /**
+     * Constructor: OpenLayers.Format.WFSCapabilities.v1_1
+     * Create an instance of one of the subclasses.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+        this.options = options;
+    },
+
+    /**
+     * APIMethod: read
+     * Read capabilities data from a string, and return a list of layers. 
+     * 
+     * Parameters: 
+     * data - {String} or {DOMElement} data to read/parse.
+     *
+     * Returns:
+     * {Array} List of named layers.
+     */
+    read: function(data) {
+        if(typeof data == "string") {
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+        }
+        var capabilities = {};
+        var root = data.documentElement;
+        this.runChildNodes(capabilities, root);
+        return capabilities;
+    },
+    
+    /**
+     * Method: runChildNodes
+     */
+    runChildNodes: function(obj, node) {
+        var children = node.childNodes;
+        var childNode, processor;
+        for(var i=0; i<children.length; ++i) {
+            childNode = children[i];
+            if(childNode.nodeType == 1) {
+                processor = this["read_cap_" + childNode.nodeName];
+                if(processor) {
+                    processor.apply(this, [obj, childNode]);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method: read_cap_FeatureTypeList
+     */
+    read_cap_FeatureTypeList: function(request, node) {
+        var featureTypeList = {
+            featureTypes: []
+        };
+        this.runChildNodes(featureTypeList, node);
+        request.featureTypeList = featureTypeList;
+    },
+    
+    /**
+     * Method: read_cap_FeatureType
+     */
+    read_cap_FeatureType: function(featureTypeList, node, parentLayer) {
+        var featureType = {};
+        this.runChildNodes(featureType, node);
+        featureTypeList.featureTypes.push(featureType);
+    },
+    
+    /**
+     * Method: read_cap_Name
+     */
+    read_cap_Name: function(obj, node) {
+        var name = this.getChildValue(node);
+        if(name) {
+            var parts = name.split(":");
+            obj.name = parts.pop();
+            if(parts.length > 0) {
+                obj.featureNS = this.lookupNamespaceURI(node, parts[0]);
+            }
+        }
+    },
+
+    /**
+     * Method: read_cap_Title
+     */
+    read_cap_Title: function(obj, node) {
+        var title = this.getChildValue(node);
+        if(title) {
+            obj.title = title;
+        }
+    },
+
+    /**
+     * Method: read_cap_Abstract
+     */
+    read_cap_Abstract: function(obj, node) {
+        var abst = this.getChildValue(node);
+        if(abst) {
+            obj["abstract"] = abst;
+        }
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1" 
+
+});
+/* ======================================================================
+    OpenLayers/Format/WFSCapabilities/v1_1_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/WFSCapabilities/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFSCapabilities/v1_1_0
+ * Read WFS Capabilities version 1.1.0.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.WFSCapabilities>
+ */
+OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(
+    OpenLayers.Format.WFSCapabilities.v1, {
+    
+    /**
+     * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0
+     * Create a new parser for WFS capabilities version 1.1.0.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.WFSCapabilities.v1.prototype.initialize.apply(
+            this, [options]
+        );
+    },
+
+    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0" 
+
+});
+/* ======================================================================
+    OpenLayers/Strategy/Save.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Save
+ * A strategy that commits newly created or modified features.  By default
+ *     the strategy waits for a call to <save> before persisting changes.  By
+ *     configuring the strategy with the <auto> option, changes can be saved
+ *     automatically.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * strategy.events.register(type, obj, listener);
+     * (end)
+     *
+     *  - *start* Triggered before saving
+     *  - *success* Triggered after a successful transaction
+     *  - *fail* Triggered after a failed transaction
+     *      
+     */
+    EVENT_TYPES: ["start", "success", "fail"],
+ 
+    /** 
+     * Property: events
+     * {<OpenLayers.Events>} Events instance for triggering this protocol
+     *    events.
+     */
+    events: null,
+    
+    /**
+     * APIProperty: auto
+     * {Boolean | Number} Auto-save.  Default is false.  If true, features will be
+     *     saved immediately after being added to the layer and with each
+     *     modification or deletion.  If auto is a number, features will be
+     *     saved on an interval provided by the value (in seconds).
+     */
+    auto: false,
+    
+    /**
+     * Property: timer
+     * {Number} The id of the timer.
+     */
+    timer: null,
+
+    /**
+     * Constructor: OpenLayers.Strategy.Save
+     * Create a new Save strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
+    },
+   
+    /**
+     * APIMethod: activate
+     * Activate the strategy.  Register any listeners, do appropriate setup.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.call(this);
+        if(activated) {
+            if(this.auto) {
+                if(typeof this.auto === "number") {
+                    this.timer = window.setInterval(
+                        OpenLayers.Function.bind(this.save, this),
+                        this.auto * 1000
+                    );
+                } else {
+                    this.layer.events.on({
+                        "featureadded": this.triggerSave,
+                        "afterfeaturemodified": this.triggerSave,
+                        scope: this
+                    });
+                }
+            }
+        }
+        return activated;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Deactivate the strategy.  Unregister any listeners, do appropriate
+     *     tear-down.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            if(this.auto) {
+                if(typeof this.auto === "number") {
+                    window.clearInterval(this.timer);
+                } else {
+                    this.layer.events.un({
+                        "featureadded": this.triggerSave,
+                        "afterfeaturemodified": this.triggerSave,
+                        scope: this
+                    });
+                }
+            }
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: triggerSave
+     * Registered as a listener.  Calls save if a feature has insert, update,
+     *     or delete state.
+     *
+     * Parameters:
+     * event - {Object} The event this function is listening for.
+     */
+    triggerSave: function(event) {
+        var feature = event.feature;
+        if(feature.state === OpenLayers.State.INSERT ||
+           feature.state === OpenLayers.State.UPDATE ||
+           feature.state === OpenLayers.State.DELETE) {
+            this.save([event.feature]);
+        }
+    },
+    
+    /**
+     * APIMethod: save
+     * Tell the layer protocol to commit unsaved features.  If the layer
+     *     projection differs from the map projection, features will be
+     *     transformed into the layer projection before being committed.
+     *
+     * Parameters:
+     * features - {Array} Features to be saved.  If null, then default is all
+     *     features in the layer.  Features are assumed to be in the map
+     *     projection.
+     */
+    save: function(features) {
+        if(!features) {
+            features = this.layer.features;
+        }
+        this.events.triggerEvent("start", {features:features});
+        var remote = this.layer.projection;
+        var local = this.layer.map.getProjectionObject();
+        if(!local.equals(remote)) {
+            var len = features.length;
+            var clones = new Array(len);
+            var orig, clone;
+            for(var i=0; i<len; ++i) {
+                orig = features[i];
+                clone = orig.clone();
+                clone.fid = orig.fid;
+                clone.state = orig.state;
+                if(orig.url) {
+                    clone.url = orig.url;
+                }
+                clone._original = orig;
+                clone.geometry.transform(local, remote);
+                clones[i] = clone;
+            }
+            features = clones;
+        }
+        this.layer.protocol.commit(features, {
+            callback: this.onCommit,
+            scope: this
+        });
+    },
+    
+    /**
+     * Method: onCommit
+     * Called after protocol commit.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} A response object.
+     */
+    onCommit: function(response) {
+        var evt = {"response": response};
+        if(response.success()) {
+            var features = response.reqFeatures;
+            // deal with inserts, updates, and deletes
+            var state, feature;
+            var destroys = [];
+            var insertIds = response.insertIds || [];
+            var j = 0;
+            for(var i=0, len=features.length; i<len; ++i) {
+                feature = features[i];
+                // if projection was different, we may be dealing with clones
+                feature = feature._original || feature;
+                state = feature.state;
+                if(state) {
+                    if(state == OpenLayers.State.DELETE) {
+                        destroys.push(feature);
+                    } else if(state == OpenLayers.State.INSERT) {
+                        feature.fid = insertIds[j];
+                        ++j;
+                    }
+                    feature.state = null;
+                }
+            }
+
+            if(destroys.length > 0) {
+                this.layer.destroyFeatures(destroys);
+            }
+
+            this.events.triggerEvent("success", evt);
+
+        } else {
+            this.events.triggerEvent("fail", evt);
+        }
+    },
+   
+    CLASS_NAME: "OpenLayers.Strategy.Save" 
+});
+/* ======================================================================
+    OpenLayers/Handler.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.Handler
+ * Base class to construct a higher-level handler for event sequences.  All
+ *     handlers have activate and deactivate methods.  In addition, they have
+ *     methods named like browser events.  When a handler is activated, any
+ *     additional methods named like a browser event is registered as a
+ *     listener for the corresponding event.  When a handler is deactivated,
+ *     those same methods are unregistered as event listeners.
+ *
+ * Handlers also typically have a callbacks object with keys named like
+ *     the abstracted events or event sequences that they are in charge of
+ *     handling.  The controls that wrap handlers define the methods that
+ *     correspond to these abstract events - so instead of listening for
+ *     individual browser events, they only listen for the abstract events
+ *     defined by the handler.
+ *     
+ * Handlers are created by controls, which ultimately have the responsibility
+ *     of making changes to the the state of the application.  Handlers
+ *     themselves may make temporary changes, but in general are expected to
+ *     return the application in the same state that they found it.
+ */
+OpenLayers.Handler = OpenLayers.Class({
+
+    /**
+     * Property: id
+     * {String}
+     */
+    id: null,
+        
+    /**
+     * APIProperty: control
+     * {<OpenLayers.Control>}. The control that initialized this handler.  The
+     *     control is assumed to have a valid map property - that map is used
+     *     in the handler's own setMap method.
+     */
+    control: null,
+
+    /**
+     * Property: map
+     * {<OpenLayers.Map>}
+     */
+    map: null,
+
+    /**
+     * APIProperty: keyMask
+     * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
+     *     constants to construct a keyMask.  The keyMask is used by
+     *     <checkModifiers>.  If the keyMask matches the combination of keys
+     *     down on an event, checkModifiers returns true.
+     *
+     * Example:
+     * (code)
+     *     // handler only responds if the Shift key is down
+     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
+     *
+     *     // handler only responds if Ctrl-Shift is down
+     *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
+     *                       OpenLayers.Handler.MOD_CTRL;
+     * (end)
+     */
+    keyMask: null,
+
+    /**
+     * Property: active
+     * {Boolean}
+     */
+    active: false,
+    
+    /**
+     * Property: evt
+     * {Event} This property references the last event handled by the handler.
+     *     Note that this property is not part of the stable API.  Use of the
+     *     evt property should be restricted to controls in the library
+     *     or other applications that are willing to update with changes to
+     *     the OpenLayers code.
+     */
+    evt: null,
+
+    /**
+     * Constructor: OpenLayers.Handler
+     * Construct a handler.
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that initialized this
+     *     handler.  The control is assumed to have a valid map property; that
+     *     map is used in the handler's own setMap method.  If a map property
+     *     is present in the options argument it will be used instead.
+     * callbacks - {Object} An object whose properties correspond to abstracted
+     *     events or sequences of browser events.  The values for these
+     *     properties are functions defined by the control that get called by
+     *     the handler.
+     * options - {Object} An optional object whose properties will be set on
+     *     the handler.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Util.extend(this, options);
+        this.control = control;
+        this.callbacks = callbacks;
+
+        var map = this.map || control.map;
+        if (map) {
+            this.setMap(map); 
+        }
+        
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    },
+    
+    /**
+     * Method: setMap
+     */
+    setMap: function (map) {
+        this.map = map;
+    },
+
+    /**
+     * Method: checkModifiers
+     * Check the keyMask on the handler.  If no <keyMask> is set, this always
+     *     returns true.  If a <keyMask> is set and it matches the combination
+     *     of keys down on an event, this returns true.
+     *
+     * Returns:
+     * {Boolean} The keyMask matches the keys down on an event.
+     */
+    checkModifiers: function (evt) {
+        if(this.keyMask == null) {
+            return true;
+        }
+        /* calculate the keyboard modifier mask for this event */
+        var keyModifiers =
+            (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
+            (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
+            (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0);
+    
+        /* if it differs from the handler object's key mask,
+           bail out of the event handler */
+        return (keyModifiers == this.keyMask);
+    },
+
+    /**
+     * APIMethod: activate
+     * Turn on the handler.  Returns false if the handler was already active.
+     * 
+     * Returns: 
+     * {Boolean} The handler was activated.
+     */
+    activate: function() {
+        if(this.active) {
+            return false;
+        }
+        // register for event handlers defined on this class.
+        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
+        for (var i=0, len=events.length; i<len; i++) {
+            if (this[events[i]]) {
+                this.register(events[i], this[events[i]]); 
+            }
+        } 
+        this.active = true;
+        return true;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Turn off the handler.  Returns false if the handler was already inactive.
+     * 
+     * Returns:
+     * {Boolean} The handler was deactivated.
+     */
+    deactivate: function() {
+        if(!this.active) {
+            return false;
+        }
+        // unregister event handlers defined on this class.
+        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
+        for (var i=0, len=events.length; i<len; i++) {
+            if (this[events[i]]) {
+                this.unregister(events[i], this[events[i]]); 
+            }
+        } 
+        this.active = false;
+        return true;
+    },
+
+    /**
+    * Method: callback
+    * Trigger the control's named callback with the given arguments
+    *
+    * Parameters:
+    * name - {String} The key for the callback that is one of the properties
+    *     of the handler's callbacks object.
+    * args - {Array(*)} An array of arguments (any type) with which to call 
+    *     the callback (defined by the control).
+    */
+    callback: function (name, args) {
+        if (name && this.callbacks[name]) {
+            this.callbacks[name].apply(this.control, args);
+        }
+    },
+
+    /**
+    * Method: register
+    * register an event on the map
+    */
+    register: function (name, method) {
+        // TODO: deal with registerPriority in 3.0
+        this.map.events.registerPriority(name, this, method);
+        this.map.events.registerPriority(name, this, this.setEvent);
+    },
+
+    /**
+    * Method: unregister
+    * unregister an event from the map
+    */
+    unregister: function (name, method) {
+        this.map.events.unregister(name, this, method);   
+        this.map.events.unregister(name, this, this.setEvent);
+    },
+    
+    /**
+     * Method: setEvent
+     * With each registered browser event, the handler sets its own evt
+     *     property.  This property can be accessed by controls if needed
+     *     to get more information about the event that the handler is
+     *     processing.
+     *
+     * This allows modifier keys on the event to be checked (alt, shift,
+     *     and ctrl cannot be checked with the keyboard handler).  For a
+     *     control to determine which modifier keys are associated with the
+     *     event that a handler is currently processing, it should access
+     *     (code)handler.evt.altKey || handler.evt.shiftKey ||
+     *     handler.evt.ctrlKey(end).
+     *
+     * Parameters:
+     * evt - {Event} The browser event.
+     */
+    setEvent: function(evt) {
+        this.evt = evt;
+        return true;
+    },
+
+    /**
+     * Method: destroy
+     * Deconstruct the handler.
+     */
+    destroy: function () {
+        // unregister event listeners
+        this.deactivate();
+        // eliminate circular references
+        this.control = this.map = null;        
+    },
+
+    CLASS_NAME: "OpenLayers.Handler"
+});
+
+/**
+ * Constant: OpenLayers.Handler.MOD_NONE
+ * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
+ */
+OpenLayers.Handler.MOD_NONE  = 0;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_SHIFT
+ * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
+ */
+OpenLayers.Handler.MOD_SHIFT = 1;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_CTRL
+ * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
+ */
+OpenLayers.Handler.MOD_CTRL  = 2;
+
+/**
+ * Constant: OpenLayers.Handler.MOD_ALT
+ * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
+ */
+OpenLayers.Handler.MOD_ALT   = 4;
+
+
+/* ======================================================================
+    OpenLayers/Handler/Point.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler.js
+ * @requires OpenLayers/Geometry/Point.js
+ */
+
+/**
+ * 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', 'cancel', and 'modify'.  The modify callback is
+ *     called with each change in the sketch and will receive the latest point
+ *     drawn.  Create a new instance with the <OpenLayers.Handler.Point>
+ *     constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
+    
+    /**
+     * Property: point
+     * {<OpenLayers.Feature.Vector>} The currently drawn point
+     */
+    point: null,
+
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>} The temporary drawing layer
+     */
+    layer: null,
+    
+    /**
+     * APIProperty: multi
+     * {Boolean} Cast features to multi-part geometries before passing to the
+     *     layer.  Default is false.
+     */
+    multi: false,
+    
+    /**
+     * Property: drawing 
+     * {Boolean} A point is being drawn
+     */
+    drawing: false,
+    
+    /**
+     * Property: mouseDown
+     * {Boolean} The mouse is down
+     */
+    mouseDown: false,
+
+    /**
+     * Property: lastDown
+     * {<OpenLayers.Pixel>} Location of the last mouse down
+     */
+    lastDown: null,
+
+    /**
+     * Property: lastUp
+     * {<OpenLayers.Pixel>}
+     */
+    lastUp: null,
+
+    /**
+     * APIProperty: persist
+     * {Boolean} Leave the feature rendered until destroyFeature is called.
+     *     Default is false.  If set to true, the feature remains rendered until
+     *     destroyFeature is called, typically by deactivating the handler or
+     *     starting another drawing.
+     */
+    persist: false,
+
+    /**
+     * Property: layerOptions
+     * {Object} Any optional properties to be set on the sketch layer.
+     */
+    layerOptions: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.Point
+     * Create a new point handler.
+     *
+     * Parameters:
+     * 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:
+     * create - Called when a sketch is first created.  Callback called with
+     *     the creation point geometry and sketch feature.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the point geometry.
+     * cancel - Called when the handler is deactivated while drawing.  The
+     *     cancel callback will receive a geometry.
+     */
+    initialize: function(control, callbacks, options) {
+        if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
+            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+        }
+
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * APIMethod: activate
+     * turn on the handler
+     */
+    activate: function() {
+        if(!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            return false;
+        }
+        // create temporary vector layer for rendering geometry sketch
+        // TBD: this could be moved to initialize/destroy - setting visibility here
+        var options = OpenLayers.Util.extend({
+            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: OpenLayers.Function.True
+        }, this.layerOptions);
+        this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
+        this.map.addLayer(this.layer);
+        return true;
+    },
+    
+    /**
+     * Method: createFeature
+     * Add temporary features
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
+     */
+    createFeature: function(pixel) {
+        var lonlat = this.map.getLonLatFromPixel(pixel);
+        this.point = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
+        );
+        this.callback("create", [this.point.geometry, this.point]);
+        this.point.geometry.clearBounds();
+        this.layer.addFeatures([this.point], {silent: true});
+    },
+
+    /**
+     * APIMethod: deactivate
+     * turn off the handler
+     */
+    deactivate: function() {
+        if(!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            return false;
+        }
+        // call the cancel callback if mid-drawing
+        if(this.drawing) {
+            this.cancel();
+        }
+        this.destroyFeature();
+        // If a layer's map property is set to null, it means that that layer
+        // isn't added to the map. Since we ourself added the layer to the map
+        // in activate(), we can assume that if this.layer.map is null it means
+        // that the layer has been destroyed (as a result of map.destroy() for
+        // example.
+        if (this.layer.map != null) {
+            this.layer.destroy(false);
+        }
+        this.layer = null;
+        return true;
+    },
+    
+    /**
+     * Method: destroyFeature
+     * Destroy the temporary geometries
+     */
+    destroyFeature: function() {
+        if(this.layer) {
+            this.layer.destroyFeatures();
+        }
+        this.point = null;
+    },
+
+    /**
+     * Method: finalize
+     * Finish the geometry and call the "done" callback.
+     *
+     * Parameters:
+     * cancel - {Boolean} Call cancel instead of done callback.  Default is
+     *     false.
+     */
+    finalize: function(cancel) {
+        var key = cancel ? "cancel" : "done";
+        this.drawing = false;
+        this.mouseDown = false;
+        this.lastDown = null;
+        this.lastUp = null;
+        this.callback(key, [this.geometryClone()]);
+        if(cancel || !this.persist) {
+            this.destroyFeature();
+        }
+    },
+
+    /**
+     * APIMethod: cancel
+     * Finish the geometry and call the "cancel" callback.
+     */
+    cancel: function() {
+        this.finalize(true);
+    },
+
+    /**
+     * Method: click
+     * Handle clicks.  Clicks are stopped from propagating to other listeners
+     *     on map.events or other dom elements.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    click: function(evt) {
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+
+    /**
+     * Method: dblclick
+     * Handle double-clicks.  Double-clicks are stopped from propagating to other
+     *     listeners on map.events or other dom elements.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    dblclick: function(evt) {
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+    
+    /**
+     * 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();
+        this.drawFeature();
+    },
+
+    /**
+     * Method: drawFeature
+     * Render features on the temporary layer.
+     */
+    drawFeature: function() {
+        this.layer.drawFeature(this.point, this.style);
+    },
+    
+    /**
+     * Method: getGeometry
+     * Return the sketch geometry.  If <multi> is true, this will return
+     *     a multi-part geometry.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Point>}
+     */
+    getGeometry: function() {
+        var geometry = this.point && this.point.geometry;
+        if(geometry && this.multi) {
+            geometry = new OpenLayers.Geometry.MultiPoint([geometry]);
+        }
+        return geometry;
+    },
+
+    /**
+     * Method: geometryClone
+     * Return a clone of the relevant geometry.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry>}
+     */
+    geometryClone: function() {
+        var geom = this.getGeometry();
+        return geom && geom.clone();
+    },
+  
+    /**
+     * Method: mousedown
+     * Handle mouse down.  Adjust the geometry and redraw.
+     * Return determines whether to propagate the event on the map.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousedown: function(evt) {
+        // check keyboard modifiers
+        if(!this.checkModifiers(evt)) {
+            return true;
+        }
+        // ignore double-clicks
+        if(this.lastDown && this.lastDown.equals(evt.xy)) {
+            return true;
+        }
+        this.drawing = true;
+        if(this.lastDown == null) {
+            if(this.persist) {
+                this.destroyFeature();
+            }
+            this.createFeature(evt.xy);
+        } else {
+            this.modifyFeature(evt.xy);
+        }
+        this.lastDown = evt.xy;
+        return false;
+    },
+
+    /**
+     * Method: mousemove
+     * Handle mouse move.  Adjust the geometry and redraw.
+     * Return determines whether to propagate the event on the map.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousemove: function (evt) {
+        if(this.drawing) {
+            this.modifyFeature(evt.xy);
+        }
+        return true;
+    },
+
+    /**
+     * Method: mouseup
+     * Handle mouse up.  Send the latest point in the geometry to the control.
+     * Return determines whether to propagate the event on the map.
+     *
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mouseup: function (evt) {
+        if(this.drawing) {
+            this.finalize();
+            return false;
+        } else {
+            return true;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Point"
+});
+/* ======================================================================
+    OpenLayers/Handler/Path.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler/Point.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/LineString.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Path
+ * Handler to draw a path on the map.  Path is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Handler.Point>
+ */
+OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
+    
+    /**
+     * Property: line
+     * {<OpenLayers.Feature.Vector>}
+     */
+    line: null,
+    
+    /**
+     * Property: freehand
+     * {Boolean} In freehand mode, the handler starts the path on mouse down,
+     * adds a point for every mouse move, and finishes the path on mouse up.
+     * Outside of freehand mode, a point is added to the path on every mouse
+     * click and double-click finishes the path.
+     */
+    freehand: false,
+    
+    /**
+     * Property: freehandToggle
+     * {String} If set, freehandToggle is checked on mouse events and will set
+     * the freehand mode to the opposite of this.freehand.  To disallow
+     * toggling between freehand and non-freehand mode, set freehandToggle to
+     * null.  Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.
+     */
+    freehandToggle: 'shiftKey',
+
+    /**
+     * Constructor: OpenLayers.Handler.Path
+     * Create a new path hander
+     *
+     * Parameters:
+     * 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:
+     * create - Called when a sketch is first created.  Callback called with
+     *     the creation point geometry and sketch feature.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * point - Called as each point is added.  Receives the new point geometry.
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the linestring geometry.
+     * 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);
+    },
+        
+    /**
+     * Method: createFeature
+     * Add temporary geometries
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
+     *     feature.
+     */
+    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.geometry])
+        );
+        this.callback("create", [this.point.geometry, this.getSketch()]);
+        this.point.geometry.clearBounds();
+        this.layer.addFeatures([this.line, this.point], {silent: true});
+    },
+        
+    /**
+     * Method: destroyFeature
+     * Destroy temporary geometries
+     */
+    destroyFeature: function() {
+        OpenLayers.Handler.Point.prototype.destroyFeature.apply(this);
+        this.line = null;
+    },
+
+    /**
+     * Method: removePoint
+     * Destroy the temporary point.
+     */
+    removePoint: function() {
+        if(this.point) {
+            this.layer.removeFeatures([this.point]);
+        }
+    },
+    
+    /**
+     * 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(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();
+    },
+    
+    /**
+     * Method: freehandMode
+     * Determine whether to behave in freehand mode or not.
+     *
+     * Returns:
+     * {Boolean}
+     */
+    freehandMode: function(evt) {
+        return (this.freehandToggle && evt[this.freehandToggle]) ?
+                    !this.freehand : this.freehand;
+    },
+
+    /**
+     * Method: modifyFeature
+     * Modify the existing geometry given the new point
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest
+     *     point.
+     */
+    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.
+     */
+    drawFeature: function() {
+        this.layer.drawFeature(this.line, this.style);
+        this.layer.drawFeature(this.point, this.style);
+    },
+
+    /**
+     * 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.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.LineString>}
+     */
+    getGeometry: function() {
+        var geometry = this.line && this.line.geometry;
+        if(geometry && this.multi) {
+            geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
+        }
+        return geometry;
+    },
+
+    /**
+     * Method: mousedown
+     * Handle mouse down.  Add a new point to the geometry and
+     * render it. Return determines whether to propagate the event on the map.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousedown: function(evt) {
+        // ignore double-clicks
+        if (this.lastDown && this.lastDown.equals(evt.xy)) {
+            return false;
+        }
+        if(this.lastDown == null) {
+            if(this.persist) {
+                this.destroyFeature();
+            }
+            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;
+        this.drawing = true;
+        return false;
+    },
+
+    /**
+     * Method: mousemove
+     * Handle mouse move.  Adjust the geometry and redraw.
+     * Return determines whether to propagate the event on the map.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousemove: function (evt) {
+        if(this.drawing) { 
+            if(this.mouseDown && this.freehandMode(evt)) {
+                this.addPoint(evt.xy);
+            } else {
+                this.modifyFeature(evt.xy);
+            }
+        }
+        return true;
+    },
+    
+    /**
+     * Method: mouseup
+     * Handle mouse up.  Send the latest point in the geometry to
+     * the control. Return determines whether to propagate the event on the map.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mouseup: function (evt) {
+        this.mouseDown = false;
+        if(this.drawing) {
+            if(this.freehandMode(evt)) {
+                this.removePoint();
+                this.finalize();
+            } else {
+                if(this.lastUp == null) {
+                   this.addPoint(evt.xy);
+                }
+                this.lastUp = evt.xy;
+            }
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * APIMethod: finishGeometry
+     * Finish the geometry and send it back to the control.
+     */
+    finishGeometry: function() {
+        var index = this.line.geometry.components.length - 1;
+        this.line.geometry.removeComponent(this.line.geometry.components[index]);
+        this.removePoint();
+        this.finalize();
+    },
+  
+    /**
+     * Method: dblclick 
+     * Handle double-clicks.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    dblclick: function(evt) {
+        if(!this.freehandMode(evt)) {
+            this.finishGeometry();
+        }
+        return false;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Path"
+});
+/* ======================================================================
+    OpenLayers/Handler/Polygon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler/Path.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Polygon
+ * Handler to draw a polygon on the map.  Polygon is displayed on mouse down,
+ * moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Handler.Path>
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
+    
+    /** 
+     * APIProperty: holeModifier
+     * {String} Key modifier to trigger hole digitizing.  Acceptable values are
+     *     "altKey", "shiftKey", or "ctrlKey".  If not set, no hole digitizing
+     *     will take place.  Default is null.
+     */
+    holeModifier: null,
+    
+    /**
+     * Property: drawingHole
+     * {Boolean} Currently drawing an interior ring.
+     */
+    drawingHole: false,
+    
+    /**
+     * Parameter: polygon
+     * {<OpenLayers.Feature.Vector>}
+     */
+    polygon: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.Polygon
+     * Create a Polygon Handler.
+     *
+     * Parameters:
+     * 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:
+     * create - Called when a sketch is first created.  Callback called with
+     *     the creation point geometry and sketch feature.
+     * modify - Called with each move of a vertex with the vertex (point)
+     *     geometry and the sketch feature.
+     * point - Called as each point is added.  Receives the new point geometry.
+     * done - Called when the point drawing is finished.  The callback will
+     *     recieve a single argument, the polygon geometry.
+     * 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);
+    },
+    
+    /**
+     * Method: createFeature
+     * Add temporary geometries
+     *
+     * Parameters:
+     * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new
+     *     feature.
+     */
+    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])
+        );
+        
+        // check for hole digitizing
+        var polygon;
+        if (this.holeModifier && (this.evt[this.holeModifier])) {
+            var geometry = this.point.geometry;
+            var features = this.control.layer.features;
+            var candidate;
+            // look for intersections, last drawn gets priority
+            for (var i=features.length-1; i>=0; --i) {
+                candidate = features[i].geometry;
+                if ((candidate instanceof OpenLayers.Geometry.Polygon || 
+                    candidate instanceof OpenLayers.Geometry.MultiPolygon) && 
+                    candidate.intersects(geometry)) {
+                    polygon = features[i];
+                    this.control.layer.removeFeatures([polygon], {silent: true});
+                    this.control.layer.events.registerPriority(
+                        "sketchcomplete", this, this.finalizeInteriorRing
+                    );
+                    this.control.layer.events.registerPriority(
+                        "sketchmodified", this, this.enforceTopology
+                    );
+                    polygon.geometry.addComponent(this.line.geometry);
+                    this.polygon = polygon;
+                    this.drawingHole = true;
+                    break;
+                }
+            }
+        }
+        if (!polygon) {
+            this.polygon = new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Polygon([this.line.geometry])
+            );
+        }
+        
+        this.callback("create", [this.point.geometry, this.getSketch()]);
+        this.point.geometry.clearBounds();
+        this.layer.addFeatures([this.polygon, this.point], {silent: true});
+    },
+    
+    /**
+     * Method: enforceTopology
+     * Simple topology enforcement for drawing interior rings.  Ensures vertices
+     *     of interior rings are contained by exterior ring.  Other topology 
+     *     rules are enforced in <finalizeInteriorRing> to allow drawing of 
+     *     rings that intersect only during the sketch (e.g. a "C" shaped ring
+     *     that nearly encloses another ring).
+     */
+    enforceTopology: function(event) {
+        var point = event.vertex;
+        var components = this.line.geometry.components;
+        // ensure that vertices of interior ring are contained by exterior ring
+        if (!this.polygon.geometry.intersects(point)) {
+            var last = components[components.length-3];
+            point.x = last.x;
+            point.y = last.y;
+        }
+    },
+    
+    /**
+     * Method: finalizeInteriorRing
+     * Enforces that new ring has some area and doesn't contain vertices of any
+     *     other rings.
+     */
+    finalizeInteriorRing: function() {
+        var ring = this.line.geometry;
+        // ensure that ring has some area
+        var modified = (ring.getArea() !== 0);
+        if (modified) {
+            // ensure that new ring doesn't intersect any other rings
+            var rings = this.polygon.geometry.components;
+            for (var i=rings.length-2; i>=0; --i) {
+                if (ring.intersects(rings[i])) {
+                    modified = false;
+                    break;
+                }
+            }
+            if (modified) {
+                // ensure that new ring doesn't contain any other rings
+                var target;
+                outer: for (var i=rings.length-2; i>0; --i) {
+                    var points = rings[i].components;
+                    for (var j=0, jj=points.length; j<jj; ++j) {
+                        if (ring.containsPoint(points[j])) {
+                            modified = false;
+                            break outer;
+                        }
+                    }
+                }
+            }
+        }
+        if (modified) {
+            if (this.polygon.state !== OpenLayers.State.INSERT) {
+                this.polygon.state = OpenLayers.State.UPDATE;
+            }
+        } else {
+            this.polygon.geometry.removeComponent(ring);
+        }
+        this.restoreFeature();
+        return false;
+    },
+
+    /**
+     * APIMethod: cancel
+     * Finish the geometry and call the "cancel" callback.
+     */
+    cancel: function() {
+        if (this.drawingHole) {
+            this.polygon.geometry.removeComponent(this.line.geometry);
+            this.restoreFeature(true);
+        }
+        return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
+    },
+    
+    /**
+     * Method: restoreFeature
+     * Move the feature from the sketch layer to the target layer.
+     *
+     * Properties: 
+     * cancel - {Boolean} Cancel drawing.  If falsey, the "sketchcomplete" event
+     *     will be fired.
+     */
+    restoreFeature: function(cancel) {
+        this.control.layer.events.unregister(
+            "sketchcomplete", this, this.finalizeInteriorRing
+        );
+        this.control.layer.events.unregister(
+            "sketchmodified", this, this.enforceTopology
+        );
+        this.layer.removeFeatures([this.polygon], {silent: true});
+        this.control.layer.addFeatures([this.polygon], {silent: true});
+        this.drawingHole = false;
+        if (!cancel) {
+            // Re-trigger "sketchcomplete" so other listeners can do their
+            // business.  While this is somewhat sloppy (if a listener is 
+            // registered with registerPriority - not common - between the start
+            // and end of a single ring drawing - very uncommon - it will be 
+            // called twice).
+            // TODO: In 3.0, collapse sketch handlers into geometry specific
+            // drawing controls.
+            this.control.layer.events.triggerEvent(
+                "sketchcomplete", {feature : this.polygon}
+            );
+        }
+    },
+
+    /**
+     * Method: destroyFeature
+     * Destroy temporary geometries
+     */
+    destroyFeature: function() {
+        OpenLayers.Handler.Path.prototype.destroyFeature.apply(this);
+        this.polygon = null;
+    },
+
+    /**
+     * Method: drawFeature
+     * Render geometries on the temporary layer.
+     */
+    drawFeature: function() {
+        this.layer.drawFeature(this.polygon, this.style);
+        this.layer.drawFeature(this.point, this.style);
+    },
+    
+    /**
+     * 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.
+     *
+     * Returns:
+     * {<OpenLayers.Geometry.Polygon>}
+     */
+    getGeometry: function() {
+        var geometry = this.polygon && this.polygon.geometry;
+        if(geometry && this.multi) {
+            geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
+        }
+        return geometry;
+    },
+
+    /**
+     * Method: dblclick
+     * Handle double-clicks.  Finish the geometry and send it back
+     * to the control.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    dblclick: function(evt) {
+        if(!this.freehandMode(evt)) {
+            // remove the penultimate point
+            var index = this.line.geometry.components.length - 2;
+            this.line.geometry.removeComponent(this.line.geometry.components[index]);
+            this.removePoint();
+            this.finalize();
+        }
+        return false;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Polygon"
+});
+/* ======================================================================
+    OpenLayers/Renderer.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer 
+ * This is the base class for all renderers.
+ *
+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
+ * It is largely composed of virtual functions that are to be implemented
+ * in technology-specific subclasses, but there is some generic code too.
+ * 
+ * The functions that *are* implemented here merely deal with the maintenance
+ *  of the size and extent variables, as well as the cached 'resolution' 
+ *  value. 
+ * 
+ * A note to the user that all subclasses should use getResolution() instead
+ *  of directly accessing this.resolution in order to correctly use the 
+ *  cacheing system.
+ *
+ */
+OpenLayers.Renderer = OpenLayers.Class({
+
+    /** 
+     * Property: container
+     * {DOMElement} 
+     */
+    container: null,
+    
+    /**
+     * Property: root
+     * {DOMElement}
+     */
+    root: null,
+
+    /** 
+     * Property: extent
+     * {<OpenLayers.Bounds>}
+     */
+    extent: null,
+
+    /**
+     * Property: locked
+     * {Boolean} If the renderer is currently in a state where many things
+     *     are changing, the 'locked' property is set to true. This means 
+     *     that renderers can expect at least one more drawFeature event to be
+     *     called with the 'locked' property set to 'true': In some renderers,
+     *     this might make sense to use as a 'only update local information'
+     *     flag. 
+     */  
+    locked: false,
+    
+    /** 
+     * Property: size
+     * {<OpenLayers.Size>} 
+     */
+    size: null,
+    
+    /**
+     * Property: resolution
+     * {Float} cache of current map resolution
+     */
+    resolution: null,
+    
+    /**
+     * Property: map  
+     * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
+     */
+    map: null,
+    
+    /**
+     * Constructor: OpenLayers.Renderer 
+     *
+     * Parameters:
+     * containerID - {<String>} 
+     * options - {Object} options for this renderer. See sublcasses for
+     *     supported options.
+     */
+    initialize: function(containerID, options) {
+        this.container = OpenLayers.Util.getElement(containerID);
+    },
+    
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        this.container = null;
+        this.extent = null;
+        this.size =  null;
+        this.resolution = null;
+        this.map = null;
+    },
+
+    /**
+     * APIMethod: supported
+     * This should be overridden by specific subclasses
+     * 
+     * Returns:
+     * {Boolean} Whether or not the browser supports the renderer class
+     */
+    supported: function() {
+        return false;
+    },    
+    
+    /**
+     * Method: setExtent
+     * Set the visible part of the layer.
+     *
+     * Resolution has probably changed, so we nullify the resolution 
+     * cache (this.resolution) -- this way it will be re-computed when 
+     * next it is needed.
+     * We nullify the resolution cache (this.resolution) if resolutionChanged
+     * is set to true - this way it will be re-computed on the next
+     * getResolution() request.
+     *
+     * Parameters:
+     * extent - {<OpenLayers.Bounds>}
+     * resolutionChanged - {Boolean}
+     */
+    setExtent: function(extent, resolutionChanged) {
+        this.extent = extent.clone();
+        if (resolutionChanged) {
+            this.resolution = null;
+        }
+    },
+    
+    /**
+     * Method: setSize
+     * Sets the size of the drawing surface.
+     * 
+     * Resolution has probably changed, so we nullify the resolution 
+     * cache (this.resolution) -- this way it will be re-computed when 
+     * next it is needed.
+     *
+     * Parameters:
+     * size - {<OpenLayers.Size>} 
+     */
+    setSize: function(size) {
+        this.size = size.clone();
+        this.resolution = null;
+    },
+    
+    /** 
+     * Method: getResolution
+     * Uses cached copy of resolution if available to minimize computing
+     * 
+     * Returns:
+     * The current map's resolution
+     */
+    getResolution: function() {
+        this.resolution = this.resolution || this.map.getResolution();
+        return this.resolution;
+    },
+    
+    /**
+     * Method: drawFeature
+     * Draw the feature.  The optional style argument can be used
+     * to override the feature's own style.  This method should only
+     * be called from layer.drawFeature().
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     * style - {<Object>}
+     * 
+     * Returns:
+     * {Boolean} true if the feature has been drawn completely, false if not,
+     *     undefined if the feature had no geometry
+     */
+    drawFeature: function(feature, style) {
+        if(style == null) {
+            style = feature.style;
+        }
+        if (feature.geometry) {
+            var bounds = feature.geometry.getBounds();
+            if(bounds) {
+                if (!bounds.intersectsBounds(this.extent)) {
+                    style = {display: "none"};
+                }
+                var rendered = this.drawGeometry(feature.geometry, style, feature.id);
+                if(style.display != "none" && style.label && rendered !== false) {
+                    var location = feature.geometry.getCentroid(); 
+                    if(style.labelXOffset || style.labelYOffset) {
+                        var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
+                        var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
+                        var res = this.getResolution();
+                        location.move(xOffset*res, yOffset*res);
+                    }
+                    this.drawText(feature.id, style, location);
+                } else {
+                    this.removeText(feature.id);
+                }
+                return rendered;
+            }
+        }
+    },
+
+
+    /** 
+     * Method: drawGeometry
+     * 
+     * Draw a geometry.  This should only be called from the renderer itself.
+     * Use layer.drawFeature() from outside the renderer.
+     * virtual function
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * style - {Object} 
+     * featureId - {<String>} 
+     */
+    drawGeometry: function(geometry, style, featureId) {},
+        
+    /**
+     * Method: drawText
+     * 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: removeText
+     * Function for removing text labels.
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String}
+     */
+    removeText: function(featureId) {},
+    
+    /**
+     * Method: clear
+     * Clear all vectors from the renderer.
+     * virtual function.
+     */    
+    clear: function() {},
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * Returns a feature id from an event on the renderer.  
+     * How this happens is specific to the renderer.  This should be
+     * called from layer.getFeatureFromEvent().
+     * Virtual function.
+     * 
+     * Parameters:
+     * evt - {<OpenLayers.Event>} 
+     *
+     * Returns:
+     * {String} A feature id or null.
+     */
+    getFeatureIdFromEvent: function(evt) {},
+    
+    /**
+     * Method: eraseFeatures 
+     * This is called by the layer to erase features
+     * 
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    eraseFeatures: function(features) {
+        if(!(features instanceof Array)) {
+            features = [features];
+        }
+        for(var i=0, len=features.length; i<len; ++i) {
+            var feature = features[i];
+            this.eraseGeometry(feature.geometry, feature.id);
+            this.removeText(feature.id);
+        }
+    },
+    
+    /**
+     * Method: eraseGeometry
+     * Remove a geometry from the renderer (by id).
+     * virtual function.
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * featureId - {String}
+     */
+    eraseGeometry: function(geometry, featureId) {},
+    
+    /**
+     * 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;
+    },
+    
+    /**
+     * Method: applyDefaultSymbolizer
+     * 
+     * Parameters:
+     * symbolizer - {Object}
+     * 
+     * Returns:
+     * {Object}
+     */
+    applyDefaultSymbolizer: function(symbolizer) {
+        var result = OpenLayers.Util.extend({},
+            OpenLayers.Renderer.defaultSymbolizer);
+        if(symbolizer.stroke === false) {
+            delete result.strokeWidth;
+            delete result.strokeColor;
+        }
+        if(symbolizer.fill === false) {
+            delete result.fillColor;
+        }
+        OpenLayers.Util.extend(result, symbolizer);
+        return result;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer"
+});
+
+/**
+ * Constant: OpenLayers.Renderer.defaultSymbolizer
+ * {Object} Properties from this symbolizer will be applied to symbolizers
+ *     with missing properties. This can also be used to set a global
+ *     symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
+ *     following code before rendering any vector features:
+ * (code)
+ * OpenLayers.Renderer.defaultSymbolizer = {
+ *     fillColor: "#808080",
+ *     fillOpacity: 1,
+ *     strokeColor: "#000000",
+ *     strokeOpacity: 1,
+ *     strokeWidth: 1,
+ *     pointRadius: 3,
+ *     graphicName: "square"
+ * };
+ * (end)
+ */
+OpenLayers.Renderer.defaultSymbolizer = {
+    fillColor: "#000000",
+    strokeColor: "#000000",
+    strokeWidth: 2,
+    fillOpacity: 1,
+    strokeOpacity: 1,
+    pointRadius: 0
+};
+    
+/* ======================================================================
+    OpenLayers/Renderer/Canvas.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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, 
+   
+    /**
+     * 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 = {};
+    },
+    
+    /** 
+     * 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>}
+     * featureId - {String}
+     */
+    eraseGeometry: function(geometry, featureId) {
+        this.eraseFeatures(this.features[featureId][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: 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) {
+        style = style || feature.style;
+        style = this.applyDefaultSymbolizer(style);  
+        
+        this.features[feature.id] = [feature, style]; 
+        this.redraw();
+    },
+
+
+    /** 
+     * Method: drawGeometry
+     * Used when looping (in redraw) over the features; draws
+     * the canvas. 
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>} 
+     * style - {Object} 
+     */
+    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;
+        }
+        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;
+        }
+    },
+
+    /**
+     * Method: drawExternalGraphic
+     * Called to draw External graphics. 
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawExternalGraphic: function(pt, style) {
+       var img = new Image();
+       
+       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 context = { img: img, 
+                       x: (pt[0]+xOffset), 
+                       y: (pt[1]+yOffset), 
+                       width: width, 
+                       height: height, 
+                       opacity: style.graphicOpacity || style.fillOpacity,
+                       canvas: this.canvas };
+
+       img.onload = OpenLayers.Function.bind( function() {
+           this.canvas.globalAlpha = this.opacity;
+           this.canvas.drawImage(this.img, this.x, 
+                                 this.y, this.width, this.height);
+       }, context);
+       img.src = style.externalGraphic;
+    },
+
+    /**
+     * 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) {
+        if(style.graphic !== false) {
+            var pt = this.getLocalXY(geometry);
+            
+            if (style.externalGraphic) {
+                this.drawExternalGraphic(pt, style);
+            } else {
+                if(style.fill !== false) {
+                    this.setCanvasStyle("fill", style);
+                    this.canvas.beginPath();
+                    this.canvas.arc(pt[0], pt[1], style.pointRadius, 0, Math.PI*2, true);
+                    this.canvas.fill();
+                }
+                
+                if(style.stroke !== false) {
+                    this.setCanvasStyle("stroke", style);
+                    this.canvas.beginPath();
+                    this.canvas.arc(pt[0], pt[1], style.pointRadius, 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) {
+        if(style.stroke !== false) {
+            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: drawLinearRing
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * geometry - {<OpenLayers.Geometry>}
+     * style    - {Object}
+     */ 
+    drawLinearRing: function(geometry, style) {
+        if(style.fill !== false) {
+            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();
+        }
+        
+        if(style.stroke !== false) {
+            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:
+     * location - {<OpenLayers.Point>}
+     * style    - {Object}
+     */
+    drawText: function(location, style) {
+        style = OpenLayers.Util.extend({
+            fontColor: "#000000",
+            labelAlign: "cm"
+        }, style);
+        var pt = this.getLocalXY(location);
+        
+        this.setCanvasStyle("reset");
+        this.canvas.fillStyle = style.fontColor;
+        this.canvas.globalAlpha = style.fontOpacity || 1.0;
+        var fontStyle = style.fontWeight + " " + style.fontSize + " " + style.fontFamily;
+        if (this.canvas.fillText) {
+            // HTML5
+            var labelAlign =
+                OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
+                "center";
+            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(style.labelAlign[0]) {
+                case "l":
+                    break;
+                case "r":
+                    pt[0] -= len;
+                    break;
+                case "c":
+                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.
+     */    
+    clear: function() {
+        this.canvas.clearRect(0, 0, this.root.width, this.root.height);
+        this.features = {};
+    },
+
+    /**
+     * Method: getFeatureIdFromEvent
+     * Returns a feature id from an event on the renderer.  
+     * 
+     * Parameters:
+     * evt - {<OpenLayers.Event>} 
+     *
+     * Returns:
+     * {String} A feature id or null.
+     */
+    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;
+    },
+    
+    /**
+     * 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.canvas.clearRect(0, 0, this.root.width, this.root.height);
+            var labelMap = [];
+            var feature, style;
+            for (var id in this.features) {
+                if (!this.features.hasOwnProperty(id)) { continue; }
+                feature = this.features[id][0];
+                style = this.features[id][1];
+                if (!feature.geometry) { continue; }
+                this.drawGeometry(feature.geometry, style);
+                if(style.label) {
+                    labelMap.push([feature, style]);
+                }
+            }
+            var item;
+            for (var i=0, len=labelMap.length; i<len; ++i) {
+                item = labelMap[i];
+                this.drawText(item[0].geometry.getCentroid(), item[1]);
+            }
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.Canvas"
+});
+
+/**
+ * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
+ * {Object}
+ */
+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
+    "l": "left",
+    "r": "right"
+};
+/* ======================================================================
+    OpenLayers/Handler/MouseWheel.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.MouseWheel
+ * Handler for wheel up/down events.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
+    /** 
+     * Property: wheelListener 
+     * {function} 
+     */
+    wheelListener: null,
+
+    /** 
+     * Property: mousePosition
+     * {<OpenLayers.Pixel>} mousePosition is necessary because
+     * evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
+     * value from the last mousemove.
+     */
+    mousePosition: null,
+
+    /**
+     * Property: interval
+     * {Integer} In order to increase server performance, an interval (in 
+     *     milliseconds) can be set to reduce the number of up/down events 
+     *     called. If set, a new up/down event will not be set until the 
+     *     interval has passed. 
+     *     Defaults to 0, meaning no interval. 
+     */
+    interval: 0,
+    
+    /**
+     * Property: delta
+     * {Integer} When interval is set, delta collects the mousewheel z-deltas
+     *     of the events that occur within the interval.
+     *      See also the cumulative option
+     */
+    delta: 0,
+    
+    /**
+     * Property: cumulative
+     * {Boolean} When interval is set: true to collect all the mousewheel 
+     *     z-deltas, false to only record the delta direction (positive or
+     *     negative)
+     */
+    cumulative: true,
+
+    /**
+     * Constructor: OpenLayers.Handler.MouseWheel
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} 
+     * callbacks - {Object} An object containing a single function to be
+     *                          called when the drag operation is finished.
+     *                          The callback should expect to recieve a single
+     *                          argument, the point geometry.
+     * options - {Object} 
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        this.wheelListener = OpenLayers.Function.bindAsEventListener(
+            this.onWheelEvent, this
+        );
+    },
+
+    /**
+     * Method: destroy
+     */    
+    destroy: function() {
+        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
+        this.wheelListener = null;
+    },
+
+    /**
+     *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+     */
+
+    /** 
+     * Method: onWheelEvent
+     * Catch the wheel event and handle it xbrowserly
+     * 
+     * Parameters:
+     * e - {Event} 
+     */
+    onWheelEvent: function(e){
+        
+        // make sure we have a map and check keyboard modifiers
+        if (!this.map || !this.checkModifiers(e)) {
+            return;
+        }
+        
+        // Ride up the element's DOM hierarchy to determine if it or any of 
+        //  its ancestors was: 
+        //   * specifically marked as scrollable
+        //   * one of our layer divs
+        //   * the map div
+        //
+        var overScrollableDiv = false;
+        var overLayerDiv = false;
+        var overMapDiv = false;
+        
+        var elem = OpenLayers.Event.element(e);
+        while((elem != null) && !overMapDiv && !overScrollableDiv) {
+
+            if (!overScrollableDiv) {
+                try {
+                    if (elem.currentStyle) {
+                        overflow = elem.currentStyle["overflow"];
+                    } else {
+                        var style = 
+                            document.defaultView.getComputedStyle(elem, null);
+                        var overflow = style.getPropertyValue("overflow");
+                    }
+                    overScrollableDiv = ( overflow && 
+                        (overflow == "auto") || (overflow == "scroll") );
+                } catch(err) {
+                    //sometimes when scrolling in a popup, this causes 
+                    // obscure browser error
+                }
+            }
+
+            if (!overLayerDiv) {
+                for(var i=0, len=this.map.layers.length; i<len; i++) {
+                    // Are we in the layer div? Note that we have two cases
+                    // here: one is to catch EventPane layers, which have a 
+                    // pane above the layer (layer.pane)
+                    if (elem == this.map.layers[i].div 
+                        || elem == this.map.layers[i].pane) { 
+                        overLayerDiv = true;
+                        break;
+                    }
+                }
+            }
+            overMapDiv = (elem == this.map.div);
+
+            elem = elem.parentNode;
+        }
+        
+        // Logic below is the following:
+        //
+        // If we are over a scrollable div or not over the map div:
+        //  * do nothing (let the browser handle scrolling)
+        //
+        //    otherwise 
+        // 
+        //    If we are over the layer div: 
+        //     * zoom/in out
+        //     then
+        //     * kill event (so as not to also scroll the page after zooming)
+        //
+        //       otherwise
+        //
+        //       Kill the event (dont scroll the page if we wheel over the 
+        //        layerswitcher or the pan/zoom control)
+        //
+        if (!overScrollableDiv && overMapDiv) {
+            if (overLayerDiv) {
+                var delta = 0;
+                if (!e) {
+                    e = window.event;
+                }
+                if (e.wheelDelta) {
+                    delta = e.wheelDelta/120; 
+                    if (window.opera && window.opera.version() < 9.2) {
+                        delta = -delta;
+                    }
+                } else if (e.detail) {
+                    delta = -e.detail / 3;
+                }
+                this.delta = this.delta + delta;
+
+                if(this.interval) {
+                    window.clearTimeout(this._timeoutId);
+                    this._timeoutId = window.setTimeout(
+                        OpenLayers.Function.bind(function(){
+                            this.wheelZoom(e);
+                        }, this),
+                        this.interval
+                    );
+                } else {
+                    this.wheelZoom(e);
+                }
+            }
+            OpenLayers.Event.stop(e);
+        }
+    },
+
+    /**
+     * Method: wheelZoom
+     * Given the wheel event, we carry out the appropriate zooming in or out,
+     *     based on the 'wheelDelta' or 'detail' property of the event.
+     * 
+     * Parameters:
+     * e - {Event}
+     */
+    wheelZoom: function(e) {
+        var delta = this.delta;
+        this.delta = 0;
+        
+        if (delta) {
+            // add the mouse position to the event because mozilla has 
+            // a bug with clientX and clientY (see 
+            // https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
+            // getLonLatFromViewPortPx(e) returns wrong values
+            if (this.mousePosition) {
+                e.xy = this.mousePosition;
+            } 
+            if (!e.xy) {
+                // If the mouse hasn't moved over the map yet, then
+                // we don't have a mouse position (in FF), so we just
+                // act as if the mouse was at the center of the map.
+                // Note that we can tell we are in the map -- and 
+                // this.map is ensured to be true above.
+                e.xy = this.map.getPixelFromLonLat(
+                    this.map.getCenter()
+                );
+            }
+            if (delta < 0) {
+                this.callback("down", [e, this.cumulative ? delta : -1]);
+            } else {
+                this.callback("up", [e, this.cumulative ? delta : 1]);
+            }
+        }
+    },
+    
+    /**
+     * Method: mousemove
+     * Update the stored mousePosition on every move.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousemove: function (evt) {
+        this.mousePosition = evt.xy;
+    },
+
+    /**
+     * Method: activate 
+     */
+    activate: function (evt) {
+        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            //register mousewheel events specifically on the window and document
+            var wheelListener = this.wheelListener;
+            OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
+            OpenLayers.Event.observe(window, "mousewheel", wheelListener);
+            OpenLayers.Event.observe(document, "mousewheel", wheelListener);
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Method: deactivate 
+     */
+    deactivate: function (evt) {
+        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            // unregister mousewheel events specifically on the window and document
+            var wheelListener = this.wheelListener;
+            OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
+            OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
+            OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.MouseWheel"
+});
+/* ======================================================================
+    OpenLayers/Symbolizer.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer
+ * Base class representing a symbolizer used for feature rendering.
+ */
+OpenLayers.Symbolizer = OpenLayers.Class({
+    
+
+    /**
+     * APIProperty: zIndex
+     * {Number} The zIndex determines the rendering order for a symbolizer.
+     *     Symbolizers with larger zIndex values are rendered over symbolizers
+     *     with smaller zIndex values.  Default is 0.
+     */
+    zIndex: 0,
+    
+    /**
+     * Constructor: OpenLayers.Symbolizer
+     * Instances of this class are not useful.  See one of the subclasses.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Util.extend(this, config);
+    },
+    
+    /** 
+     * APIMethod: clone
+     * Create a copy of this symbolizer.
+     *
+     * Returns a symbolizer of the same type with the same properties.
+     */
+    clone: function() {
+        var Type = eval(this.CLASS_NAME);
+        return new Type(OpenLayers.Util.extend({}, this));
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer"
+    
+});
+
+/* ======================================================================
+    OpenLayers/Symbolizer/Raster.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Symbolizer.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer.Raster
+ * A symbolizer used to render raster images.
+ */
+OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {
+    
+    /**
+     * Constructor: OpenLayers.Symbolizer.Raster
+     * Create a symbolizer for rendering rasters.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new raster symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer.Raster"
+    
+});
+/* ======================================================================
+    OpenLayers/Tile.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/*
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
+ */
+
+/*
+ * Class: OpenLayers.Tile 
+ * This is a class designed to designate a single tile, however
+ *     it is explicitly designed to do relatively little. Tiles store 
+ *     information about themselves -- such as the URL that they are related
+ *     to, and their size - but do not add themselves to the layer div 
+ *     automatically, for example. Create a new tile with the 
+ *     <OpenLayers.Tile> constructor, or a subclass. 
+ * 
+ * TBD 3.0 - remove reference to url in above paragraph
+ * 
+ */
+OpenLayers.Tile = OpenLayers.Class({
+    
+    /** 
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types
+     */
+    EVENT_TYPES: [ "loadstart", "loadend", "reload", "unload"],
+    
+    /**
+     * APIProperty: events
+     * {<OpenLayers.Events>} An events object that handles all 
+     *                       events on the tile.
+     */
+    events: null,
+
+    /**
+     * Property: id 
+     * {String} null
+     */
+    id: null,
+    
+    /** 
+     * Property: layer 
+     * {<OpenLayers.Layer>} layer the tile is attached to 
+     */
+    layer: null,
+    
+    /**
+     * Property: url
+     * {String} url of the request.
+     *
+     * TBD 3.0 
+     * Deprecated. The base tile class does not need an url. This should be 
+     * handled in subclasses. Does not belong here.
+     */
+    url: null,
+
+    /** 
+     * APIProperty: bounds 
+     * {<OpenLayers.Bounds>} null
+     */
+    bounds: null,
+    
+    /** 
+     * Property: size 
+     * {<OpenLayers.Size>} null
+     */
+    size: null,
+    
+    /** 
+     * Property: position 
+     * {<OpenLayers.Pixel>} Top Left pixel of the tile
+     */    
+    position: null,
+
+    /**
+     * Property: isLoading
+     * {Boolean} Is the tile loading?
+     */
+    isLoading: false,
+        
+    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
+     *             there is no need for the base tile class to have a url.
+     * 
+     * Constructor: OpenLayers.Tile
+     * Constructor for a new <OpenLayers.Tile> instance.
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+     * position - {<OpenLayers.Pixel>}
+     * bounds - {<OpenLayers.Bounds>}
+     * url - {<String>}
+     * size - {<OpenLayers.Size>}
+     * options - {Object}
+     */   
+    initialize: function(layer, position, bounds, url, size, options) {
+        this.layer = layer;
+        this.position = position.clone();
+        this.bounds = bounds.clone();
+        this.url = url;
+        this.size = size.clone();
+
+        //give the tile a unique id based on its BBOX.
+        this.id = OpenLayers.Util.createUniqueID("Tile_");
+        
+        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
+
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /**
+     * Method: unload
+     * Call immediately before destroying if you are listening to tile
+     * events, so that counters are properly handled if tile is still
+     * loading at destroy-time. Will only fire an event if the tile is
+     * still loading.
+     */
+    unload: function() {
+       if (this.isLoading) { 
+           this.isLoading = false; 
+           this.events.triggerEvent("unload"); 
+       }
+    },
+    
+    /** 
+     * APIMethod: destroy
+     * Nullify references to prevent circular references and memory leaks.
+     */
+    destroy:function() {
+        this.layer  = null;
+        this.bounds = null;
+        this.size = null;
+        this.position = null;
+        
+        this.events.destroy();
+        this.events = null;
+    },
+    
+    /**
+     * Method: clone
+     *
+     * Parameters:
+     * obj - {<OpenLayers.Tile>} The tile to be cloned
+     *
+     * Returns:
+     * {<OpenLayers.Tile>} An exact clone of this <OpenLayers.Tile>
+     */
+    clone: function (obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Tile(this.layer, 
+                                      this.position, 
+                                      this.bounds, 
+                                      this.url, 
+                                      this.size);
+        } 
+        
+        // catch any randomly tagged-on properties
+        OpenLayers.Util.applyDefaults(obj, this);
+        
+        return obj;
+    },
+
+    /**
+     * Method: draw
+     * Clear whatever is currently in the tile, then return whether or not 
+     *     it should actually be re-drawn.
+     * 
+     * Returns:
+     * {Boolean} Whether or not the tile should actually be drawn. Note that 
+     *     this is not really the best way of doing things, but such is 
+     *     the way the code has been developed. Subclasses call this and
+     *     depend on the return to know if they should draw or not.
+     */
+    draw: function() {
+        var maxExtent = this.layer.maxExtent;
+        var withinMaxExtent = (maxExtent &&
+                               this.bounds.intersectsBounds(maxExtent, false));
+ 
+        // The only case where we *wouldn't* want to draw the tile is if the 
+        // tile is outside its layer's maxExtent.
+        this.shouldDraw = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
+                
+        //clear tile's contents and mark as not drawn
+        this.clear();
+        
+        return this.shouldDraw;
+    },
+    
+    /** 
+     * Method: moveTo
+     * Reposition the tile.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * redraw - {Boolean} Call draw method on tile after moving.
+     *     Default is true
+     */
+    moveTo: function (bounds, position, redraw) {
+        if (redraw == null) {
+            redraw = true;
+        }
+
+        this.bounds = bounds.clone();
+        this.position = position.clone();
+        if (redraw) {
+            this.draw();
+        }
+    },
+
+    /** 
+     * Method: clear
+     * Clear the tile of any bounds/position-related data so that it can 
+     *     be reused in a new location. To be implemented by subclasses.
+     */
+    clear: function() {
+        // to be implemented by subclasses
+    },
+    
+    /**   
+     * Method: getBoundsFromBaseLayer
+     * Take the pixel locations of the corner of the tile, and pass them to 
+     *     the base layer and ask for the location of those pixels, so that 
+     *     displaying tiles over Google works fine.
+     *
+     * Parameters:
+     * position - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * bounds - {<OpenLayers.Bounds>} 
+     */
+    getBoundsFromBaseLayer: function(position) {
+        var msg = OpenLayers.i18n('reprojectDeprecated',
+                                              {'layerName':this.layer.name});
+        OpenLayers.Console.warn(msg);
+        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
+        var bottomRightPx = position.clone();
+        bottomRightPx.x += this.size.w;
+        bottomRightPx.y += this.size.h;
+        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
+        // Handle the case where the base layer wraps around the date line.
+        // Google does this, and it breaks WMS servers to request bounds in 
+        // that fashion.  
+        if (topLeft.lon > bottomRight.lon) {
+            if (topLeft.lon < 0) {
+                topLeft.lon = -180 - (topLeft.lon+180);
+            } else {
+                bottomRight.lon = 180+bottomRight.lon+180;
+            }        
+        }
+        var bounds = new OpenLayers.Bounds(topLeft.lon, 
+                                       bottomRight.lat, 
+                                       bottomRight.lon, 
+                                       topLeft.lat);  
+        return bounds;
+    },        
+        
+    /** 
+     * Method: showTile
+     * Show the tile only if it should be drawn.
+     */
+    showTile: function() { 
+        if (this.shouldDraw) {
+            this.show();
+        }
+    },
+    
+    /** 
+     * Method: show
+     * Show the tile.  To be implemented by subclasses.
+     */
+    show: function() { },
+    
+    /** 
+     * Method: hide
+     * Hide the tile.  To be implemented by subclasses.
+     */
+    hide: function() { },
+    
+    CLASS_NAME: "OpenLayers.Tile"
+});
+/* ======================================================================
+    OpenLayers/Tile/Image.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Tile.js
+ */
+
+/**
+ * Class: OpenLayers.Tile.Image
+ * Instances of OpenLayers.Tile.Image are used to manage the image tiles
+ * used by various layers.  Create a new image tile with the
+ * <OpenLayers.Tile.Image> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Tile>
+ */
+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
+
+    /** 
+     * Property: url
+     * {String} The URL of the image being requested. No default. Filled in by
+     * layer.getURL() function. 
+     */
+    url: null,
+    
+    /** 
+     * Property: imgDiv
+     * {DOMElement} The div element which wraps the image.
+     */
+    imgDiv: null,
+
+    /**
+     * Property: frame
+     * {DOMElement} The image element is appended to the frame.  Any gutter on
+     * the image will be hidden behind the frame. 
+     */ 
+    frame: null, 
+    
+    /**
+     * Property: layerAlphaHack
+     * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
+     */
+    layerAlphaHack: null,
+    
+    /**
+     * Property: isBackBuffer
+     * {Boolean} Is this tile a back buffer tile?
+     */
+    isBackBuffer: false,
+    
+    /**
+     * Property: lastRatio
+     * {Float} Used in transition code only.  This is the previous ratio
+     *     of the back buffer tile resolution to the map resolution.  Compared
+     *     with the current ratio to determine if zooming occurred.
+     */
+    lastRatio: 1,
+
+    /**
+     * Property: isFirstDraw
+     * {Boolean} Is this the first time the tile is being drawn?
+     *     This is used to force resetBackBuffer to synchronize
+     *     the backBufferTile with the foreground tile the first time
+     *     the foreground tile loads so that if the user zooms
+     *     before the layer has fully loaded, the backBufferTile for
+     *     tiles that have been loaded can be used.
+     */
+    isFirstDraw: true,
+        
+    /**
+     * Property: backBufferTile
+     * {<OpenLayers.Tile>} A clone of the tile used to create transition
+     *     effects when the tile is moved or changes resolution.
+     */
+    backBufferTile: null,
+    
+    /**
+     * APIProperty: maxGetUrlLength
+     * {Number} If set, requests that would result in GET urls with more
+     * characters than the number provided will be made using form-encoded
+     * HTTP POST. It is good practice to avoid urls that are longer than 2048
+     * characters.
+     *
+     * Caution:
+     * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and
+     * Opera < 10.0 do not fully support this option.
+     *
+     * Note:
+     * Do not use this option for layers that have a transitionEffect
+     * configured - IFrame tiles from POST requests can not be resized.
+     */
+    maxGetUrlLength: null,
+    
+    /** TBD 3.0 - reorder the parameters to the init function to remove 
+     *             URL. the getUrl() function on the layer gets called on 
+     *             each draw(), so no need to specify it here.
+     * 
+     * Constructor: OpenLayers.Tile.Image
+     * Constructor for a new <OpenLayers.Tile.Image> instance.
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
+     * position - {<OpenLayers.Pixel>}
+     * bounds - {<OpenLayers.Bounds>}
+     * url - {<String>} Deprecated. Remove me in 3.0.
+     * size - {<OpenLayers.Size>}
+     * options - {Object}
+     */   
+    initialize: function(layer, position, bounds, url, size, options) {
+        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
+
+        if (this.maxGetUrlLength != null) {
+            OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
+        }
+
+        this.url = url; //deprecated remove me
+        
+        this.frame = document.createElement('div'); 
+        this.frame.style.overflow = 'hidden'; 
+        this.frame.style.position = 'absolute'; 
+
+        this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();        
+    },
+
+    /** 
+     * APIMethod: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+        if (this.imgDiv != null)  {
+            this.removeImgDiv();
+        }
+        this.imgDiv = null;
+        if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { 
+            this.layer.div.removeChild(this.frame); 
+        }
+        this.frame = null; 
+        
+        /* clean up the backBufferTile if it exists */
+        if (this.backBufferTile) {
+            this.backBufferTile.destroy();
+            this.backBufferTile = null;
+        }
+        
+        this.layer.events.unregister("loadend", this, this.resetBackBuffer);
+        
+        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * Method: clone
+     *
+     * Parameters:
+     * obj - {<OpenLayers.Tile.Image>} The tile to be cloned
+     *
+     * Returns:
+     * {<OpenLayers.Tile.Image>} An exact clone of this <OpenLayers.Tile.Image>
+     */
+    clone: function (obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Tile.Image(this.layer, 
+                                            this.position, 
+                                            this.bounds, 
+                                            this.url, 
+                                            this.size);        
+        } 
+        
+        //pick up properties from superclass
+        obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]);
+        
+        //dont want to directly copy the image div
+        obj.imgDiv = null;
+            
+        
+        return obj;
+    },
+    
+    /**
+     * Method: draw
+     * Check that a tile should be drawn, and draw it.
+     * 
+     * Returns:
+     * {Boolean} Always returns true.
+     */
+    draw: function() {
+        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
+            this.bounds = this.getBoundsFromBaseLayer(this.position);
+        }
+        var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
+        
+        if ((OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) || 
+            this.layer.singleTile) {
+            if (drawTile) {
+                //we use a clone of this tile to create a double buffer for visual
+                //continuity.  The backBufferTile is used to create transition
+                //effects while the tile in the grid is repositioned and redrawn
+                if (!this.backBufferTile) {
+                    this.backBufferTile = this.clone();
+                    this.backBufferTile.hide();
+                    // this is important.  It allows the backBuffer to place itself
+                    // appropriately in the DOM.  The Image subclass needs to put
+                    // the backBufferTile behind the main tile so the tiles can
+                    // load over top and display as soon as they are loaded.
+                    this.backBufferTile.isBackBuffer = true;
+                    
+                    // potentially end any transition effects when the tile loads
+                    this.events.register('loadend', this, this.resetBackBuffer);
+                    
+                    // clear transition back buffer tile only after all tiles in
+                    // this layer have loaded to avoid visual glitches
+                    this.layer.events.register("loadend", this, this.resetBackBuffer);
+                }
+                // run any transition effects
+                this.startTransition();
+            } else {
+                // if we aren't going to draw the tile, then the backBuffer should
+                // be hidden too!
+                if (this.backBufferTile) {
+                    this.backBufferTile.clear();
+                }
+            }
+        } else {
+            if (drawTile && this.isFirstDraw) {
+                this.events.register('loadend', this, this.showTile);
+                this.isFirstDraw = false;
+            }   
+        }    
+        
+        if (!drawTile) {
+            return false;
+        }
+        
+        if (this.isLoading) {
+            //if we're already loading, send 'reload' instead of 'loadstart'.
+            this.events.triggerEvent("reload"); 
+        } else {
+            this.isLoading = true;
+            this.events.triggerEvent("loadstart");
+        }
+        
+        return this.renderTile();
+    },
+    
+    /** 
+     * Method: resetBackBuffer
+     * Triggered by two different events, layer loadend, and tile loadend.
+     *     In any of these cases, we check to see if we can hide the 
+     *     backBufferTile yet and update its parameters to match the 
+     *     foreground tile.
+     *
+     * Basic logic:
+     *  - If the backBufferTile hasn't been drawn yet, reset it
+     *  - If layer is still loading, show foreground tile but don't hide
+     *    the backBufferTile yet
+     *  - If layer is done loading, reset backBuffer tile and show 
+     *    foreground tile
+     */
+    resetBackBuffer: function() {
+        this.showTile();
+        if (this.backBufferTile && 
+            (this.isFirstDraw || !this.layer.numLoadingTiles)) {
+            this.isFirstDraw = false;
+            // check to see if the backBufferTile is within the max extents
+            // before rendering it 
+            var maxExtent = this.layer.maxExtent;
+            var withinMaxExtent = (maxExtent &&
+                                   this.bounds.intersectsBounds(maxExtent, false));
+            if (withinMaxExtent) {
+                this.backBufferTile.position = this.position;
+                this.backBufferTile.bounds = this.bounds;
+                this.backBufferTile.size = this.size;
+                this.backBufferTile.imageSize = this.layer.getImageSize(this.bounds) || this.size;
+                this.backBufferTile.imageOffset = this.layer.imageOffset;
+                this.backBufferTile.resolution = this.layer.getResolution();
+                this.backBufferTile.renderTile();
+            }
+
+            this.backBufferTile.hide();
+        }
+    },
+    
+    /**
+     * Method: renderTile
+     * Internal function to actually initialize the image tile,
+     *     position it correctly, and set its url.
+     */
+    renderTile: function() {
+        if (this.layer.async) {
+            this.initImgDiv();
+            // Asyncronous image requests call the asynchronous getURL method
+            // on the layer to fetch an image that covers 'this.bounds', in the scope of
+            // 'this', setting the 'url' property of the layer itself, and running
+            // the callback 'positionFrame' when the image request returns.
+            this.layer.getURLasync(this.bounds, this, "url", this.positionImage);
+        } else {
+            // syncronous image requests get the url and position the frame immediately,
+            // and don't wait for an image request to come back.
+          
+            this.url = this.layer.getURL(this.bounds);
+
+            this.initImgDiv();
+          
+            // position the frame immediately
+            this.positionImage(); 
+        }
+        return true;
+    },
+
+    /**
+     * Method: positionImage
+     * Using the properties currenty set on the layer, position the tile correctly.
+     * This method is used both by the async and non-async versions of the Tile.Image
+     * code.
+     */
+     positionImage: function() {
+        // if the this layer doesn't exist at the point the image is
+        // returned, do not attempt to use it for size computation
+        if (this.layer === null) {
+            return;
+        }
+        // position the frame 
+        OpenLayers.Util.modifyDOMElement(this.frame, 
+                                          null, this.position, this.size);   
+
+        var imageSize = this.layer.getImageSize(this.bounds); 
+        if (this.layerAlphaHack) {
+            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
+                    null, null, imageSize, this.url);
+        } else {
+            OpenLayers.Util.modifyDOMElement(this.imgDiv,
+                    null, null, imageSize) ;
+            this.imgDiv.src = this.url;
+        }
+    },
+
+    /** 
+     * Method: clear
+     *  Clear the tile of any bounds/position-related data so that it can 
+     *   be reused in a new location.
+     */
+    clear: function() {
+        if(this.imgDiv) {
+            this.hide();
+            if (OpenLayers.Tile.Image.useBlankTile) { 
+                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
+            }    
+        }
+    },
+
+    /**
+     * Method: initImgDiv
+     * Creates the imgDiv property on the tile.
+     */
+    initImgDiv: function() {
+        if (this.imgDiv == null) {
+            var offset = this.layer.imageOffset; 
+            var size = this.layer.getImageSize(this.bounds); 
+
+            if (this.layerAlphaHack) {
+                this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
+                                                               offset,
+                                                               size,
+                                                               null,
+                                                               "relative",
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               true);
+            } else {
+                this.imgDiv = OpenLayers.Util.createImage(null,
+                                                          offset,
+                                                          size,
+                                                          null,
+                                                          "relative",
+                                                          null,
+                                                          null,
+                                                          true);
+            }
+
+            // needed for changing to a different server for onload error
+            if (this.layer.url instanceof Array) {
+                this.imgDiv.urls = this.layer.url.slice();
+            }
+      
+            this.imgDiv.className = 'olTileImage';
+
+            /* checkImgURL used to be used to called as a work around, but it
+               ended up hiding problems instead of solving them and broke things
+               like relative URLs. See discussion on the dev list:
+               http://openlayers.org/pipermail/dev/2007-January/000205.html
+
+            OpenLayers.Event.observe( this.imgDiv, "load",
+                OpenLayers.Function.bind(this.checkImgURL, this) );
+            */
+            this.frame.style.zIndex = this.isBackBuffer ? 0 : 1;
+            this.frame.appendChild(this.imgDiv); 
+            this.layer.div.appendChild(this.frame); 
+
+            if(this.layer.opacity != null) {
+
+                OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
+                                                 null, null, null, 
+                                                 this.layer.opacity);
+            }
+
+            // we need this reference to check back the viewRequestID
+            this.imgDiv.map = this.layer.map;
+
+            //bind a listener to the onload of the image div so that we 
+            // can register when a tile has finished loading.
+            var onload = function() {
+
+                //normally isLoading should always be true here but there are some 
+                // right funky conditions where loading and then reloading a tile
+                // with the same url *really*fast*. this check prevents sending 
+                // a 'loadend' if the msg has already been sent
+                //
+                if (this.isLoading) { 
+                    this.isLoading = false; 
+                    this.events.triggerEvent("loadend"); 
+                }
+            };
+
+            if (this.layerAlphaHack) { 
+                OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load', 
+                                         OpenLayers.Function.bind(onload, this));    
+            } else { 
+                OpenLayers.Event.observe(this.imgDiv, 'load', 
+                                     OpenLayers.Function.bind(onload, this)); 
+            } 
+
+
+            // Bind a listener to the onerror of the image div so that we
+            // can registere when a tile has finished loading with errors.
+            var onerror = function() {
+
+                // If we have gone through all image reload attempts, it is time
+                // to realize that we are done with this image. Since
+                // OpenLayers.Util.onImageLoadError already has taken care about
+                // the error, we can continue as if the image was loaded
+                // successfully.
+                if (this.imgDiv._attempts > OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
+                    onload.call(this);
+                }
+            };
+            OpenLayers.Event.observe(this.imgDiv, "error",
+                                     OpenLayers.Function.bind(onerror, this));
+        }
+        
+        this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
+    },
+
+    /**
+     * Method: removeImgDiv
+     * Removes the imgDiv from the DOM and stops listening to events on it.
+     */
+    removeImgDiv: function() {
+        // unregister the "load" and "error" handlers. Only the "error" handler if
+        // this.layerAlphaHack is true.
+        OpenLayers.Event.stopObservingElement(this.imgDiv);
+        
+        if (this.imgDiv.parentNode == this.frame) {
+            this.frame.removeChild(this.imgDiv);
+            this.imgDiv.map = null;
+        }
+        this.imgDiv.urls = null;
+
+        var child = this.imgDiv.firstChild;
+        //check for children (alphaHack img or IFrame)
+        if (child) {
+            OpenLayers.Event.stopObservingElement(child);
+            this.imgDiv.removeChild(child);
+            delete child;
+        } else {
+            // abort any currently loading image
+            this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
+        }
+    },
+
+    /**
+     * Method: checkImgURL
+     * Make sure that the image that just loaded is the one this tile is meant
+     * to display, since panning/zooming might have changed the tile's URL in
+     * the meantime. If the tile URL did change before the image loaded, set
+     * the imgDiv display to 'none', as either (a) it will be reset to visible
+     * when the new URL loads in the image, or (b) we don't want to display
+     * this tile after all because its new bounds are outside our maxExtent.
+     * 
+     * This function should no longer  be neccesary with the improvements to
+     * Grid.js in OpenLayers 2.3. The lack of a good isEquivilantURL function
+     * caused problems in 2.2, but it's possible that with the improved 
+     * isEquivilant URL function, this might be neccesary at some point.
+     * 
+     * See discussion in the thread at 
+     * http://openlayers.org/pipermail/dev/2007-January/000205.html
+     */
+    checkImgURL: function () {
+        // Sometimes our image will load after it has already been removed
+        // from the map, in which case this check is not needed.  
+        if (this.layer) {
+            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src;
+            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
+                this.hide();
+            }
+        }
+    },
+    
+    /**
+     * Method: startTransition
+     * This method is invoked on tiles that are backBuffers for tiles in the
+     *     grid.  The grid tile is about to be cleared and a new tile source
+     *     loaded.  This is where the transition effect needs to be started
+     *     to provide visual continuity.
+     */
+    startTransition: function() {
+        // backBufferTile has to be valid and ready to use
+        if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
+            return;
+        }
+
+        // calculate the ratio of change between the current resolution of the
+        // backBufferTile and the layer.  If several animations happen in a
+        // row, then the backBufferTile will scale itself appropriately for
+        // each request.
+        var ratio = 1;
+        if (this.backBufferTile.resolution) {
+            ratio = this.backBufferTile.resolution / this.layer.getResolution();
+        }
+        
+        // if the ratio is not the same as it was last time (i.e. we are
+        // zooming), then we need to adjust the backBuffer tile
+        if (ratio != this.lastRatio) {
+            if (this.layer.transitionEffect == 'resize') {
+                // In this case, we can just immediately resize the 
+                // backBufferTile.
+                var upperLeft = new OpenLayers.LonLat(
+                    this.backBufferTile.bounds.left, 
+                    this.backBufferTile.bounds.top
+                );
+                var size = new OpenLayers.Size(
+                    this.backBufferTile.size.w * ratio,
+                    this.backBufferTile.size.h * ratio
+                );
+
+                var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
+                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame, 
+                                                 null, px, size);
+                var imageSize = this.backBufferTile.imageSize;
+                imageSize = new OpenLayers.Size(imageSize.w * ratio, 
+                                                imageSize.h * ratio);
+                var imageOffset = this.backBufferTile.imageOffset;
+                if(imageOffset) {
+                    imageOffset = new OpenLayers.Pixel(
+                        imageOffset.x * ratio, imageOffset.y * ratio
+                    );
+                }
+
+                OpenLayers.Util.modifyDOMElement(
+                    this.backBufferTile.imgDiv, null, imageOffset, imageSize
+                ) ;
+
+                this.backBufferTile.show();
+            }
+        } else {
+            // default effect is just to leave the existing tile
+            // until the new one loads if this is a singleTile and
+            // there was no change in resolution.  Otherwise we
+            // don't bother to show the backBufferTile at all
+            if (this.layer.singleTile) {
+                this.backBufferTile.show();
+            } else {
+                this.backBufferTile.hide();
+            }
+        }
+        this.lastRatio = ratio;
+
+    },
+    
+    /** 
+     * Method: show
+     * Show the tile by showing its frame.
+     */
+    show: function() {
+        this.frame.style.display = '';
+        // Force a reflow on gecko based browsers to actually show the element
+        // before continuing execution.
+        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, 
+                this.layer.transitionEffect) != -1) {
+            if (OpenLayers.IS_GECKO === true) { 
+                this.frame.scrollLeft = this.frame.scrollLeft; 
+            } 
+        }
+    },
+    
+    /** 
+     * Method: hide
+     * Hide the tile by hiding its frame.
+     */
+    hide: function() {
+        this.frame.style.display = 'none';
+    },
+    
+    CLASS_NAME: "OpenLayers.Tile.Image"
+  }
+);
+
+OpenLayers.Tile.Image.useBlankTile = ( 
+    OpenLayers.BROWSER_NAME == "safari" || 
+    OpenLayers.BROWSER_NAME == "opera"); 
+/* ======================================================================
+    OpenLayers/Format/GML/v2.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/GML/Base.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GML.v2
+ * Parses GML version 2.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.GML.Base>
+ */
+OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
+    
+    /**
+     * Property: schemaLocation
+     * {String} Schema location for a particular minor version.
+     */
+    schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
+
+    /**
+     * Constructor: OpenLayers.Format.GML.v2
+     * Create a parser for GML v2.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     *
+     * Valid options properties:
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (required).
+     * geometryName - {String} Geometry element name.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "gml": OpenLayers.Util.applyDefaults({
+            "outerBoundaryIs": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.outer = obj.components[0];
+            },
+            "innerBoundaryIs": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.inner.push(obj.components[0]);
+            },
+            "Box": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                if(!container.components) {
+                    container.components = [];
+                }
+                var min = obj.points[0];
+                var max = obj.points[1];
+                container.components.push(
+                    new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
+                );
+            }
+        }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
+        "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
+        "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
+    },
+
+    /**
+     * Method: write
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
+     *     An array of features or a single feature.
+     *
+     * Returns:
+     * {String} Given an array of features, a doc with a gml:featureMembers
+     *     element will be returned.  Given a single feature, a doc with a
+     *     gml:featureMember element will be returned.
+     */
+    write: function(features) {
+        var name;
+        if(features instanceof Array) {
+            // GML2 only has abstract feature collections
+            // wfs provides a feature collection from a well-known schema
+            name = "wfs:FeatureCollection";
+        } else {
+            name = "gml:featureMember";
+        }
+        var root = this.writeNode(name, features);
+        this.setAttributeNS(
+            root, this.namespaces["xsi"],
+            "xsi:schemaLocation", this.schemaLocation
+        );
+
+        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
+    },
+
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "gml": OpenLayers.Util.applyDefaults({
+            "Point": function(geometry) {
+                var node = this.createElementNSPlus("gml:Point");
+                this.writeNode("coordinates", [geometry], node);
+                return node;
+            },
+            "coordinates": function(points) {
+                var numPoints = points.length;
+                var parts = new Array(numPoints);
+                var point;
+                for(var i=0; i<numPoints; ++i) {
+                    point = points[i];
+                    if(this.xy) {
+                        parts[i] = point.x + "," + point.y;
+                    } else {
+                        parts[i] = point.y + "," + point.x;
+                    }
+                    if(point.z != undefined) { // allow null or undefined
+                        parts[i] += "," + point.z;
+                    }
+                }
+                return this.createElementNSPlus("gml:coordinates", {
+                    attributes: {
+                        decimal: ".", cs: ",", ts: " "
+                    },
+                    value: (numPoints == 1) ? parts[0] : parts.join(" ")
+                });
+            },
+            "LineString": function(geometry) {
+                var node = this.createElementNSPlus("gml:LineString");
+                this.writeNode("coordinates", geometry.components, node);
+                return node;
+            },
+            "Polygon": function(geometry) {
+                var node = this.createElementNSPlus("gml:Polygon");
+                this.writeNode("outerBoundaryIs", geometry.components[0], node);
+                for(var i=1; i<geometry.components.length; ++i) {
+                    this.writeNode(
+                        "innerBoundaryIs", geometry.components[i], node
+                    );
+                }
+                return node;
+            },
+            "outerBoundaryIs": function(ring) {
+                var node = this.createElementNSPlus("gml:outerBoundaryIs");
+                this.writeNode("LinearRing", ring, node);
+                return node;
+            },
+            "innerBoundaryIs": function(ring) {
+                var node = this.createElementNSPlus("gml:innerBoundaryIs");
+                this.writeNode("LinearRing", ring, node);
+                return node;
+            },
+            "LinearRing": function(ring) {
+                var node = this.createElementNSPlus("gml:LinearRing");
+                this.writeNode("coordinates", ring.components, node);
+                return node;
+            },
+            "Box": function(bounds) {
+                var node = this.createElementNSPlus("gml:Box");
+                this.writeNode("coordinates", [
+                    {x: bounds.left, y: bounds.bottom},
+                    {x: bounds.right, y: bounds.top}
+                ], node);
+                // srsName attribute is optional for gml:Box
+                if(this.srsName) {
+                    node.setAttribute("srsName", this.srsName);
+                }
+                return node;
+            }
+        }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
+        "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
+        "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.GML.v2" 
+
+});
+/* ======================================================================
+    OpenLayers/Format/Filter/v1_0_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/GML/v2.js
+ * @requires OpenLayers/Format/Filter/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1_0_0
+ * Write ogc:Filter version 1.0.0.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.GML.v2>
+ *  - <OpenLayers.Format.Filter.v1>
+ */
+OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
+    OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
+    
+    /**
+     * Constant: VERSION
+     * {String} 1.0.0
+     */
+    VERSION: "1.0.0",
+    
+    /**
+     * Property: schemaLocation
+     * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
+     */
+    schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
+
+    /**
+     * Constructor: OpenLayers.Format.Filter.v1_0_0
+     * Instances of this class are not created directly.  Use the
+     *     <OpenLayers.Format.Filter> constructor instead.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.GML.v2.prototype.initialize.apply(
+            this, [options]
+        );
+    },
+
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "ogc": OpenLayers.Util.applyDefaults({
+            "PropertyIsEqualTo": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.EQUAL_TO
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsNotEqualTo": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
+                });
+                this.readChildNodes(node, filter);
+                obj.filters.push(filter);
+            },
+            "PropertyIsLike": function(node, obj) {
+                var filter = new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LIKE
+                });
+                this.readChildNodes(node, filter);
+                var wildCard = node.getAttribute("wildCard");
+                var singleChar = node.getAttribute("singleChar");
+                var esc = node.getAttribute("escape");
+                filter.value2regex(wildCard, singleChar, esc);
+                obj.filters.push(filter);
+            }
+        }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
+        "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
+        "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]        
+    },
+
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "ogc": OpenLayers.Util.applyDefaults({
+            "PropertyIsEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsNotEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                this.writeNode("Literal", filter.value, node);
+                return node;
+            },
+            "PropertyIsLike": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
+                    attributes: {
+                        wildCard: "*", singleChar: ".", escape: "!"
+                    }
+                });
+                // no ogc:expression handling for now
+                this.writeNode("PropertyName", filter, node);
+                // convert regex string to ogc string
+                this.writeNode("Literal", filter.regex2value(), node);
+                return node;
+            },
+            "BBOX": function(filter) {
+                var node = this.createElementNSPlus("ogc:BBOX");
+                this.writeNode("PropertyName", filter, node);
+                var box = this.writeNode("gml:Box", filter.value, node);
+                if(filter.projection) {
+                    box.setAttribute("srsName", filter.projection);
+                }
+                return node;
+            }}, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
+            
+        "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
+        "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
+        
+    },
+
+    /**
+     * Method: writeSpatial
+     *
+     * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter.Spatial>} The filter.
+     * name - {String} Name of the generated XML element.
+     *
+     * Returns:
+     * {DOMElement} The created XML element.
+     */
+    writeSpatial: function(filter, name) {
+        var node = this.createElementNSPlus("ogc:"+name);
+        this.writeNode("PropertyName", filter, node);
+        var child;
+        if(filter.value instanceof OpenLayers.Geometry) {
+            child = this.writeNode("feature:_geometry", filter.value).firstChild;
+        } else {
+            child = this.writeNode("gml:Box", filter.value);
+        }
+        if(filter.projection) {
+            child.setAttribute("srsName", filter.projection);
+        }
+        node.appendChild(child);
+        return node;
+    },
+
+
+    CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" 
+
+});
+/* ======================================================================
+    OpenLayers/Format/WFST/v1_0_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/WFST/v1.js
+ * @requires OpenLayers/Format/Filter/v1_0_0.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFST.v1_0_0
+ * A format for creating WFS v1.0.0 transactions.  Create a new instance with the
+ *     <OpenLayers.Format.WFST.v1_0_0> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.Filter.v1_0_0>
+ *  - <OpenLayers.Format.WFST.v1>
+ */
+OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
+    OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
+    
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: "1.0.0",
+
+    /**
+     * APIProperty: srsNameInQuery
+     * {Boolean} If true the reference system is passed in Query requests
+     *     via the "srsName" attribute to the "wfs:Query" element, this
+     *     property defaults to false as it isn't WFS 1.0.0 compliant.
+     */
+    srsNameInQuery: false,
+    
+    /**
+     * Property: schemaLocations
+     * {Object} Properties are namespace aliases, values are schema locations.
+     */
+    schemaLocations: {
+        "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
+    },
+
+    /**
+     * Constructor: OpenLayers.Format.WFST.v1_0_0
+     * A class for parsing and generating WFS v1.0.0 transactions.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options properties:
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (optional).
+     * featurePrefix - {String} Feature namespace alias (optional - only used
+     *     if featureNS is provided).  Default is 'feature'.
+     * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
+        OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "wfs": OpenLayers.Util.applyDefaults({
+            "WFS_TransactionResponse": function(node, obj) {
+                obj.insertIds = [];
+                obj.success = false;
+                this.readChildNodes(node, obj);
+            },
+            "InsertResult": function(node, container) {
+                var obj = {fids: []};
+                this.readChildNodes(node, obj);
+                container.insertIds.push(obj.fids[0]);
+            },
+            "TransactionResult": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "Status": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "SUCCESS": function(node, obj) {
+                obj.success = true;
+            }
+        }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
+        "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
+        "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
+        "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
+    },
+
+    /**
+     * Property: writers
+     * As a compliment to the readers property, this structure contains public
+     *     writing functions grouped by namespace alias and named like the
+     *     node names they produce.
+     */
+    writers: {
+        "wfs": OpenLayers.Util.applyDefaults({
+            "Query": function(options) {
+                options = OpenLayers.Util.extend({
+                    featureNS: this.featureNS,
+                    featurePrefix: this.featurePrefix,
+                    featureType: this.featureType,
+                    srsName: this.srsName,
+                    srsNameInQuery: this.srsNameInQuery
+                }, options);
+                var node = this.createElementNSPlus("wfs:Query", {
+                    attributes: {
+                        typeName: (options.featureNS ? options.featurePrefix + ":" : "") +
+                            options.featureType
+                    }
+                });
+                if(options.srsNameInQuery && options.srsName) {
+                    node.setAttribute("srsName", options.srsName);
+                }
+                if(options.featureNS) {
+                    node.setAttribute("xmlns:" + options.featurePrefix, options.featureNS);
+                }
+                if(options.propertyNames) {
+                    for(var i=0,len = options.propertyNames.length; i<len; i++) {
+                        this.writeNode(
+                            "ogc:PropertyName", 
+                            {property: options.propertyNames[i]},
+                            node
+                        );
+                    }
+                }
+                if(options.filter) {
+                    this.setFilterProperty(options.filter);
+                    this.writeNode("ogc:Filter", options.filter, node);
+                }
+                return node;
+            }
+        }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
+        "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
+        "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
+        "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
+    },
+   
+    CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" 
+});
+/* ======================================================================
+    OpenLayers/Renderer/Elements.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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.
+        return this.getNextElement(rightIndex);
+    },
+    
+    /**
+     * 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:
+     * 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:
+     * {Integer} The z-index value for the specified node (from the node 
+     *     data itself).
+     */
+    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;
+        }
+    },
+
+    /**
+     * APIMethod: getNextElement
+     * Get the next element in the order stack.
+     * 
+     * Parameters:
+     * index - {Integer} The index of the current node in this.order.
+     * 
+     * Returns:
+     * {DOMElement} the node following the index passed in, or
+     *     null.
+     */
+    getNextElement: function(index) {
+        var nextIndex = index + 1;
+        if (nextIndex < this.order.length) {
+            var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
+            if (nextElement == undefined) {
+                nextElement = this.getNextElement(nextIndex);
+            }
+            return nextElement;
+        } else {
+            return null;
+        } 
+    },
+    
+    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.
+     * 
+     * Parameters:
+     * indexer - {<OpenLayers.ElementsIndexer>}
+     * newNode - {DOMElement}
+     * nextNode - {DOMElement}
+     * 
+     * Returns:
+     * {Integer}
+     */
+    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 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 result = nextNode._boundsBottom - newNode._boundsBottom;
+            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,
+    
+    /**
+     * Property: root
+     * {DOMElement}
+     */
+    root: null,
+    
+    /**
+     * Property: vectorRoot
+     * {DOMElement}
+     */
+    vectorRoot: null,
+
+    /**
+     * Property: textRoot
+     * {DOMElement}
+     */
+    textRoot: null,
+
+    /**
+     * 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: BACKGROUND_ID_SUFFIX
+     * {String}
+     */
+    LABEL_ID_SUFFIX: "_label",
+    
+    /**
+     * 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("_root");
+        this.vectorRoot = this.createRoot("_vroot");
+        this.textRoot = this.createRoot("_troot");
+        
+        this.root.appendChild(this.vectorRoot);
+        this.root.appendChild(this.textRoot);
+        
+        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() {
+        var child;
+        var root = this.vectorRoot;
+        if (root) {
+            while (child = root.firstChild) {
+                root.removeChild(child);
+            }
+        }
+        root = this.textRoot;
+        if (root) {
+            while (child = root.firstChild) {
+                root.removeChild(child);
+            }
+        }
+        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:
+     * 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;
+            }
+            return rendered;
+        };
+
+        rendered = false;
+        var removeBackground = false;
+        if (style.display != "none") {
+            if (style.backgroundGraphic) {
+                this.redrawBackgroundNode(geometry.id, geometry, style,
+                    featureId);
+            } else {
+                removeBackground = true;
+            }
+            rendered = this.redrawNode(geometry.id, geometry, style,
+                featureId);
+        }
+        if (rendered == false) {
+            var node = document.getElementById(geometry.id);
+            if (node) {
+                if (node._style.backgroundGraphic) {
+                    removeBackground = true;
+                }
+                node.parentNode.removeChild(node);
+            }
+        }
+        if (removeBackground) {
+            var node = document.getElementById(
+                geometry.id + this.BACKGROUND_ID_SUFFIX);
+            if (node) {
+                node.parentNode.removeChild(node);
+            }
+        }
+        return rendered;
+    },
+    
+    /**
+     * 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) {
+        style = this.applyDefaultSymbolizer(style);
+        // 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._boundsBottom = geometry.getBounds().bottom;
+        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.vectorRoot.insertBefore(node, insert);
+            } else {
+                this.vectorRoot.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.vectorRoot){ 
+                this.vectorRoot.appendChild(node);
+            }
+        }
+        
+        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
+        );
+    },
+
+    /**
+     * 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:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 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)
+     */
+    drawGeometryNode: function(node, geometry, style) {
+        style = style || node._style;
+
+        var options = {
+            'isFilled': style.fill === undefined ?
+                true :
+                style.fill,
+            'isStroked': style.stroke === undefined ?
+                !!style.strokeWidth :
+                style.stroke
+        };
+        var drawn;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                if(style.graphic === false) {
+                    options.isFilled = false;
+                    options.isStroked = false;
+                }
+                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._options = options; 
+
+        //set style
+        //TBD simplify this
+        if (drawn != false) {
+            return {
+                node: this.setStyle(node, style, options, geometry),
+                complete: drawn
+            };
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * 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: removeText
+     * Removes a label
+     * 
+     * Parameters:
+     * featureId - {String}
+     */
+    removeText: function(featureId) {
+        var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
+        if (label) {
+            this.textRoot.removeChild(label);
+        }
+    },
+
+    /**
+     * 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:
+     * geometry - {<OpenLayers.Geometry>}
+     * featureId - {String}
+     */
+    eraseGeometry: function(geometry, featureId) {
+        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], featureId);
+            }
+        } 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);
+                    }
+                }
+            }
+        }
+    },
+
+    /** 
+     * 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:
+     * {DOMElement} A new node of the given type and id.
+     */
+    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/Strategy/Fixed.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Fixed
+ * A simple strategy that requests features once and never requests new data.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * APIProperty: preload
+     * {Boolean} Load data before layer made visible. Enabling this may result
+     *   in considerable overhead if your application loads many data layers
+     *   that are not visible by default. Default is false.
+     */
+    preload: false,
+
+    /**
+     * Constructor: OpenLayers.Strategy.Fixed
+     * Create a new Fixed strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * APIMethod: destroy
+     * Clean up the strategy.
+     */
+    destroy: function() {
+        OpenLayers.Strategy.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * Method: activate
+     * Activate the strategy: load data or add listener to load when visible
+     *
+     * Returns:
+     * {Boolean} True if the strategy was successfully activated or false if
+     *      the strategy was already active.
+     */
+    activate: function() {
+        if(OpenLayers.Strategy.prototype.activate.apply(this, arguments)) {
+            this.layer.events.on({
+                "refresh": this.load,
+                scope: this
+            });
+            if(this.layer.visibility == true || this.preload) {
+                this.load();
+            } else {
+                this.layer.events.on({
+                    "visibilitychanged": this.load,
+                    scope: this
+                });
+            }
+            return true;
+        }
+        return false;
+    },
+    
+    /**
+     * Method: deactivate
+     * Deactivate the strategy.  Undo what is done in <activate>.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            this.layer.events.un({
+                "refresh": this.load,
+                "visibilitychanged": this.load,
+                scope: this
+            });
+        }
+        return deactivated;
+    },
+
+    /**
+     * Method: load
+     * Tells protocol to load data and unhooks the visibilitychanged event
+     *
+     * Parameters:
+     * options - {Object} options to pass to protocol read.
+     */
+    load: function(options) {
+        var layer = this.layer;
+        layer.events.triggerEvent("loadstart");
+        layer.protocol.read(OpenLayers.Util.applyDefaults({
+            callback: OpenLayers.Function.bind(this.merge, this,
+                layer.map.getProjectionObject()),
+            filter: layer.filter
+        }, options));
+        layer.events.un({
+            "visibilitychanged": this.load,
+            scope: this
+        });
+    },
+
+    /**
+     * Method: merge
+     * Add all features to the layer.
+     *
+     * Parameters:
+     * mapProjection - {OpenLayers.Projection} the map projection
+     * resp - {Object} options to pass to protocol read.
+     */
+    merge: function(mapProjection, resp) {
+        var layer = this.layer;
+        layer.destroyFeatures();
+        var features = resp.features;
+        if (features && features.length > 0) {
+            if(!mapProjection.equals(layer.projection)) {
+                var geom;
+                for(var i=0, len=features.length; i<len; ++i) {
+                    geom = features[i].geometry;
+                    if(geom) {
+                        geom.transform(layer.projection, mapProjection);
+                    }
+                }
+            }
+            layer.addFeatures(features);
+        }
+        layer.events.triggerEvent("loadend");
+    },
+
+    CLASS_NAME: "OpenLayers.Strategy.Fixed"
+});
+/* ======================================================================
+    OpenLayers/Protocol.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol
+ * Abstract vector layer protocol class.  Not to be instantiated directly.  Use
+ *     one of the protocol subclasses instead.
+ */
+OpenLayers.Protocol = OpenLayers.Class({
+    
+    /**
+     * Property: format
+     * {<OpenLayers.Format>} The format used by this protocol.
+     */
+    format: null,
+    
+    /**
+     * Property: options
+     * {Object} Any options sent to the constructor.
+     */
+    options: null,
+
+    /**
+     * Property: autoDestroy
+     * {Boolean} The creator of the protocol can set autoDestroy to false
+     *      to fully control when the protocol is destroyed. Defaults to
+     *      true.
+     */
+    autoDestroy: true,
+   
+    /**
+     * Property: defaultFilter
+     * {OpenLayers.Filter} Optional default filter to read requests
+     */
+    defaultFilter: null,
+    
+    /**
+     * Constructor: OpenLayers.Protocol
+     * Abstract class for vector protocols.  Create instances of a subclass.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        options = options || {};
+        OpenLayers.Util.extend(this, options);
+        this.options = options;
+    },
+
+    /**
+     * Method: mergeWithDefaultFilter
+     * Merge filter passed to the read method with the default one
+     *
+     * Parameters:
+     * filter - {OpenLayers.Filter}
+     */
+    mergeWithDefaultFilter: function(filter) {
+        var merged;
+        if (filter && this.defaultFilter) {
+            merged = new OpenLayers.Filter.Logical({
+                type: OpenLayers.Filter.Logical.AND,
+                filters: [this.defaultFilter, filter]
+            });
+        } else {
+            merged = filter || this.defaultFilter || undefined;
+        }
+        return merged;
+    },
+
+    /**
+     * APIMethod: destroy
+     * Clean up the protocol.
+     */
+    destroy: function() {
+        this.options = null;
+        this.format = null;
+    },
+    
+    /**
+     * APIMethod: read
+     * Construct a request for reading new features.
+     *
+     * Parameters:
+     * options - {Object} Optional object for configuring the request.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     * object, the same object will be passed to the callback function passed
+     * if one exists in the options object.
+     */
+    read: function(options) {
+        options = options || {};
+        options.filter = this.mergeWithDefaultFilter(options.filter);
+    },
+    
+    
+    /**
+     * APIMethod: create
+     * Construct a request for writing newly created features.
+     *
+     * Parameters:
+     * features - {Array({<OpenLayers.Feature.Vector>})} or
+     *            {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     * object, the same object will be passed to the callback function passed
+     * if one exists in the options object.
+     */
+    create: function() {
+    },
+    
+    /**
+     * APIMethod: update
+     * Construct a request updating modified features.
+     *
+     * Parameters:
+     * features - {Array({<OpenLayers.Feature.Vector>})} or
+     *            {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     * object, the same object will be passed to the callback function passed
+     * if one exists in the options object.
+     */
+    update: function() {
+    },
+    
+    /**
+     * APIMethod: delete
+     * Construct a request deleting a removed feature.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     * object, the same object will be passed to the callback function passed
+     * if one exists in the options object.
+     */
+    "delete": function() {
+    },
+
+    /**
+     * APIMethod: commit
+     * Go over the features and for each take action
+     * based on the feature state. Possible actions are create,
+     * update and delete.
+     *
+     * Parameters:
+     * features - {Array({<OpenLayers.Feature.Vector>})}
+     * options - {Object} Object whose possible keys are "create", "update",
+     *      "delete", "callback" and "scope", the values referenced by the
+     *      first three are objects as passed to the "create", "update", and
+     *      "delete" methods, the value referenced by the "callback" key is
+     *      a function which is called when the commit operation is complete
+     *      using the scope referenced by the "scope" key.
+     *
+     * Returns:
+     * {Array({<OpenLayers.Protocol.Response>})} An array of
+     * <OpenLayers.Protocol.Response> objects.
+     */
+    commit: function() {
+    },
+
+    /**
+     * Method: abort
+     * Abort an ongoing request.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>}
+     */
+    abort: function(response) {
+    },
+   
+    /**
+     * Method: createCallback
+     * Returns a function that applies the given public method with resp and
+     *     options arguments.
+     *
+     * Parameters:
+     * method - {Function} The method to be applied by the callback.
+     * response - {<OpenLayers.Protocol.Response>} The protocol response object.
+     * options - {Object} Options sent to the protocol method
+     */
+    createCallback: function(method, response, options) {
+        return OpenLayers.Function.bind(function() {
+            method.apply(this, [response, options]);
+        }, this);
+    },
+   
+    CLASS_NAME: "OpenLayers.Protocol" 
+});
+
+/**
+ * Class: OpenLayers.Protocol.Response
+ * Protocols return Response objects to their users.
+ */
+OpenLayers.Protocol.Response = OpenLayers.Class({
+    /**
+     * Property: code
+     * {Number} - OpenLayers.Protocol.Response.SUCCESS or
+     *            OpenLayers.Protocol.Response.FAILURE
+     */
+    code: null,
+
+    /**
+     * Property: requestType
+     * {String} The type of request this response corresponds to. Either
+     *      "create", "read", "update" or "delete".
+     */
+    requestType: null,
+
+    /**
+     * Property: last
+     * {Boolean} - true if this is the last response expected in a commit,
+     * false otherwise, defaults to true.
+     */
+    last: true,
+
+    /**
+     * Property: features
+     * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+     * The features returned in the response by the server.
+     */
+    features: null,
+
+    /**
+     * Property: reqFeatures
+     * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
+     * The features provided by the user and placed in the request by the
+     *      protocol.
+     */
+    reqFeatures: null,
+
+    /**
+     * Property: priv
+     */
+    priv: null,
+
+    /**
+     * Constructor: OpenLayers.Protocol.Response
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /**
+     * Method: success
+     *
+     * Returns:
+     * {Boolean} - true on success, false otherwise
+     */
+    success: function() {
+        return this.code > 0;
+    },
+
+    CLASS_NAME: "OpenLayers.Protocol.Response"
+});
+
+OpenLayers.Protocol.Response.SUCCESS = 1;
+OpenLayers.Protocol.Response.FAILURE = 0;
+/* ======================================================================
+    OpenLayers/Protocol/WFS.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Protocol.js
+ */
+
+/**
+ * Function: OpenLayers.Protocol.WFS
+ * Used to create a versioned WFS protocol.  Default version is 1.0.0.
+ *
+ * Returns:
+ * {<OpenLayers.Protocol>} A WFS protocol of the given version.
+ */
+OpenLayers.Protocol.WFS = function(options) {
+    options = OpenLayers.Util.applyDefaults(
+        options, OpenLayers.Protocol.WFS.DEFAULTS
+    );
+    var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
+    if(!cls) {
+        throw "Unsupported WFS version: " + options.version;
+    }
+    return new cls(options);
+};
+
+/**
+ * Function: OpenLayers.Protocol.WFS.fromWMSLayer
+ * Convenience function to create a WFS protocol from a WMS layer.  This makes
+ *     the assumption that a WFS requests can be issued at the same URL as
+ *     WMS requests and that a WFS featureType exists with the same name as the
+ *     WMS layer.
+ *     
+ * This function is designed to auto-configure <url>, <featureType>,
+ *     <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
+ *     srsName matching with the WMS layer will not work with WFS 1.0.0..
+ * 
+ * Parameters:
+ * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
+ *     FeatureType at the same server url with the same typename.
+ * options - {Object} Default properties to be set on the protocol.
+ *
+ */
+OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
+    var typeName, featurePrefix;
+    var param = layer.params["LAYERS"];
+    var parts = (param instanceof Array ? param[0] : param).split(":");
+    if(parts.length > 1) {
+        featurePrefix = parts[0];
+    }
+    typeName = parts.pop();
+    var protocolOptions = {
+        url: layer.url,
+        featureType: typeName,
+        featurePrefix: featurePrefix,
+        srsName: layer.projection && layer.projection.getCode() ||
+                 layer.map && layer.map.getProjectionObject().getCode(),
+        version: "1.1.0"
+    };
+    return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
+        options, protocolOptions
+    ));
+};
+
+/**
+ * Constant: OpenLayers.Protocol.WFS.DEFAULTS
+ */
+OpenLayers.Protocol.WFS.DEFAULTS = {
+    "version": "1.0.0"
+};
+/* ======================================================================
+    OpenLayers/Layer/Markers.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Markers
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer> 
+ */
+OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
+    
+    /** 
+     * APIProperty: isBaseLayer 
+     * {Boolean} Markers layer is never a base layer.  
+     */
+    isBaseLayer: false,
+    
+    /** 
+     * APIProperty: markers 
+     * {Array(<OpenLayers.Marker>)} internal marker list 
+     */
+    markers: null,
+
+
+    /** 
+     * Property: drawn 
+     * {Boolean} internal state of drawing. This is a workaround for the fact
+     * that the map does not call moveTo with a zoomChanged when the map is
+     * first starting up. This lets us catch the case where we have *never*
+     * drawn the layer, and draw it even if the zoom hasn't changed.
+     */
+    drawn: false,
+    
+    /**
+     * Constructor: OpenLayers.Layer.Markers 
+     * Create a Markers layer.
+     *
+     * Parameters:
+     * name - {String} 
+     * options - {Object} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+        this.markers = [];
+    },
+    
+    /**
+     * APIMethod: destroy 
+     */
+    destroy: function() {
+        this.clearMarkers();
+        this.markers = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * APIMethod: setOpacity
+     * Sets the opacity for all the markers.
+     * 
+     * Parameter:
+     * opacity - {Float}
+     */
+    setOpacity: function(opacity) {
+        if (opacity != this.opacity) {
+            this.opacity = opacity;
+            for (var i=0, len=this.markers.length; i<len; i++) {
+                this.markers[i].setOpacity(this.opacity);
+            }
+        }
+    },
+
+    /** 
+     * Method: moveTo
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} 
+     * zoomChanged - {Boolean} 
+     * dragging - {Boolean} 
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+
+        if (zoomChanged || !this.drawn) {
+            for(var i=0, len=this.markers.length; i<len; i++) {
+                this.drawMarker(this.markers[i]);
+            }
+            this.drawn = true;
+        }
+    },
+
+    /**
+     * APIMethod: addMarker
+     *
+     * Parameters:
+     * marker - {<OpenLayers.Marker>} 
+     */
+    addMarker: function(marker) {
+        this.markers.push(marker);
+
+        if (this.opacity != null) {
+            marker.setOpacity(this.opacity);
+        }
+
+        if (this.map && this.map.getExtent()) {
+            marker.map = this.map;
+            this.drawMarker(marker);
+        }
+    },
+
+    /**
+     * APIMethod: removeMarker
+     *
+     * Parameters:
+     * marker - {<OpenLayers.Marker>} 
+     */
+    removeMarker: function(marker) {
+        if (this.markers && this.markers.length) {
+            OpenLayers.Util.removeItem(this.markers, marker);
+            marker.erase();
+        }
+    },
+
+    /**
+     * Method: clearMarkers
+     * This method removes all markers from a layer. The markers are not
+     * destroyed by this function, but are removed from the list of markers.
+     */
+    clearMarkers: function() {
+        if (this.markers != null) {
+            while(this.markers.length > 0) {
+                this.removeMarker(this.markers[0]);
+            }
+        }
+    },
+
+    /** 
+     * Method: drawMarker
+     * Calculate the pixel location for the marker, create it, and 
+     *    add it to the layer's div
+     *
+     * Parameters:
+     * marker - {<OpenLayers.Marker>} 
+     */
+    drawMarker: function(marker) {
+        var px = this.map.getLayerPxFromLonLat(marker.lonlat);
+        if (px == null) {
+            marker.display(false);
+        } else {
+            if (!marker.isDrawn()) {
+                var markerImg = marker.draw(px);
+                this.div.appendChild(markerImg);
+            } else if(marker.icon) {
+                marker.icon.moveTo(px);
+            }
+        }
+    },
+    
+    /** 
+     * APIMethod: getDataExtent
+     * Calculates the max extent which includes all of the markers.
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>}
+     */
+    getDataExtent: function () {
+        var maxExtent = null;
+        
+        if ( this.markers && (this.markers.length > 0)) {
+            var maxExtent = new OpenLayers.Bounds();
+            for(var i=0, len=this.markers.length; i<len; i++) {
+                var marker = this.markers[i];
+                maxExtent.extend(marker.lonlat);
+            }
+        }
+
+        return maxExtent;
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.Markers"
+});
+/* ======================================================================
+    OpenLayers/Protocol/WFS/v1.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Protocol/WFS.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol.WFS.v1
+ * Abstract class for for v1.0.0 and v1.1.0 protocol.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Protocol>
+ */
+OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
+    
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: null,
+    
+    /**
+     * Property: srsName
+     * {String} Name of spatial reference system.  Default is "EPSG:4326".
+     */
+    srsName: "EPSG:4326",
+    
+    /**
+     * Property: featureType
+     * {String} Local feature typeName.
+     */
+    featureType: null,
+    
+    /**
+     * Property: featureNS
+     * {String} Feature namespace.
+     */
+    featureNS: null,
+    
+    /**
+     * Property: geometryName
+     * {String} Name of the geometry attribute for features.  Default is
+     *     "the_geom".
+     */
+    geometryName: "the_geom",
+    
+    /**
+     * Property: schema
+     * {String} Optional schema location that will be included in the
+     *     schemaLocation attribute value.  Note that the feature type schema
+     *     is required for a strict XML validator (on transactions with an
+     *     insert for example), but is *not* required by the WFS specification
+     *     (since the server is supposed to know about feature type schemas).
+     */
+    schema: null,
+
+    /**
+     * Property: featurePrefix
+     * {String} Namespace alias for feature type.  Default is "feature".
+     */
+    featurePrefix: "feature",
+    
+    /**
+     * Property: formatOptions
+     * {Object} Optional options for the format.  If a format is not provided,
+     *     this property can be used to extend the default format options.
+     */
+    formatOptions: null,
+
+    /** 
+     * Property: readFormat 
+     * {<OpenLayers.Format>} For WFS requests it is possible to get a  
+     *     different output format than GML. In that case, we cannot parse  
+     *     the response with the default format (WFST) and we need a different 
+     *     format for reading. 
+     */ 
+    readFormat: null,
+    
+    /**
+     * Property: readOptions
+     * {Object} Optional object to pass to format's read.
+     */
+    readOptions: null,
+    
+    /**
+     * Constructor: OpenLayers.Protocol.WFS
+     * A class for giving layers WFS protocol.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options properties:
+     * url - {String} URL to send requests to (required).
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (required, but can be autodetected
+     *     for reading if featurePrefix is provided and identical to the prefix
+     *     in the server response).
+     * featurePrefix - {String} Feature namespace alias (optional - only used
+     *     for writing if featureNS is provided).  Default is 'feature'.
+     * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
+     */
+    initialize: function(options) {
+        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
+        if(!options.format) {
+            this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
+                version: this.version,
+                featureType: this.featureType,
+                featureNS: this.featureNS,
+                featurePrefix: this.featurePrefix,
+                geometryName: this.geometryName,
+                srsName: this.srsName,
+                schema: this.schema
+            }, this.formatOptions));
+        }
+        if(!this.featureNS && this.featurePrefix) {
+            // featureNS autodetection
+            var readNode = this.format.readNode;
+            this.format.readNode = function(node, obj) {
+                if(!this.featureNS && node.prefix == this.featurePrefix) {
+                    this.featureNS = node.namespaceURI;
+                    this.setNamespace("feature", this.featureNS);
+                }
+                return readNode.apply(this, arguments);
+            };
+        }
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Clean up the protocol.
+     */
+    destroy: function() {
+        if(this.options && !this.options.format) {
+            this.format.destroy();
+        }
+        this.format = null;
+        OpenLayers.Protocol.prototype.destroy.apply(this);
+    },
+
+    /**
+     * APIMethod: read
+     * Construct a request for reading new features.  Since WFS splits the
+     *     basic CRUD operations into GetFeature requests (for read) and
+     *     Transactions (for all others), this method does not make use of the
+     *     format's read method (that is only about reading transaction
+     *     responses).
+     *
+     * To use a configured protocol to get e.g. a WFS hit count, applications
+     * could do the following:
+     *
+     * (code)
+     * protocol.read({
+     *     readOptions: {output: "object"},
+     *     resultType: "hits",
+     *     maxFeatures: null,
+     *     callback: function(resp) {
+     *         // process resp.numberOfFeatures here
+     *     }
+     * });
+     * (end)
+     */
+    read: function(options) {
+        OpenLayers.Protocol.prototype.read.apply(this, arguments);
+        options = OpenLayers.Util.extend({}, options);
+        OpenLayers.Util.applyDefaults(options, this.options || {});
+        var response = new OpenLayers.Protocol.Response({requestType: "read"});
+        
+        var data = OpenLayers.Format.XML.prototype.write.apply(
+            this.format, [this.format.writeNode("wfs:GetFeature", options)]
+        );
+
+        response.priv = OpenLayers.Request.POST({
+            url: options.url,
+            callback: this.createCallback(this.handleRead, response, options),
+            params: options.params,
+            headers: options.headers,
+            data: data
+        });        
+
+        return response;
+    },
+    
+    /**
+     * Method: handleRead
+     * Deal with response from the read request.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} The response object to pass
+     *     to the user callback.
+     * options - {Object} The user options passed to the read call.
+     */
+    handleRead: function(response, options) {
+        options = OpenLayers.Util.extend({}, options);
+        OpenLayers.Util.applyDefaults(options, this.options);
+
+        if(options.callback) {
+            var request = response.priv;
+            if(request.status >= 200 && request.status < 300) {
+                // success
+                if (options.readOptions && options.readOptions.output == "object") {
+                    OpenLayers.Util.extend(response, 
+                        this.parseResponse(request, options.readOptions));
+                } else {
+                    response.features = this.parseResponse(request, options.readOptions);
+                }
+                response.code = OpenLayers.Protocol.Response.SUCCESS;
+            } else {
+                // failure
+                response.code = OpenLayers.Protocol.Response.FAILURE;
+            }
+            options.callback.call(options.scope, response);
+        }
+    },
+
+    /**
+     * Method: parseResponse
+     * Read HTTP response body and return features
+     *
+     * Parameters:
+     * request - {XMLHttpRequest} The request object
+     * options - {Object} Optional object to pass to format's read
+     *
+     * Returns:
+     * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
+     *     {<OpenLayers.Feature.Vector>} 
+     * An object with a features property, an array of features or a single 
+     * feature.
+     */
+    parseResponse: function(request, options) {
+        var doc = request.responseXML;
+        if(!doc || !doc.documentElement) {
+            doc = request.responseText;
+        }
+        if(!doc || doc.length <= 0) {
+            return null;
+        }
+        return (this.readFormat !== null) ? this.readFormat.read(doc) : 
+            this.format.read(doc, options);
+    },
+
+    /**
+     * Method: commit
+     * Given a list of feature, assemble a batch request for update, create,
+     *     and delete transactions.  A commit call on the prototype amounts
+     *     to writing a WFS transaction - so the write method on the format
+     *     is used.
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>}
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} A response object with a features
+     *     property containing any insertIds and a priv property referencing
+     *     the XMLHttpRequest object.
+     */
+    commit: function(features, options) {
+
+        options = OpenLayers.Util.extend({}, options);
+        OpenLayers.Util.applyDefaults(options, this.options);
+        
+        var response = new OpenLayers.Protocol.Response({
+            requestType: "commit",
+            reqFeatures: features
+        });
+        response.priv = OpenLayers.Request.POST({
+            url: options.url,
+            data: this.format.write(features, options),
+            callback: this.createCallback(this.handleCommit, response, options)
+        });
+        
+        return response;
+    },
+    
+    /**
+     * Method: handleCommit
+     * Called when the commit request returns.
+     * 
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} The response object to pass
+     *     to the user callback.
+     * options - {Object} The user options passed to the commit call.
+     */
+    handleCommit: function(response, options) {
+        if(options.callback) {
+            var request = response.priv;
+
+            // ensure that we have an xml doc
+            var data = request.responseXML;
+            if(!data || !data.documentElement) {
+                data = request.responseText;
+            }
+            
+            var obj = this.format.read(data) || {};
+            
+            response.insertIds = obj.insertIds || [];
+            response.code = (obj.success) ?
+                OpenLayers.Protocol.Response.SUCCESS :
+                OpenLayers.Protocol.Response.FAILURE;
+            options.callback.call(options.scope, response);
+        }
+    },
+    
+    /**
+     * Method: filterDelete
+     * Send a request that deletes all features by their filter.
+     * 
+     * Parameters:
+     * filter - {OpenLayers.Filter} filter
+     */
+    filterDelete: function(filter, options) {
+        options = OpenLayers.Util.extend({}, options);
+        OpenLayers.Util.applyDefaults(options, this.options);    
+        
+        var response = new OpenLayers.Protocol.Response({
+            requestType: "commit"
+        });    
+        
+        var root = this.format.createElementNSPlus("wfs:Transaction", {
+            attributes: {
+                service: "WFS",
+                version: this.version
+            }
+        });
+        
+        var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
+            attributes: {
+                typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
+                    options.featureType
+            }
+        });       
+        
+        if(options.featureNS) {
+            deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
+        }
+        var filterNode = this.format.writeNode("ogc:Filter", filter);
+        
+        deleteNode.appendChild(filterNode);
+        
+        root.appendChild(deleteNode);
+        
+        var data = OpenLayers.Format.XML.prototype.write.apply(
+            this.format, [root]
+        );
+        
+        return OpenLayers.Request.POST({
+            url: this.url,
+            callback : options.callback || function(){},
+            data: data
+        });   
+        
+    },
+
+    /**
+     * Method: abort
+     * Abort an ongoing request, the response object passed to
+     * this method must come from this protocol (as a result
+     * of a read, or commit operation).
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>}
+     */
+    abort: function(response) {
+        if (response) {
+            response.priv.abort();
+        }
+    },
+  
+    CLASS_NAME: "OpenLayers.Protocol.WFS.v1" 
+});
+/* ======================================================================
+    OpenLayers/Filter/Spatial.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Filter.js
+ * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Spatial
+ * This class represents a spatial filter.
+ * Currently implemented: BBOX, DWithin and Intersects
+ * 
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
+
+    /**
+     * APIProperty: type
+     * {String} Type of spatial filter.
+     *
+     * The type should be one of:
+     * - OpenLayers.Filter.Spatial.BBOX
+     * - OpenLayers.Filter.Spatial.INTERSECTS
+     * - OpenLayers.Filter.Spatial.DWITHIN
+     * - OpenLayers.Filter.Spatial.WITHIN
+     * - OpenLayers.Filter.Spatial.CONTAINS
+     */
+    type: null,
+    
+    /**
+     * APIProperty: property
+     * {String} Name of the context property to compare.
+     */
+    property: null,
+    
+    /**
+     * APIProperty: value
+     * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
+     *     to be used by the filter.  Use bounds for BBOX filters and geometry
+     *     for INTERSECTS or DWITHIN filters.
+     */
+    value: null,
+
+    /**
+     * APIProperty: distance
+     * {Number} The distance to use in a DWithin spatial filter.
+     */
+    distance: null,
+
+    /**
+     * APIProperty: distanceUnits
+     * {String} The units to use for the distance, e.g. 'm'.
+     */
+    distanceUnits: null,
+    
+    /** 
+     * Constructor: OpenLayers.Filter.Spatial
+     * Creates a spatial filter.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *     filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Spatial>}
+     */
+    initialize: function(options) {
+        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+    },
+
+   /**
+    * Method: evaluate
+    * Evaluates this filter for a specific feature.
+    * 
+    * Parameters:
+    * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
+    * 
+    * Returns:
+    * {Boolean} The feature meets filter criteria.
+    */
+    evaluate: function(feature) {
+        var intersect = false;
+        switch(this.type) {
+            case OpenLayers.Filter.Spatial.BBOX:
+            case OpenLayers.Filter.Spatial.INTERSECTS:
+                if(feature.geometry) {
+                    var geom = this.value;
+                    if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
+                        geom = this.value.toGeometry();
+                    }
+                    if(feature.geometry.intersects(geom)) {
+                        intersect = true;
+                    }
+                }
+                break;
+            default:
+                OpenLayers.Console.error(
+                    OpenLayers.i18n("filterEvaluateNotImplemented"));
+                break;
+        }
+        return intersect;
+    },
+
+    /**
+     * APIMethod: clone
+     * Clones this filter.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Spatial>} Clone of this filter.
+     */
+    clone: function() {
+        var options = OpenLayers.Util.applyDefaults({
+            value: this.value && this.value.clone && this.value.clone()
+        }, this);
+        return new OpenLayers.Filter.Spatial(options);
+    },
+    CLASS_NAME: "OpenLayers.Filter.Spatial"
+});
+
+OpenLayers.Filter.Spatial.BBOX = "BBOX";
+OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
+OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
+OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
+OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
+/* ======================================================================
+    OpenLayers/Control.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Console.js
+ */
+
+/**
+ * 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 
+     * {Number} Controls can have a 'type'. The type determines the type of
+     * interactions which are possible with them when they are placed in an
+     * <OpenLayers.Control.Panel>. 
+     */
+    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: "",
+
+    /**
+     * APIProperty: autoActivate
+     * {Boolean} Activate the control when it is added to a map.  Default is
+     *     false.
+     */
+    autoActivate: false,
+
+    /** 
+     * 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 = OpenLayers.Function.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;
+        if(this.map) {
+            OpenLayers.Element.addClass(
+                this.map.viewPortDiv,
+                this.displayClass.replace(/ /g, "") + "Active"
+            );
+        }
+        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;
+            if(this.map) {
+                OpenLayers.Element.removeClass(
+                    this.map.viewPortDiv,
+                    this.displayClass.replace(/ /g, "") + "Active"
+                );
+            }
+            this.events.triggerEvent("deactivate");
+            return true;
+        }
+        return false;
+    },
+
+    CLASS_NAME: "OpenLayers.Control"
+});
+
+/**
+ * Constant: OpenLayers.Control.TYPE_BUTTON
+ */
+OpenLayers.Control.TYPE_BUTTON = 1;
+
+/**
+ * Constant: OpenLayers.Control.TYPE_TOGGLE
+ */
+OpenLayers.Control.TYPE_TOGGLE = 2;
+
+/**
+ * Constant: OpenLayers.Control.TYPE_TOOL
+ */
+OpenLayers.Control.TYPE_TOOL   = 3;
+/* ======================================================================
+    OpenLayers/Request.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * 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.Request = {
+    
+    /**
+     * Constant: DEFAULT_CONFIG
+     * {Object} Default configuration for all requests.
+     */
+    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
+    },
+    
+    /**
+     * Constant: URL_SPLIT_REGEX
+     */
+    URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
+    
+    /**
+     * 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.
+     * 
+     * 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.
+     */
+    events: new OpenLayers.Events(this, null, ["complete", "success", "failure"]),
+    
+    /**
+     * 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:
+     * config - {Object} Object containing properties for configuring the
+     *     request.  Allowed configuration properties are described below.
+     *     This object is modified and should not be reused.
+     *
+     * 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:
+     * {XMLHttpRequest} Request object.  To abort the request before a response
+     *     is received, call abort() on the request object.
+     */
+    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);
+
+        // 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;
+            }
+        }
+        var sameOrigin = !(url.indexOf("http") == 0);
+        var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
+        if (urlParts) {
+            var location = window.location;
+            sameOrigin =
+                urlParts[1] == location.protocol &&
+                urlParts[3] == location.hostname;
+            var uPort = urlParts[4], lPort = location.port;
+            if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
+                sameOrigin = sameOrigin && uPort == lPort;
+            }
+        }
+        if (!sameOrigin) {
+            if (config.proxy) {
+                if (typeof config.proxy == "function") {
+                    url = config.proxy(url);
+                } else {
+                    url = config.proxy + encodeURIComponent(url);
+                }
+            } else {
+                OpenLayers.Console.warn(
+                    OpenLayers.i18n("proxyNeeded"), {url: url});
+            }
+        }
+        request.open(
+            config.method, url, config.async, config.user, config.password
+        );
+        for(var header in config.headers) {
+            request.setRequestHeader(header, config.headers[header]);
+        }
+
+        var events = this.events;
+
+        // we want to execute runCallbacks with "this" as the
+        // execution scope
+        var self = this;
+        
+        request.onreadystatechange = function() {
+            if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
+                var proceed = events.triggerEvent(
+                    "complete",
+                    {request: request, config: config, requestUrl: url}
+                );
+                if(proceed !== false) {
+                    self.runCallbacks(
+                        {request: request, config: config, requestUrl: url}
+                    );
+                }
+            }
+        };
+        
+        // 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 {
+            window.setTimeout(function(){
+                if (request.readyState !== 0) { // W3C: 0-UNSENT
+                    request.send(config.data);
+                }
+            }, 0);
+        }
+        return request;
+    },
+    
+    /**
+     * Method: runCallbacks
+     * Calls the complete, success and failure callbacks. Application
+     *    can listen to the "complete" event, have the listener 
+     *    display a confirm window and always return false, and
+     *    execute OpenLayers.Request.runCallbacks if the user
+     *    hits "yes" in the confirm window.
+     *
+     * Parameters:
+     * options - {Object} Hash containing request, config and requestUrl keys
+     */
+    runCallbacks: function(options) {
+        var request = options.request;
+        var config = options.config;
+        
+        // bind callbacks to readyState 4 (done)
+        var complete = (config.scope) ?
+            OpenLayers.Function.bind(config.callback, config.scope) :
+            config.callback;
+        
+        // optional success callback
+        var success;
+        if(config.success) {
+            success = (config.scope) ?
+                OpenLayers.Function.bind(config.success, config.scope) :
+                config.success;
+        }
+
+        // optional failure callback
+        var failure;
+        if(config.failure) {
+            failure = (config.scope) ?
+                OpenLayers.Function.bind(config.failure, config.scope) :
+                config.failure;
+        }
+
+        complete(request);
+
+        if (!request.status || (request.status >= 200 && request.status < 300)) {
+            this.events.triggerEvent("success", options);
+            if(success) {
+                success(request);
+            }
+        }
+        if(request.status && (request.status < 200 || request.status >= 300)) {                    
+            this.events.triggerEvent("failure", options);
+            if(failure) {
+                failure(request);
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: GET
+     * Send an HTTP GET request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to GET.
+     *
+     * 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.
+     * 
+     * Returns:
+     * {XMLHttpRequest} Request object.
+     */
+    GET: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "GET"});
+        return OpenLayers.Request.issue(config);
+    },
+    
+    /**
+     * 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:
+     * 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:
+     * {XMLHttpRequest} Request object.
+     */
+    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";
+        }
+        return OpenLayers.Request.issue(config);
+    },
+    
+    /**
+     * 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:
+     * 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:
+     * {XMLHttpRequest} Request object.
+     */
+    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);
+    },
+    
+    /**
+     * APIMethod: DELETE
+     * Send an HTTP DELETE request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to DELETE.
+     *
+     * 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.
+     * 
+     * Returns:
+     * {XMLHttpRequest} Request object.
+     */
+    DELETE: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "DELETE"});
+        return OpenLayers.Request.issue(config);
+    },
+  
+    /**
+     * APIMethod: HEAD
+     * Send an HTTP HEAD request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to HEAD.
+     *
+     * 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.
+     * 
+     * Returns:
+     * {XMLHttpRequest} Request object.
+     */
+    HEAD: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "HEAD"});
+        return OpenLayers.Request.issue(config);
+    },
+    
+    /**
+     * APIMethod: OPTIONS
+     * Send an HTTP OPTIONS request.  Additional configuration properties are
+     *     documented in the <issue> method, with the method property set
+     *     to OPTIONS.
+     *
+     * 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.
+     * 
+     * Returns:
+     * {XMLHttpRequest} Request object.
+     */
+    OPTIONS: function(config) {
+        config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
+        return OpenLayers.Request.issue(config);
+    }
+
+};
+/* ======================================================================
+    OpenLayers/Request/XMLHttpRequest.js
+   ====================================================================== */
+
+// XMLHttpRequest.js Copyright (C) 2010 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,
+        bIE7    = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
+
+    // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
+    function fXMLHttpRequest() {
+        this._object    = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
+        this._listeners    = [];
+    };
+
+    // Constructor
+    function cXMLHttpRequest() {
+        return new fXMLHttpRequest;
+    };
+    cXMLHttpRequest.prototype    = fXMLHttpRequest.prototype;
+
+    // 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    = '';
+
+    // Priority proposal
+    cXMLHttpRequest.prototype.priority        = "NORMAL";
+
+    // 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) {
+        // Delete headers, required when object is reused
+        delete this._headers;
+
+        // When bAsync parameter value is omitted, use true as default
+        if (arguments.length < 3)
+            bAsync    = true;
+
+        // 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,
+            fOnUnload;
+
+        // BUGFIX: IE - memory leak on page unload (inter-page leak)
+        if (bIE && bAsync) {
+            fOnUnload = function() {
+                if (nState != cXMLHttpRequest.DONE) {
+                    fCleanTransport(oRequest);
+                    // Safe to abort here since onreadystatechange handler removed
+                    oRequest.abort();
+                }
+            };
+            window.attachEvent("onunload", fOnUnload);
+        }
+
+        // Add method sniffer
+        if (cXMLHttpRequest.onopen)
+            cXMLHttpRequest.onopen.apply(this, arguments);
+
+        if (arguments.length > 4)
+            this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+        else
+        if (arguments.length > 3)
+            this._object.open(sMethod, sUrl, bAsync, sUser);
+        else
+            this._object.open(sMethod, sUrl, bAsync);
+
+        this.readyState    = cXMLHttpRequest.OPENED;
+        fReadyStateChange(this);
+
+        this._object.onreadystatechange    = function() {
+            if (bGecko && !bAsync)
+                return;
+
+            // Synchronize state
+            oRequest.readyState        = oRequest._object.readyState;
+
+            //
+            fSynchronizeValues(oRequest);
+
+            // BUGFIX: Firefox fires unnecessary DONE when aborting
+            if (oRequest._aborted) {
+                // Reset readyState to UNSENT
+                oRequest.readyState    = cXMLHttpRequest.UNSENT;
+
+                // Return now
+                return;
+            }
+
+            if (oRequest.readyState == cXMLHttpRequest.DONE) {
+                // Free up queue
+                delete oRequest._data;
+/*                if (bAsync)
+                    fQueue_remove(oRequest);*/
+                //
+                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
+                    if (sUser) {
+                         if (sPassword)
+                            oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
+                        else
+                            oRequest._object.open(sMethod, sUrl, bAsync, sUser);
+                    }
+                    else
+                        oRequest._object.open(sMethod, sUrl, bAsync);
+                    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 until 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;
+        }
+    };
+    function fXMLHttpRequest_send(oRequest) {
+        oRequest._object.send(oRequest._data);
+
+        // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
+        if (bGecko && !oRequest._async) {
+            oRequest.readyState    = cXMLHttpRequest.OPENED;
+
+            // Synchronize state
+            fSynchronizeValues(oRequest);
+
+            // Simulate missing states
+            while (oRequest.readyState < cXMLHttpRequest.DONE) {
+                oRequest.readyState++;
+                fReadyStateChange(oRequest);
+                // Check if we are aborted
+                if (oRequest._aborted)
+                    return;
+            }
+        }
+    };
+    cXMLHttpRequest.prototype.send    = function(vData) {
+        // Add method sniffer
+        if (cXMLHttpRequest.onsend)
+            cXMLHttpRequest.onsend.apply(this, arguments);
+
+        if (!arguments.length)
+            vData    = null;
+
+        // 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 (!oRequest._headers["Content-Type"])
+                oRequest._object.setRequestHeader("Content-Type", "application/xml");
+        }
+
+        this._data    = vData;
+/*
+        // Add to queue
+        if (this._async)
+            fQueue_add(this);
+        else*/
+            fXMLHttpRequest_send(this);
+    };
+    cXMLHttpRequest.prototype.abort    = function() {
+        // Add method sniffer
+        if (cXMLHttpRequest.onabort)
+            cXMLHttpRequest.onabort.apply(this, arguments);
+
+        // BUGFIX: Gecko - unnecessary DONE when aborting
+        if (this.readyState > cXMLHttpRequest.UNSENT)
+            this._aborted    = true;
+
+        this._object.abort();
+
+        // BUGFIX: IE - memory leak
+        fCleanTransport(this);
+
+        this.readyState    = cXMLHttpRequest.UNSENT;
+
+        delete this._data;
+/*        if (this._async)
+            fQueue_remove(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);
+    };
+
+    // EventTarget interface implementation
+    cXMLHttpRequest.prototype.addEventListener    = function(sName, fHandler, bUseCapture) {
+        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
+            if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
+                return;
+        // Add listener
+        this._listeners.push([sName, fHandler, bUseCapture]);
+    };
+
+    cXMLHttpRequest.prototype.removeEventListener    = function(sName, fHandler, bUseCapture) {
+        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
+            if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
+                break;
+        // Remove listener
+        if (oListener)
+            this._listeners.splice(nIndex, 1);
+    };
+
+    cXMLHttpRequest.prototype.dispatchEvent    = function(oEvent) {
+        var oEventPseudo    = {
+            'type':            oEvent.type,
+            'target':        this,
+            'currentTarget':this,
+            'eventPhase':    2,
+            'bubbles':        oEvent.bubbles,
+            'cancelable':    oEvent.cancelable,
+            'timeStamp':    oEvent.timeStamp,
+            'stopPropagation':    function() {},    // There is no flow
+            'preventDefault':    function() {},    // There is no default action
+            'initEvent':        function() {}    // Original event object should be initialized
+        };
+
+        // Execute onreadystatechange
+        if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
+            (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
+
+        // Execute listeners
+        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
+            if (oListener[0] == oEventPseudo.type && !oListener[2])
+                (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
+    };
+
+    //
+    cXMLHttpRequest.prototype.toString    = function() {
+        return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
+    };
+
+    cXMLHttpRequest.toString    = function() {
+        return '[' + "XMLHttpRequest" + ']';
+    };
+
+    // Helper function
+    function fReadyStateChange(oRequest) {
+        // Sniffing code
+        if (cXMLHttpRequest.onreadystatechange)
+            cXMLHttpRequest.onreadystatechange.apply(oRequest);
+
+        // Fake event
+        oRequest.dispatchEvent({
+            'type':            "readystatechange",
+            'bubbles':        false,
+            'cancelable':    false,
+            'timeStamp':    new Date + 0
+        });
+    };
+
+    function fGetDocument(oRequest) {
+        var oDocument    = oRequest.responseXML,
+            sResponse    = oRequest.responseText;
+        // Try parsing responseText
+        if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
+            oDocument    = new window.ActiveXObject("Microsoft.XMLDOM");
+            oDocument.async                = false;
+            oDocument.validateOnParse    = false;
+            oDocument.loadXML(sResponse);
+        }
+        // Check if there is no error in document
+        if (oDocument)
+            if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (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;
+    };
+/*
+    // Queue manager
+    var oQueuePending    = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
+        aQueueRunning    = [];
+    function fQueue_add(oRequest) {
+        oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
+        //
+        setTimeout(fQueue_process);
+    };
+
+    function fQueue_remove(oRequest) {
+        for (var nIndex = 0, bFound    = false; nIndex < aQueueRunning.length; nIndex++)
+            if (bFound)
+                aQueueRunning[nIndex - 1]    = aQueueRunning[nIndex];
+            else
+            if (aQueueRunning[nIndex] == oRequest)
+                bFound    = true;
+        if (bFound)
+            aQueueRunning.length--;
+        //
+        setTimeout(fQueue_process);
+    };
+
+    function fQueue_process() {
+        if (aQueueRunning.length < 6) {
+            for (var sPriority in oQueuePending) {
+                if (oQueuePending[sPriority].length) {
+                    var oRequest    = oQueuePending[sPriority][0];
+                    oQueuePending[sPriority]    = oQueuePending[sPriority].slice(1);
+                    //
+                    aQueueRunning.push(oRequest);
+                    // Send request
+                    fXMLHttpRequest_send(oRequest);
+                    break;
+                }
+            }
+        }
+    };
+*/
+    // 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-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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
+ * @requires OpenLayers/Lang.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: OpenLayers.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: OpenLayers.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/Drag.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Drag
+ * The drag handler is used to deal with sequences of browser events related
+ *     to dragging.  The handler is used by controls that want to know when
+ *     a drag sequence begins, when a drag is happening, and when it has
+ *     finished.
+ *
+ * Controls that use the drag handler typically construct it with callbacks
+ *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
+ *     when the drag begins, with each move, and when the drag is done.  In
+ *     addition, controls can have callbacks keyed to 'up' and 'out' if they
+ *     care to differentiate between the types of events that correspond with
+ *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
+ *     the 'down' and 'up' callbacks will be called, but not the 'done'
+ *     callback.
+ *
+ * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
+  
+    /** 
+     * Property: started
+     * {Boolean} When a mousedown event is received, we want to record it, but
+     *     not set 'dragging' until the mouse moves after starting. 
+     */
+    started: false,
+    
+    /**
+     * Property: stopDown
+     * {Boolean} Stop propagation of mousedown events from getting to listeners
+     *     on the same element.  Default is true.
+     */
+    stopDown: true,
+
+    /** 
+     * Property: dragging 
+     * {Boolean} 
+     */
+    dragging: false,
+
+    /** 
+     * Property: last
+     * {<OpenLayers.Pixel>} The last pixel location of the drag.
+     */
+    last: null,
+
+    /** 
+     * Property: start
+     * {<OpenLayers.Pixel>} The first pixel location of the drag.
+     */
+    start: null,
+
+    /**
+     * Property: oldOnselectstart
+     * {Function}
+     */
+    oldOnselectstart: null,
+    
+    /**
+     * Property: interval
+     * {Integer} In order to increase performance, an interval (in 
+     *     milliseconds) can be set to reduce the number of drag events 
+     *     called. If set, a new drag event will not be set until the 
+     *     interval has passed. 
+     *     Defaults to 0, meaning no interval. 
+     */
+    interval: 0,
+    
+    /**
+     * Property: timeoutId
+     * {String} The id of the timeout used for the mousedown interval.
+     *     This is "private", and should be left alone.
+     */
+    timeoutId: null,
+    
+    /**
+     * APIProperty: documentDrag
+     * {Boolean} If set to true, the handler will also handle mouse moves when
+     *     the cursor has moved out of the map viewport. Default is false.
+     */
+    documentDrag: false,
+    
+    /**
+     * Property: documentEvents
+     * {Boolean} Are we currently observing document events?
+     */
+    documentEvents: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.Drag
+     * Returns OpenLayers.Handler.Drag
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that is making use of
+     *     this handler.  If a handler is being used without a control, the
+     *     handlers setMap method must be overridden to deal properly with
+     *     the map.
+     * callbacks - {Object} An object containing a single function to be
+     *     called when the drag operation is finished. The callback should
+     *     expect to recieve a single argument, the pixel location of the event.
+     *     Callbacks for 'move' and 'done' are supported. You can also speficy
+     *     callbacks for 'down', 'up', and 'out' to respond to those events.
+     * options - {Object} 
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        
+        if (this.documentDrag === true) {
+            var me = this;
+            this._docMove = function(evt) {
+                me.mousemove({
+                    xy: {x: evt.clientX, y: evt.clientY},
+                    element: document
+                });
+            };
+            this._docUp = function(evt) {
+                me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
+            };
+        }
+    },
+    
+    /**
+     * The four methods below (down, move, up, and out) are used by subclasses
+     *     to do their own processing related to these mouse events.
+     */
+    
+    /**
+     * Method: down
+     * This method is called during the handling of the mouse down event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse down event
+     */
+    down: function(evt) {
+    },
+    
+    /**
+     * Method: move
+     * This method is called during the handling of the mouse move event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse move event
+     *
+     */
+    move: function(evt) {
+    },
+
+    /**
+     * Method: up
+     * This method is called during the handling of the mouse up event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse up event
+     */
+    up: function(evt) {
+    },
+
+    /**
+     * Method: out
+     * This method is called during the handling of the mouse out event.
+     *     Subclasses can do their own processing here.
+     *
+     * Parameters:
+     * evt - {Event} The mouse out event
+     */
+    out: function(evt) {
+    },
+
+    /**
+     * The methods below are part of the magic of event handling.  Because
+     *     they are named like browser events, they are registered as listeners
+     *     for the events they represent.
+     */
+
+    /**
+     * Method: mousedown
+     * Handle mousedown events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mousedown: function (evt) {
+        var propagate = true;
+        this.dragging = false;
+        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
+            this.started = true;
+            this.start = evt.xy;
+            this.last = evt.xy;
+            OpenLayers.Element.addClass(
+                this.map.viewPortDiv, "olDragDown"
+            );
+            this.down(evt);
+            this.callback("down", [evt.xy]);
+            OpenLayers.Event.stop(evt);
+            
+            if(!this.oldOnselectstart) {
+                this.oldOnselectstart = (document.onselectstart) ? document.onselectstart : OpenLayers.Function.True;
+            }
+            document.onselectstart = OpenLayers.Function.False;
+            
+            propagate = !this.stopDown;
+        } else {
+            this.started = false;
+            this.start = null;
+            this.last = null;
+        }
+        return propagate;
+    },
+
+    /**
+     * Method: mousemove
+     * Handle mousemove events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mousemove: function (evt) {
+        if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {
+            if(this.documentDrag === true && this.documentEvents) {
+                if(evt.element === document) {
+                    this.adjustXY(evt);
+                    // do setEvent manually because the documentEvents are not
+                    // registered with the map
+                    this.setEvent(evt);
+                } else {
+                    this.removeDocumentEvents();
+                }
+            }
+            if (this.interval > 0) {
+                this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval);
+            }
+            this.dragging = true;
+            this.move(evt);
+            this.callback("move", [evt.xy]);
+            if(!this.oldOnselectstart) {
+                this.oldOnselectstart = document.onselectstart;
+                document.onselectstart = OpenLayers.Function.False;
+            }
+            this.last = this.evt.xy;
+        }
+        return true;
+    },
+    
+    /**
+     * Method: removeTimeout
+     * Private. Called by mousemove() to remove the drag timeout.
+     */
+    removeTimeout: function() {
+        this.timeoutId = null;
+    },
+
+    /**
+     * Method: mouseup
+     * Handle mouseup events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mouseup: function (evt) {
+        if (this.started) {
+            if(this.documentDrag === true && this.documentEvents) {
+                this.adjustXY(evt);
+                this.removeDocumentEvents();
+            }
+            var dragged = (this.start != this.last);
+            this.started = false;
+            this.dragging = false;
+            OpenLayers.Element.removeClass(
+                this.map.viewPortDiv, "olDragDown"
+            );
+            this.up(evt);
+            this.callback("up", [evt.xy]);
+            if(dragged) {
+                this.callback("done", [evt.xy]);
+            }
+            document.onselectstart = this.oldOnselectstart;
+        }
+        return true;
+    },
+
+    /**
+     * Method: mouseout
+     * Handle mouseout events
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    mouseout: function (evt) {
+        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
+            if(this.documentDrag === true) {
+                this.addDocumentEvents();
+            } else {
+                var dragged = (this.start != this.last);
+                this.started = false; 
+                this.dragging = false;
+                OpenLayers.Element.removeClass(
+                    this.map.viewPortDiv, "olDragDown"
+                );
+                this.out(evt);
+                this.callback("out", []);
+                if(dragged) {
+                    this.callback("done", [evt.xy]);
+                }
+                if(document.onselectstart) {
+                    document.onselectstart = this.oldOnselectstart;
+                }
+            }
+        }
+        return true;
+    },
+
+    /**
+     * Method: click
+     * The drag handler captures the click event.  If something else registers
+     *     for clicks on the same element, its listener will not be called 
+     *     after a drag.
+     * 
+     * Parameters: 
+     * evt - {Event} 
+     * 
+     * Returns:
+     * {Boolean} Let the event propagate.
+     */
+    click: function (evt) {
+        // let the click event propagate only if the mouse moved
+        return (this.start == this.last);
+    },
+
+    /**
+     * Method: activate
+     * Activate the handler.
+     * 
+     * Returns:
+     * {Boolean} The handler was successfully activated.
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            this.dragging = false;
+            activated = true;
+        }
+        return activated;
+    },
+
+    /**
+     * Method: deactivate 
+     * Deactivate the handler.
+     * 
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.started = false;
+            this.dragging = false;
+            this.start = null;
+            this.last = null;
+            deactivated = true;
+            OpenLayers.Element.removeClass(
+                this.map.viewPortDiv, "olDragDown"
+            );
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: adjustXY
+     * Converts event coordinates that are relative to the document body to
+     * ones that are relative to the map viewport. The latter is the default in
+     * OpenLayers.
+     * 
+     * Parameters:
+     * evt - {Object}
+     */
+    adjustXY: function(evt) {
+        var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
+        evt.xy.x -= pos[0];
+        evt.xy.y -= pos[1];
+    },
+    
+    /**
+     * Method: addDocumentEvents
+     * Start observing document events when documentDrag is true and the mouse
+     * cursor leaves the map viewport while dragging.
+     */
+    addDocumentEvents: function() {
+        OpenLayers.Element.addClass(document.body, "olDragDown");
+        this.documentEvents = true;
+        OpenLayers.Event.observe(document, "mousemove", this._docMove);
+        OpenLayers.Event.observe(document, "mouseup", this._docUp);
+    },
+    
+    /**
+     * Method: removeDocumentEvents
+     * Stops observing document events when documentDrag is true and the mouse
+     * cursor re-enters the map viewport while dragging.
+     */
+    removeDocumentEvents: function() {
+        OpenLayers.Element.removeClass(document.body, "olDragDown");
+        this.documentEvents = false;
+        OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
+        OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Drag"
+});
+/* ======================================================================
+    OpenLayers/Handler/Box.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ * @requires OpenLayers/Handler/Drag.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Box
+ * Handler for dragging a rectangle across the map.  Box is displayed 
+ * on mouse down, moves on mouse move, and is finished on mouse up.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Handler> 
+ */
+OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
+
+    /** 
+     * Property: dragHandler 
+     * {<OpenLayers.Handler.Drag>} 
+     */
+    dragHandler: null,
+
+    /**
+     * APIProperty: boxDivClassName
+     * {String} The CSS class to use for drawing the box. Default is
+     *     olHandlerBoxZoomBox
+     */
+    boxDivClassName: 'olHandlerBoxZoomBox',
+    
+    /**
+     * Property: boxCharacteristics
+     * {Object} Caches some box characteristics from css. This is used
+     *     by the getBoxCharacteristics method.
+     */
+    boxCharacteristics: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.Box
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} 
+     * callbacks - {Object} An object with a "done" property whose value is a
+     *     callback to be called when the box drag operation is finished.  
+     *     The callback should expect to recieve a single argument, the box 
+     *     bounds or a pixel. If the box dragging didn't span more than a 5 
+     *     pixel distance, a pixel will be returned instead of a bounds object.
+     * options - {Object} 
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        this.dragHandler = new OpenLayers.Handler.Drag(
+            this, 
+            {
+                down: this.startBox, 
+                move: this.moveBox, 
+                out: this.removeBox,
+                up: this.endBox
+            }, 
+            {keyMask: this.keyMask}
+        );
+    },
+
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+        if (this.dragHandler) {
+            this.dragHandler.destroy();
+            this.dragHandler = null;
+        }            
+        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * Method: setMap
+     */
+    setMap: function (map) {
+        OpenLayers.Handler.prototype.setMap.apply(this, arguments);
+        if (this.dragHandler) {
+            this.dragHandler.setMap(map);
+        }
+    },
+
+    /**
+    * Method: startBox
+    *
+    * Parameters:
+    * evt - {Event} 
+    */
+    startBox: function (xy) {
+        this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+             new OpenLayers.Pixel(-9999, -9999));
+        this.zoomBox.className = this.boxDivClassName;                                         
+        this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+        this.map.viewPortDiv.appendChild(this.zoomBox);
+
+        OpenLayers.Element.addClass(
+            this.map.viewPortDiv, "olDrawBox"
+        );
+    },
+
+    /**
+    * Method: moveBox
+    */
+    moveBox: function (xy) {
+        var startX = this.dragHandler.start.x;
+        var startY = this.dragHandler.start.y;
+        var deltaX = Math.abs(startX - xy.x);
+        var deltaY = Math.abs(startY - xy.y);
+        this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+        this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+        this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
+        this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
+
+        // depending on the box model, modify width and height to take borders
+        // of the box into account
+        var box = this.getBoxCharacteristics();
+        if (box.newBoxModel) {
+            if (xy.x > startX) {
+                this.zoomBox.style.width =
+                    Math.max(1, deltaX - box.xOffset) + "px";
+            }
+            if (xy.y > startY) {
+                this.zoomBox.style.height =
+                    Math.max(1, deltaY - box.yOffset) + "px";
+            }
+        }
+    },
+
+    /**
+    * Method: endBox
+    */
+    endBox: function(end) {
+        var result;
+        if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||    
+            Math.abs(this.dragHandler.start.y - end.y) > 5) {   
+            var start = this.dragHandler.start;
+            var top = Math.min(start.y, end.y);
+            var bottom = Math.max(start.y, end.y);
+            var left = Math.min(start.x, end.x);
+            var right = Math.max(start.x, end.x);
+            result = new OpenLayers.Bounds(left, bottom, right, top);
+        } else {
+            result = this.dragHandler.start.clone(); // i.e. OL.Pixel
+        } 
+        this.removeBox();
+
+        this.callback("done", [result]);
+    },
+
+    /**
+     * Method: removeBox
+     * Remove the zoombox from the screen and nullify our reference to it.
+     */
+    removeBox: function() {
+        this.map.viewPortDiv.removeChild(this.zoomBox);
+        this.zoomBox = null;
+        this.boxCharacteristics = null;
+        OpenLayers.Element.removeClass(
+            this.map.viewPortDiv, "olDrawBox"
+        );
+
+    },
+
+    /**
+     * Method: activate
+     */
+    activate: function () {
+        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            this.dragHandler.activate();
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Method: deactivate
+     */
+    deactivate: function () {
+        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.dragHandler.deactivate();
+            return true;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * 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(
+                this.zoomBox, "border-right-width")) + 1;
+            var yOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+                "border-top-width")) + parseInt(OpenLayers.Element.getStyle(
+                this.zoomBox, "border-bottom-width")) + 1;
+            // all browsers use the new box model, except IE in quirks mode
+            var newBoxModel = OpenLayers.BROWSER_NAME == "msie" ?
+                document.compatMode != "BackCompat" : true;
+            this.boxCharacteristics = {
+                xOffset: xOffset,
+                yOffset: yOffset,
+                newBoxModel: newBoxModel
+            };
+        }
+        return this.boxCharacteristics;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Box"
+});
+/* ======================================================================
+    OpenLayers/Control/ZoomBox.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Handler/Box.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ZoomBox
+ * 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>
+ */
+OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
+    /**
+     * Property: type
+     * {OpenLayers.Control.TYPE}
+     */
+    type: OpenLayers.Control.TYPE_TOOL,
+
+    /**
+     * Property: out
+     * {Boolean} Should the control be used for zooming out?
+     */
+    out: false,
+
+    /**
+     * Property: alwaysZoom
+     * {Boolean} Always zoom in/out, when box drawed 
+     */
+    alwaysZoom: false,
+
+    /**
+     * Method: draw
+     */    
+    draw: function() {
+        this.handler = new OpenLayers.Handler.Box( this,
+                            {done: this.zoomBox}, {keyMask: this.keyMask} );
+    },
+
+    /**
+     * Method: zoomBox
+     *
+     * Parameters:
+     * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
+     */
+    zoomBox: function (position) {
+        if (position instanceof OpenLayers.Bounds) {
+            var bounds;
+            if (!this.out) {
+                var minXY = this.map.getLonLatFromPixel(
+                            new OpenLayers.Pixel(position.left, position.bottom));
+                var maxXY = this.map.getLonLatFromPixel(
+                            new OpenLayers.Pixel(position.right, position.top));
+                bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
+                                               maxXY.lon, maxXY.lat);
+            } else {
+                var pixWidth = Math.abs(position.right-position.left);
+                var pixHeight = Math.abs(position.top-position.bottom);
+                var zoomFactor = Math.min((this.map.size.h / pixHeight),
+                    (this.map.size.w / pixWidth));
+                var extent = this.map.getExtent();
+                var center = this.map.getLonLatFromPixel(
+                    position.getCenterPixel());
+                var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
+                var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
+                var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
+                var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
+                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),
+                               this.map.getZoom() + 1);
+            } else {
+                this.map.setCenter(this.map.getLonLatFromPixel(position),
+                               this.map.getZoom() - 1);
+            }
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Control.ZoomBox"
+});
+/* ======================================================================
+    OpenLayers/Control/DragPan.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Handler/Drag.js
+ */
+
+/**
+ * Class: OpenLayers.Control.DragPan
+ * The DragPan control pans the map with a drag of the mouse.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
+
+    /** 
+     * Property: type
+     * {OpenLayers.Control.TYPES}
+     */
+    type: OpenLayers.Control.TYPE_TOOL,
+    
+    /**
+     * Property: panned
+     * {Boolean} The map moved.
+     */
+    panned: false,
+    
+    /**
+     * Property: interval
+     * {Integer} The number of milliseconds that should ellapse before
+     *     panning the map again. Set this to increase dragging performance.
+     *     Defaults to 25 milliseconds.
+     */
+    interval: 25,
+    
+    /**
+     * APIProperty: documentDrag
+     * {Boolean} If set to true, mouse dragging will continue even if the
+     *     mouse cursor leaves the map viewport. Default is false.
+     */
+    documentDrag: false,
+    
+    /**
+     * Method: draw
+     * Creates a Drag handler, using <panMap> and
+     * <panMapDone> as callbacks.
+     */    
+    draw: function() {
+        this.handler = new OpenLayers.Handler.Drag(this, {
+                "move": this.panMap,
+                "done": this.panMapDone
+            }, {
+                interval: this.interval,
+                documentDrag: this.documentDrag
+            }
+        );
+    },
+
+    /**
+    * Method: panMap
+    *
+    * Parameters:
+    * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
+    */
+    panMap: function(xy) {
+        this.panned = true;
+        this.map.pan(
+            this.handler.last.x - xy.x,
+            this.handler.last.y - xy.y,
+            {dragging: this.handler.dragging, animate: false}
+        );
+    },
+    
+    /**
+     * Method: panMapDone
+     * Finish the panning operation.  Only call setCenter (through <panMap>)
+     *     if the map has actually been moved.
+     *
+     * Parameters:
+     * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
+     */
+    panMapDone: function(xy) {
+        if(this.panned) {
+            this.panMap(xy);
+            this.panned = false;
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Control.DragPan"
+});
+/* ======================================================================
+    OpenLayers/Handler/Click.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Click
+ * A handler for mouse clicks.  The intention of this handler is to give
+ *     controls more flexibility with handling clicks.  Browsers trigger
+ *     click events twice for a double-click.  In addition, the mousedown,
+ *     mousemove, mouseup sequence fires a click event.  With this handler,
+ *     controls can decide whether to ignore clicks associated with a double
+ *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
+ *     that include a drag.  Create a new instance with the
+ *     <OpenLayers.Handler.Click> constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler> 
+ */
+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * APIProperty: delay
+     * {Number} Number of milliseconds between clicks before the event is
+     *     considered a double-click.
+     */
+    delay: 300,
+    
+    /**
+     * APIProperty: single
+     * {Boolean} Handle single clicks.  Default is true.  If false, clicks
+     * will not be reported.  If true, single-clicks will be reported.
+     */
+    single: true,
+    
+    /**
+     * APIProperty: double
+     * {Boolean} Handle double-clicks.  Default is false.
+     */
+    'double': false,
+    
+    /**
+     * APIProperty: pixelTolerance
+     * {Number} Maximum number of pixels between mouseup and mousedown for an
+     *     event to be considered a click.  Default is 0.  If set to an
+     *     integer value, clicks with a drag greater than the value will be
+     *     ignored.  This property can only be set when the handler is
+     *     constructed.
+     */
+    pixelTolerance: 0,
+    
+    /**
+     * APIProperty: stopSingle
+     * {Boolean} Stop other listeners from being notified of clicks.  Default
+     *     is false.  If true, any click listeners registered before this one
+     *     will not be notified of *any* click event (associated with double
+     *     or single clicks).
+     */
+    stopSingle: false,
+    
+    /**
+     * APIProperty: stopDouble
+     * {Boolean} Stop other listeners from being notified of double-clicks.
+     *     Default is false.  If true, any click listeners registered before
+     *     this one will not be notified of *any* double-click events.
+     * 
+     * The one caveat with stopDouble is that given a map with two click
+     *     handlers, one with stopDouble true and the other with stopSingle
+     *     true, the stopSingle handler should be activated last to get
+     *     uniform cross-browser performance.  Since IE triggers one click
+     *     with a dblclick and FF triggers two, if a stopSingle handler is
+     *     activated first, all it gets in IE is a single click when the
+     *     second handler stops propagation on the dblclick.
+     */
+    stopDouble: false,
+
+    /**
+     * Property: timerId
+     * {Number} The id of the timeout waiting to clear the <delayedCall>.
+     */
+    timerId: null,
+    
+    /**
+     * Property: down
+     * {<OpenLayers.Pixel>} The pixel location of the last mousedown.
+     */
+    down: null,
+    
+    /**
+     * Property: rightclickTimerId
+     * {Number} The id of the right mouse timeout waiting to clear the 
+     *     <delayedEvent>.
+     */
+    rightclickTimerId: null,
+    
+    /**
+     * Constructor: OpenLayers.Handler.Click
+     * Create a new click handler.
+     * 
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that is making use of
+     *     this handler.  If a handler is being used without a control, the
+     *     handler's setMap method must be overridden to deal properly with
+     *     the map.
+     * callbacks - {Object} An object with keys corresponding to callbacks
+     *     that will be called by the handler. The callbacks should
+     *     expect to recieve a single argument, the click event.
+     *     Callbacks for 'click' and 'dblclick' are supported.
+     * options - {Object} Optional object whose properties will be set on the
+     *     handler.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+        // optionally register for mouseup and mousedown
+        if(this.pixelTolerance != null) {
+            this.mousedown = function(evt) {
+                this.down = evt.xy;
+                return true;
+            };
+        }
+    },
+    
+    /**
+     * Method: mousedown
+     * Handle mousedown.  Only registered as a listener if pixelTolerance is
+     *     a non-zero value at construction.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mousedown: null,
+
+    /**
+     * Method: mouseup
+     * Handle mouseup.  Installed to support collection of right mouse events.
+     * 
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mouseup:  function (evt) {
+        var propagate = true;
+
+        // Collect right mouse clicks from the mouseup
+        //  IE - ignores the second right click in mousedown so using
+        //  mouseup instead
+        if (this.checkModifiers(evt) && 
+            this.control.handleRightClicks && 
+            OpenLayers.Event.isRightClick(evt)) {
+          propagate = this.rightclick(evt);
+        }
+
+        return propagate;
+    },
+    
+    /**
+     * Method: rightclick
+     * Handle rightclick.  For a dblrightclick, we get two clicks so we need 
+     *     to always register for dblrightclick to properly handle single 
+     *     clicks.
+     *     
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    rightclick: function(evt) {
+        if(this.passesTolerance(evt)) {
+           if(this.rightclickTimerId != null) {
+                //Second click received before timeout this must be 
+                // a double click
+                this.clearTimer();      
+                this.callback('dblrightclick', [evt]);
+                return !this.stopDouble;
+            } else { 
+                //Set the rightclickTimerId, send evt only if double is 
+                // true else trigger single
+                var clickEvent = this['double'] ?
+                    OpenLayers.Util.extend({}, evt) : 
+                    this.callback('rightclick', [evt]);
+
+                var delayedRightCall = OpenLayers.Function.bind(
+                    this.delayedRightCall, 
+                    this, 
+                    clickEvent
+                );
+                this.rightclickTimerId = window.setTimeout(
+                    delayedRightCall, this.delay
+                );
+            } 
+        }
+        return !this.stopSingle;
+    },
+    
+    /**
+     * Method: delayedRightCall
+     * Sets <rightclickTimerId> to null.  And optionally triggers the 
+     *     rightclick callback if evt is set.
+     */
+    delayedRightCall: function(evt) {
+        this.rightclickTimerId = null;
+        if (evt) {
+           this.callback('rightclick', [evt]);
+        }
+        return !this.stopSingle;
+    },
+    
+    /**
+     * Method: dblclick
+     * Handle dblclick.  For a dblclick, we get two clicks in some browsers
+     *     (FF) and one in others (IE).  So we need to always register for
+     *     dblclick to properly handle single clicks.
+     *     
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    dblclick: function(evt) {
+        if(this.passesTolerance(evt)) {
+            if(this["double"]) {
+                this.callback('dblclick', [evt]);
+            }
+            this.clearTimer();
+        }
+        return !this.stopDouble;
+    },
+    
+    /**
+     * Method: click
+     * Handle click.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    click: function(evt) {
+        if(this.passesTolerance(evt)) {
+            if(this.timerId != null) {
+                // already received a click
+                this.clearTimer();
+            } else {
+                // set the timer, send evt only if single is true
+                //use a clone of the event object because it will no longer 
+                //be a valid event object in IE in the timer callback
+                var clickEvent = this.single ?
+                    OpenLayers.Util.extend({}, evt) : null;
+                this.timerId = window.setTimeout(
+                    OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
+                    this.delay
+                );
+            }
+        }
+        return !this.stopSingle;
+    },
+    
+    /**
+     * Method: passesTolerance
+     * Determine whether the event is within the optional pixel tolerance.  Note
+     *     that the pixel tolerance check only works if mousedown events get to
+     *     the listeners registered here.  If they are stopped by other elements,
+     *     the <pixelTolerance> will have no effect here (this method will always
+     *     return true).
+     *
+     * Returns:
+     * {Boolean} The click is within the pixel tolerance (if specified).
+     */
+    passesTolerance: function(evt) {
+        var passes = true;
+        if(this.pixelTolerance != null && this.down) {
+            var dpx = Math.sqrt(
+                Math.pow(this.down.x - evt.xy.x, 2) +
+                Math.pow(this.down.y - evt.xy.y, 2)
+            );
+            if(dpx > this.pixelTolerance) {
+                passes = false;
+            }
+        }
+        return passes;
+    },
+
+    /**
+     * Method: clearTimer
+     * Clear the timer and set <timerId> to null.
+     */
+    clearTimer: function() {
+        if(this.timerId != null) {
+            window.clearTimeout(this.timerId);
+            this.timerId = null;
+        }
+        if(this.rightclickTimerId != null) {
+            window.clearTimeout(this.rightclickTimerId);
+            this.rightclickTimerId = null;
+        }
+    },
+    
+    /**
+     * Method: delayedCall
+     * Sets <timerId> to null.  And optionally triggers the click callback if
+     *     evt is set.
+     */
+    delayedCall: function(evt) {
+        this.timerId = null;
+        if(evt) {
+            this.callback('click', [evt]);
+        }
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Deactivate the handler.
+     *
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.clearTimer();
+            this.down = null;
+            deactivated = true;
+        }
+        return deactivated;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Click"
+});
+/* ======================================================================
+    OpenLayers/Control/Navigation.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control/ZoomBox.js
+ * @requires OpenLayers/Control/DragPan.js
+ * @requires OpenLayers/Handler/MouseWheel.js
+ * @requires OpenLayers/Handler/Click.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Navigation
+ * The navigation control handles map browsing with mouse events (dragging,
+ *     double-clicking, and scrolling the wheel).  Create a new navigation 
+ *     control with the <OpenLayers.Control.Navigation> control.  
+ * 
+ *     Note that this control is added to the map by default (if no controls 
+ *     array is sent in the options object to the <OpenLayers.Map> 
+ *     constructor).
+ * 
+ * Inherits:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
+
+    /** 
+     * Property: dragPan
+     * {<OpenLayers.Control.DragPan>} 
+     */
+    dragPan: null,
+
+    /**
+     * APIProprety: dragPanOptions
+     * {Object} Options passed to the DragPan control.
+     */
+    dragPanOptions: null,
+
+    /**
+     * APIProperty: documentDrag
+     * {Boolean} Allow panning of the map by dragging outside map viewport.
+     *     Default is false.
+     */
+    documentDrag: false,
+
+    /** 
+     * Property: zoomBox
+     * {<OpenLayers.Control.ZoomBox>}
+     */
+    zoomBox: null,
+
+    /**
+     * APIProperty: zoomBoxEnabled
+     * {Boolean} Whether the user can draw a box to zoom
+     */
+    zoomBoxEnabled: true, 
+
+    /**
+     * APIProperty: zoomWheelEnabled
+     * {Boolean} Whether the mousewheel should zoom the map
+     */
+    zoomWheelEnabled: true,
+    
+    /**
+     * Property: mouseWheelOptions
+     * {Object} Options passed to the MouseWheel control (only useful if
+     *     <zoomWheelEnabled> is set to true)
+     */
+    mouseWheelOptions: null,
+
+    /**
+     * APIProperty: handleRightClicks
+     * {Boolean} Whether or not to handle right clicks. Default is false.
+     */
+    handleRightClicks: false,
+
+    /**
+     * APIProperty: zoomBoxKeyMask
+     * {Integer} <OpenLayers.Handler> key code of the key, which has to be
+     *    pressed, while drawing the zoom box with the mouse on the screen. 
+     *    You should probably set handleRightClicks to true if you use this
+     *    with MOD_CTRL, to disable the context menu for machines which use
+     *    CTRL-Click as a right click.
+     * Default: <OpenLayers.Handler.MOD_SHIFT
+     */
+    zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
+    
+    /**
+     * APIProperty: autoActivate
+     * {Boolean} Activate the control when it is added to a map.  Default is
+     *     true.
+     */
+    autoActivate: true,
+
+    /**
+     * Constructor: OpenLayers.Control.Navigation
+     * Create a new navigation control
+     * 
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *                    the control
+     */
+    initialize: function(options) {
+        this.handlers = {};
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: destroy
+     * The destroy method is used to perform any clean up before the control
+     * is dereferenced.  Typically this is where event listeners are removed
+     * to prevent memory leaks.
+     */
+    destroy: function() {
+        this.deactivate();
+
+        if (this.dragPan) {
+            this.dragPan.destroy();
+        }
+        this.dragPan = null;
+
+        if (this.zoomBox) {
+            this.zoomBox.destroy();
+        }
+        this.zoomBox = null;
+        OpenLayers.Control.prototype.destroy.apply(this,arguments);
+    },
+    
+    /**
+     * Method: activate
+     */
+    activate: function() {
+        this.dragPan.activate();
+        if (this.zoomWheelEnabled) {
+            this.handlers.wheel.activate();
+        }    
+        this.handlers.click.activate();
+        if (this.zoomBoxEnabled) {
+            this.zoomBox.activate();
+        }
+        return OpenLayers.Control.prototype.activate.apply(this,arguments);
+    },
+
+    /**
+     * Method: deactivate
+     */
+    deactivate: function() {
+        this.zoomBox.deactivate();
+        this.dragPan.deactivate();
+        this.handlers.click.deactivate();
+        this.handlers.wheel.deactivate();
+        return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
+    },
+    
+    /**
+     * Method: draw
+     */
+    draw: function() {
+        // disable right mouse context menu for support of right click events
+        if (this.handleRightClicks) {
+            this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
+        }
+
+        var clickCallbacks = { 
+            'dblclick': this.defaultDblClick, 
+            'dblrightclick': this.defaultDblRightClick 
+        };
+        var clickOptions = {
+            'double': true, 
+            'stopDouble': true
+        };
+        this.handlers.click = new OpenLayers.Handler.Click(
+            this, clickCallbacks, clickOptions
+        );
+        this.dragPan = new OpenLayers.Control.DragPan(
+            OpenLayers.Util.extend({
+                map: this.map,
+                documentDrag: this.documentDrag
+            }, this.dragPanOptions)
+        );
+        this.zoomBox = new OpenLayers.Control.ZoomBox(
+                    {map: this.map, keyMask: this.zoomBoxKeyMask});
+        this.dragPan.draw();
+        this.zoomBox.draw();
+        this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
+                                    this, {"up"  : this.wheelUp,
+                                           "down": this.wheelDown},
+                                    this.mouseWheelOptions );
+    },
+
+    /**
+     * Method: defaultDblClick 
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    defaultDblClick: function (evt) {
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom + 1);
+    },
+
+    /**
+     * Method: defaultDblRightClick 
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    defaultDblRightClick: function (evt) {
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom - 1);
+    },
+    
+    /**
+     * Method: wheelChange  
+     *
+     * Parameters:
+     * evt - {Event}
+     * deltaZ - {Integer}
+     */
+    wheelChange: function(evt, deltaZ) {
+        var currentZoom = this.map.getZoom();
+        var newZoom = this.map.getZoom() + Math.round(deltaZ);
+        newZoom = Math.max(newZoom, 0);
+        newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
+        if (newZoom === currentZoom) {
+            return;
+        }
+        var size    = this.map.getSize();
+        var deltaX  = size.w/2 - evt.xy.x;
+        var deltaY  = evt.xy.y - size.h/2;
+        var newRes  = this.map.baseLayer.getResolutionForZoom(newZoom);
+        var zoomPoint = this.map.getLonLatFromPixel(evt.xy);
+        var newCenter = new OpenLayers.LonLat(
+                            zoomPoint.lon + deltaX * newRes,
+                            zoomPoint.lat + deltaY * newRes );
+        this.map.setCenter( newCenter, newZoom );
+    },
+
+    /** 
+     * Method: wheelUp
+     * User spun scroll wheel up
+     * 
+     * Parameters:
+     * evt - {Event}
+     * delta - {Integer}
+     */
+    wheelUp: function(evt, delta) {
+        this.wheelChange(evt, delta || 1);
+    },
+
+    /** 
+     * Method: wheelDown
+     * User spun scroll wheel down
+     * 
+     * Parameters:
+     * evt - {Event}
+     * delta - {Integer}
+     */
+    wheelDown: function(evt, delta) {
+        this.wheelChange(evt, delta || -1);
+    },
+    
+    /**
+     * Method: disableZoomBox
+     */
+    disableZoomBox : function() {
+        this.zoomBoxEnabled = false;
+        this.zoomBox.deactivate();       
+    },
+    
+    /**
+     * Method: enableZoomBox
+     */
+    enableZoomBox : function() {
+        this.zoomBoxEnabled = true;
+        if (this.active) {
+            this.zoomBox.activate();
+        }    
+    },
+    
+    /**
+     * Method: disableZoomWheel
+     */
+    
+    disableZoomWheel : function() {
+        this.zoomWheelEnabled = false;
+        this.handlers.wheel.deactivate();       
+    },
+    
+    /**
+     * Method: enableZoomWheel
+     */
+    
+    enableZoomWheel : function() {
+        this.zoomWheelEnabled = true;
+        if (this.active) {
+            this.handlers.wheel.activate();
+        }    
+    },
+
+    CLASS_NAME: "OpenLayers.Control.Navigation"
+});
+/* ======================================================================
+    OpenLayers/Layer/HTTPRequest.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.HTTPRequest
+ * 
+ * Inherits from: 
+ *  - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
+
+    /** 
+     * Constant: URL_HASH_FACTOR
+     * {Float} Used to hash URL param strings for multi-WMS server selection.
+     *         Set to the Golden Ratio per Knuth's recommendation.
+     */
+    URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
+
+    /** 
+     * Property: url
+     * {Array(String) or String} This is either an array of url strings or 
+     *                           a single url string. 
+     */
+    url: null,
+
+    /** 
+     * Property: params
+     * {Object} Hashtable of key/value parameters
+     */
+    params: null,
+    
+    /** 
+     * APIProperty: reproject
+     * *Deprecated*. See http://trac.openlayers.org/wiki/SpatialMercator
+     * for information on the replacement for this functionality. 
+     * {Boolean} Whether layer should reproject itself based on base layer 
+     *           locations. This allows reprojection onto commercial layers. 
+     *           Default is false: Most layers can't reproject, but layers 
+     *           which can create non-square geographic pixels can, like WMS.
+     *           
+     */
+    reproject: false,
+
+    /**
+     * Constructor: OpenLayers.Layer.HTTPRequest
+     * 
+     * Parameters:
+     * name - {String}
+     * url - {Array(String) or String}
+     * params - {Object}
+     * options - {Object} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, params, options) {
+        var newArguments = arguments;
+        newArguments = [name, options];
+        OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
+        this.url = url;
+        this.params = OpenLayers.Util.extend( {}, params);
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        this.url = null;
+        this.params = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
+    },
+    
+    /**
+     * APIMethod: clone
+     * 
+     * Parameters:
+     * obj - {Object}
+     * 
+     * Returns:
+     * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this 
+     *                                  <OpenLayers.Layer.HTTPRequest>
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.HTTPRequest(this.name,
+                                                   this.url,
+                                                   this.params,
+                                                   this.getOptions());
+        }
+        
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+        
+        return obj;
+    },
+
+    /** 
+     * APIMethod: setUrl
+     * 
+     * Parameters:
+     * newUrl - {String}
+     */
+    setUrl: function(newUrl) {
+        this.url = newUrl;
+    },
+
+    /**
+     * APIMethod: mergeNewParams
+     * 
+     * Parameters:
+     * newParams - {Object}
+     *
+     * Returns:
+     * redrawn: {Boolean} whether the layer was actually redrawn.
+     */
+    mergeNewParams:function(newParams) {
+        this.params = OpenLayers.Util.extend(this.params, newParams);
+        var ret = this.redraw();
+        if(this.map != null) {
+            this.map.events.triggerEvent("changelayer", {
+                layer: this,
+                property: "params"
+            });
+        }
+        return ret;
+    },
+
+    /**
+     * APIMethod: redraw
+     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
+     *
+     * Parameters:
+     * force - {Boolean} Force redraw by adding random parameter.
+     *
+     * Returns:
+     * {Boolean} The layer was redrawn.
+     */
+    redraw: function(force) { 
+        if (force) {
+            return this.mergeNewParams({"_olSalt": Math.random()});
+        } else {
+            return OpenLayers.Layer.prototype.redraw.apply(this, []);
+        }
+    },
+    
+    /**
+     * Method: selectUrl
+     * selectUrl() implements the standard floating-point multiplicative
+     *     hash function described by Knuth, and hashes the contents of the 
+     *     given param string into a float between 0 and 1. This float is then
+     *     scaled to the size of the provided urls array, and used to select
+     *     a URL.
+     *
+     * Parameters:
+     * paramString - {String}
+     * urls - {Array(String)}
+     * 
+     * Returns:
+     * {String} An entry from the urls array, deterministically selected based
+     *          on the paramString.
+     */
+    selectUrl: function(paramString, urls) {
+        var product = 1;
+        for (var i=0, len=paramString.length; i<len; i++) { 
+            product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; 
+            product -= Math.floor(product); 
+        }
+        return urls[Math.floor(product * urls.length)];
+    },
+
+    /** 
+     * Method: getFullRequestString
+     * Combine url with layer's params and these newParams. 
+     *   
+     *    does checking on the serverPath variable, allowing for cases when it 
+     *     is supplied with trailing ? or &, as well as cases where not. 
+     *
+     *    return in formatted string like this:
+     *        "server?key1=value1&key2=value2&key3=value3"
+     * 
+     * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
+     *
+     * Parameters:
+     * newParams - {Object}
+     * altUrl - {String} Use this as the url instead of the layer's url
+     *   
+     * Returns: 
+     * {String}
+     */
+    getFullRequestString:function(newParams, altUrl) {
+
+        // if not altUrl passed in, use layer's url
+        var url = altUrl || this.url;
+        
+        // create a new params hashtable with all the layer params and the 
+        // new params together. then convert to string
+        var allParams = OpenLayers.Util.extend({}, this.params);
+        allParams = OpenLayers.Util.extend(allParams, newParams);
+        var paramsString = OpenLayers.Util.getParameterString(allParams);
+        
+        // if url is not a string, it should be an array of strings, 
+        // in which case we will deterministically select one of them in 
+        // order to evenly distribute requests to different urls.
+        //
+        if (url instanceof Array) {
+            url = this.selectUrl(paramsString, url);
+        }   
+ 
+        // ignore parameters that are already in the url search string
+        var urlParams = 
+            OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
+        for(var key in allParams) {
+            if(key.toUpperCase() in urlParams) {
+                delete allParams[key];
+            }
+        }
+        paramsString = OpenLayers.Util.getParameterString(allParams);
+        
+        return OpenLayers.Util.urlAppend(url, paramsString);
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
+});
+/* ======================================================================
+    OpenLayers/Layer/Grid.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/HTTPRequest.js
+ * @requires OpenLayers/Console.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Grid
+ * Base class for layers that use a lattice of tiles.  Create a new grid
+ * layer with the <OpenLayers.Layer.Grid> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Layer.HTTPRequest>
+ */
+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
+    
+    /**
+     * APIProperty: tileSize
+     * {<OpenLayers.Size>}
+     */
+    tileSize: null,
+
+    /**
+     * Property: tileOriginCorner
+     * {String} If the <tileOrigin> property is not provided, the tile origin 
+     *     will be derived from the layer's <maxExtent>.  The corner of the 
+     *     <maxExtent> used is determined by this property.  Acceptable values
+     *     are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
+     *     (bottom right).  Default is "bl".
+     */
+    tileOriginCorner: "bl",
+    
+    /**
+     * APIProperty: tileOrigin
+     * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
+     *     If provided, requests for tiles at all resolutions will be aligned
+     *     with this location (no tiles shall overlap this location).  If
+     *     not provided, the grid of tiles will be aligned with the layer's
+     *     <maxExtent>.  Default is ``null``.
+     */
+    tileOrigin: null,
+    
+    /** APIProperty: tileOptions
+     *  {Object} optional configuration options for <OpenLayers.Tile> instances
+     *  created by this Layer, if supported by the tile class.
+     */
+    tileOptions: null,
+    
+    /**
+     * Property: grid
+     * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is 
+     *     an array of tiles.
+     */
+    grid: null,
+
+    /**
+     * APIProperty: singleTile
+     * {Boolean} Moves the layer into single-tile mode, meaning that one tile 
+     *     will be loaded. The tile's size will be determined by the 'ratio'
+     *     property. When the tile is dragged such that it does not cover the 
+     *     entire viewport, it is reloaded.
+     */
+    singleTile: false,
+
+    /** APIProperty: ratio
+     *  {Float} Used only when in single-tile mode, this specifies the 
+     *          ratio of the size of the single tile to the size of the map.
+     */
+    ratio: 1.5,
+
+    /**
+     * APIProperty: buffer
+     * {Integer} Used only when in gridded mode, this specifies the number of 
+     *           extra rows and colums of tiles on each side which will
+     *           surround the minimum grid tiles to cover the map.
+     */
+    buffer: 2,
+
+    /**
+     * APIProperty: numLoadingTiles
+     * {Integer} How many tiles are still loading?
+     */
+    numLoadingTiles: 0,
+
+    /**
+     * Constructor: OpenLayers.Layer.Grid
+     * Create a new grid layer
+     *
+     * Parameters:
+     * name - {String}
+     * url - {String}
+     * params - {Object}
+     * options - {Object} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, params, options) {
+        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
+                                                                arguments);
+        
+        //grid layers will trigger 'tileloaded' when each new tile is 
+        // loaded, as a means of progress update to listeners.
+        // listeners can access 'numLoadingTiles' if they wish to keep track
+        // of the loading progress
+        //
+        this.events.addEventType("tileloaded");
+
+        this.grid = [];
+    },
+
+    /**
+     * APIMethod: destroy
+     * Deconstruct the layer and clear the grid.
+     */
+    destroy: function() {
+        this.clearGrid();
+        this.grid = null;
+        this.tileSize = null;
+        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); 
+    },
+
+    /**
+     * Method: clearGrid
+     * Go through and remove all tiles from the grid, calling
+     *    destroy() on each of them to kill circular references
+     */
+    clearGrid:function() {
+        if (this.grid) {
+            for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
+                var row = this.grid[iRow];
+                for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
+                    var tile = row[iCol];
+                    this.removeTileMonitoringHooks(tile);
+                    tile.destroy();
+                }
+            }
+            this.grid = [];
+        }
+    },
+
+    /**
+     * APIMethod: clone
+     * Create a clone of this layer
+     *
+     * Parameters:
+     * obj - {Object} Is this ever used?
+     * 
+     * Returns:
+     * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.Grid(this.name,
+                                            this.url,
+                                            this.params,
+                                            this.getOptions());
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+        if (this.tileSize != null) {
+            obj.tileSize = this.tileSize.clone();
+        }
+        
+        // we do not want to copy reference to grid, so we make a new array
+        obj.grid = [];
+
+        return obj;
+    },    
+
+    /**
+     * Method: moveTo
+     * This function is called whenever the map is moved. All the moving
+     * of actual 'tiles' is done by the map, but moveTo's role is to accept
+     * a bounds and make sure the data that that bounds requires is pre-loaded.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * zoomChanged - {Boolean}
+     * dragging - {Boolean}
+     */
+    moveTo:function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
+        
+        bounds = bounds || this.map.getExtent();
+
+        if (bounds != null) {
+             
+            // if grid is empty or zoom has changed, we *must* re-tile
+            var forceReTile = !this.grid.length || zoomChanged;
+
+            // total bounds of the tiles
+            var tilesBounds = this.getTilesBounds();            
+      
+            if (this.singleTile) {
+                
+                // We want to redraw whenever even the slightest part of the 
+                //  current bounds is not contained by our tile.
+                //  (thus, we do not specify partial -- its default is false)
+                if ( forceReTile || 
+                     (!dragging && !tilesBounds.containsBounds(bounds))) {
+                    this.initSingleTile(bounds);
+                }
+            } else {
+             
+                // if the bounds have changed such that they are not even 
+                //  *partially* contained by our tiles (IE user has 
+                //  programmatically panned to the other side of the earth) 
+                //  then we want to reTile (thus, partial true).  
+                //
+                if (forceReTile || !tilesBounds.containsBounds(bounds, true)) {
+                    this.initGriddedTiles(bounds);
+                } else {
+                    //we might have to shift our buffer tiles
+                    this.moveGriddedTiles(bounds);
+                }
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: setTileSize
+     * Check if we are in singleTile mode and if so, set the size as a ratio
+     *     of the map size (as specified by the layer's 'ratio' property).
+     * 
+     * Parameters:
+     * size - {<OpenLayers.Size>}
+     */
+    setTileSize: function(size) { 
+        if (this.singleTile) {
+            size = this.map.getSize();
+            size.h = parseInt(size.h * this.ratio);
+            size.w = parseInt(size.w * this.ratio);
+        } 
+        OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
+    },
+        
+    /**
+     * Method: getGridBounds
+     * Deprecated. This function will be removed in 3.0. Please use 
+     *     getTilesBounds() instead.
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
+     * currently loaded tiles (including those partially or not at all seen 
+     * onscreen)
+     */
+    getGridBounds: function() {
+        var msg = "The getGridBounds() function is deprecated. It will be " +
+                  "removed in 3.0. Please use getTilesBounds() instead.";
+        OpenLayers.Console.warn(msg);
+        return this.getTilesBounds();
+    },
+
+    /**
+     * APIMethod: getTilesBounds
+     * Return the bounds of the tile grid.
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
+     *     currently loaded tiles (including those partially or not at all seen 
+     *     onscreen).
+     */
+    getTilesBounds: function() {    
+        var bounds = null; 
+        
+        if (this.grid.length) {
+            var bottom = this.grid.length - 1;
+            var bottomLeftTile = this.grid[bottom][0];
+    
+            var right = this.grid[0].length - 1; 
+            var topRightTile = this.grid[0][right];
+    
+            bounds = new OpenLayers.Bounds(bottomLeftTile.bounds.left, 
+                                           bottomLeftTile.bounds.bottom,
+                                           topRightTile.bounds.right, 
+                                           topRightTile.bounds.top);
+            
+        }   
+        return bounds;
+    },
+
+    /**
+     * Method: initSingleTile
+     * 
+     * Parameters: 
+     * bounds - {<OpenLayers.Bounds>}
+     */
+    initSingleTile: function(bounds) {
+
+        //determine new tile bounds
+        var center = bounds.getCenterLonLat();
+        var tileWidth = bounds.getWidth() * this.ratio;
+        var tileHeight = bounds.getHeight() * this.ratio;
+                                       
+        var tileBounds = 
+            new OpenLayers.Bounds(center.lon - (tileWidth/2),
+                                  center.lat - (tileHeight/2),
+                                  center.lon + (tileWidth/2),
+                                  center.lat + (tileHeight/2));
+  
+        var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
+        var px = this.map.getLayerPxFromLonLat(ul);
+
+        if (!this.grid.length) {
+            this.grid[0] = [];
+        }
+
+        var tile = this.grid[0][0];
+        if (!tile) {
+            tile = this.addTile(tileBounds, px);
+            
+            this.addTileMonitoringHooks(tile);
+            tile.draw();
+            this.grid[0][0] = tile;
+        } else {
+            tile.moveTo(tileBounds, px);
+        }           
+        
+        //remove all but our single tile
+        this.removeExcessTiles(1,1);
+    },
+
+    /** 
+     * Method: calculateGridLayout
+     * Generate parameters for the grid layout.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * origin - {<OpenLayers.LonLat>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, origin, resolution) {
+        var tilelon = resolution * this.tileSize.w;
+        var tilelat = resolution * this.tileSize.h;
+        
+        var offsetlon = bounds.left - origin.lon;
+        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
+        var tilecolremain = offsetlon/tilelon - tilecol;
+        var tileoffsetx = -tilecolremain * this.tileSize.w;
+        var tileoffsetlon = origin.lon + tilecol * tilelon;
+        
+        var offsetlat = bounds.top - (origin.lat + tilelat);  
+        var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer;
+        var tilerowremain = tilerow - offsetlat/tilelat;
+        var tileoffsety = -tilerowremain * this.tileSize.h;
+        var tileoffsetlat = origin.lat + tilerow * tilelat;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+
+    },
+    
+    /**
+     * Method: getTileOrigin
+     * Determine the origin for aligning the grid of tiles.  If a <tileOrigin>
+     *     property is supplied, that will be returned.  Otherwise, the origin
+     *     will be derived from the layer's <maxExtent> property.  In this case,
+     *     the tile origin will be the corner of the <maxExtent> given by the 
+     *     <tileOriginCorner> property.
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} The tile origin.
+     */
+    getTileOrigin: function() {
+        var origin = this.tileOrigin;
+        if (!origin) {
+            var extent = this.getMaxExtent();
+            var edges = ({
+                "tl": ["left", "top"],
+                "tr": ["right", "top"],
+                "bl": ["left", "bottom"],
+                "br": ["right", "bottom"]
+            })[this.tileOriginCorner];
+            origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
+        }
+        return origin;
+    },
+
+    /**
+     * Method: initGriddedTiles
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     */
+    initGriddedTiles:function(bounds) {
+        
+        // work out mininum number of rows and columns; this is the number of
+        // tiles required to cover the viewport plus at least one for panning
+
+        var viewSize = this.map.getSize();
+        var minRows = Math.ceil(viewSize.h/this.tileSize.h) + 
+                      Math.max(1, 2 * this.buffer);
+        var minCols = Math.ceil(viewSize.w/this.tileSize.w) +
+                      Math.max(1, 2 * this.buffer);
+        
+        var origin = this.getTileOrigin();
+        var resolution = this.map.getResolution();
+        
+        var tileLayout = this.calculateGridLayout(bounds, origin, resolution);
+
+        var tileoffsetx = Math.round(tileLayout.tileoffsetx); // heaven help us
+        var tileoffsety = Math.round(tileLayout.tileoffsety);
+
+        var tileoffsetlon = tileLayout.tileoffsetlon;
+        var tileoffsetlat = tileLayout.tileoffsetlat;
+        
+        var tilelon = tileLayout.tilelon;
+        var tilelat = tileLayout.tilelat;
+
+        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
+
+        var startX = tileoffsetx; 
+        var startLon = tileoffsetlon;
+
+        var rowidx = 0;
+        
+        var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
+        var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
+        
+    
+        do {
+            var row = this.grid[rowidx++];
+            if (!row) {
+                row = [];
+                this.grid.push(row);
+            }
+
+            tileoffsetlon = startLon;
+            tileoffsetx = startX;
+            var colidx = 0;
+ 
+            do {
+                var tileBounds = 
+                    new OpenLayers.Bounds(tileoffsetlon, 
+                                          tileoffsetlat, 
+                                          tileoffsetlon + tilelon,
+                                          tileoffsetlat + tilelat);
+
+                var x = tileoffsetx;
+                x -= layerContainerDivLeft;
+
+                var y = tileoffsety;
+                y -= layerContainerDivTop;
+
+                var px = new OpenLayers.Pixel(x, y);
+                var tile = row[colidx++];
+                if (!tile) {
+                    tile = this.addTile(tileBounds, px);
+                    this.addTileMonitoringHooks(tile);
+                    row.push(tile);
+                } else {
+                    tile.moveTo(tileBounds, px, false);
+                }
+     
+                tileoffsetlon += tilelon;       
+                tileoffsetx += this.tileSize.w;
+            } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer)
+                     || colidx < minCols);
+             
+            tileoffsetlat -= tilelat;
+            tileoffsety += this.tileSize.h;
+        } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer)
+                || rowidx < minRows);
+        
+        //shave off exceess rows and colums
+        this.removeExcessTiles(rowidx, colidx);
+
+        //now actually draw the tiles
+        this.spiralTileLoad();
+    },
+
+    /**
+     * Method: getMaxExtent
+     * Get this layer's maximum extent. (Implemented as a getter for
+     *     potential specific implementations in sub-classes.)
+     *
+     * Returns:
+     * {OpenLayers.Bounds}
+     */
+    getMaxExtent: function() {
+        return this.maxExtent;
+    },
+    
+    /**
+     * Method: spiralTileLoad
+     *   Starts at the top right corner of the grid and proceeds in a spiral 
+     *    towards the center, adding tiles one at a time to the beginning of a 
+     *    queue. 
+     * 
+     *   Once all the grid's tiles have been added to the queue, we go back 
+     *    and iterate through the queue (thus reversing the spiral order from 
+     *    outside-in to inside-out), calling draw() on each tile. 
+     */
+    spiralTileLoad: function() {
+        var tileQueue = [];
+ 
+        var directions = ["right", "down", "left", "up"];
+
+        var iRow = 0;
+        var iCell = -1;
+        var direction = OpenLayers.Util.indexOf(directions, "right");
+        var directionsTried = 0;
+        
+        while( directionsTried < directions.length) {
+
+            var testRow = iRow;
+            var testCell = iCell;
+
+            switch (directions[direction]) {
+                case "right":
+                    testCell++;
+                    break;
+                case "down":
+                    testRow++;
+                    break;
+                case "left":
+                    testCell--;
+                    break;
+                case "up":
+                    testRow--;
+                    break;
+            } 
+    
+            // if the test grid coordinates are within the bounds of the 
+            //  grid, get a reference to the tile.
+            var tile = null;
+            if ((testRow < this.grid.length) && (testRow >= 0) &&
+                (testCell < this.grid[0].length) && (testCell >= 0)) {
+                tile = this.grid[testRow][testCell];
+            }
+            
+            if ((tile != null) && (!tile.queued)) {
+                //add tile to beginning of queue, mark it as queued.
+                tileQueue.unshift(tile);
+                tile.queued = true;
+                
+                //restart the directions counter and take on the new coords
+                directionsTried = 0;
+                iRow = testRow;
+                iCell = testCell;
+            } else {
+                //need to try to load a tile in a different direction
+                direction = (direction + 1) % 4;
+                directionsTried++;
+            }
+        } 
+        
+        // now we go through and draw the tiles in forward order
+        for(var i=0, len=tileQueue.length; i<len; i++) {
+            var tile = tileQueue[i];
+            tile.draw();
+            //mark tile as unqueued for the next time (since tiles are reused)
+            tile.queued = false;       
+        }
+    },
+
+    /**
+     * APIMethod: addTile
+     * Gives subclasses of Grid the opportunity to create an 
+     * OpenLayer.Tile of their choosing. The implementer should initialize 
+     * the new tile and take whatever steps necessary to display it.
+     *
+     * Parameters
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {<OpenLayers.Tile>} The added OpenLayers.Tile
+     */
+    addTile:function(bounds, position) {
+        // Should be implemented by subclasses
+    },
+    
+    /** 
+     * Method: addTileMonitoringHooks
+     * This function takes a tile as input and adds the appropriate hooks to 
+     *     the tile so that the layer can keep track of the loading tiles.
+     * 
+     * Parameters: 
+     * tile - {<OpenLayers.Tile>}
+     */
+    addTileMonitoringHooks: function(tile) {
+        
+        tile.onLoadStart = function() {
+            //if that was first tile then trigger a 'loadstart' on the layer
+            if (this.numLoadingTiles == 0) {
+                this.events.triggerEvent("loadstart");
+            }
+            this.numLoadingTiles++;
+        };
+        tile.events.register("loadstart", this, tile.onLoadStart);
+      
+        tile.onLoadEnd = function() {
+            this.numLoadingTiles--;
+            this.events.triggerEvent("tileloaded");
+            //if that was the last tile, then trigger a 'loadend' on the layer
+            if (this.numLoadingTiles == 0) {
+                this.events.triggerEvent("loadend");
+            }
+        };
+        tile.events.register("loadend", this, tile.onLoadEnd);
+        tile.events.register("unload", this, tile.onLoadEnd);
+    },
+
+    /** 
+     * Method: removeTileMonitoringHooks
+     * This function takes a tile as input and removes the tile hooks 
+     *     that were added in addTileMonitoringHooks()
+     * 
+     * Parameters: 
+     * tile - {<OpenLayers.Tile>}
+     */
+    removeTileMonitoringHooks: function(tile) {
+        tile.unload();
+        tile.events.un({
+            "loadstart": tile.onLoadStart,
+            "loadend": tile.onLoadEnd,
+            "unload": tile.onLoadEnd,
+            scope: this
+        });
+    },
+    
+    /**
+     * Method: moveGriddedTiles
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     */
+    moveGriddedTiles: function(bounds) {
+        var buffer = this.buffer || 1;
+        while (true) {
+            var tlLayer = this.grid[0][0].position;
+            var tlViewPort = 
+                this.map.getViewPortPxFromLayerPx(tlLayer);
+            if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
+                this.shiftColumn(true);
+            } else if (tlViewPort.x < -this.tileSize.w * buffer) {
+                this.shiftColumn(false);
+            } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) {
+                this.shiftRow(true);
+            } else if (tlViewPort.y < -this.tileSize.h * buffer) {
+                this.shiftRow(false);
+            } else {
+                break;
+            }
+        };
+    },
+
+    /**
+     * Method: shiftRow
+     * Shifty grid work
+     *
+     * Parameters:
+     * prepend - {Boolean} if true, prepend to beginning.
+     *                          if false, then append to end
+     */
+    shiftRow:function(prepend) {
+        var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
+        var grid = this.grid;
+        var modelRow = grid[modelRowIndex];
+
+        var resolution = this.map.getResolution();
+        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
+        var deltaLat = resolution * -deltaY;
+
+        var row = (prepend) ? grid.pop() : grid.shift();
+
+        for (var i=0, len=modelRow.length; i<len; i++) {
+            var modelTile = modelRow[i];
+            var bounds = modelTile.bounds.clone();
+            var position = modelTile.position.clone();
+            bounds.bottom = bounds.bottom + deltaLat;
+            bounds.top = bounds.top + deltaLat;
+            position.y = position.y + deltaY;
+            row[i].moveTo(bounds, position);
+        }
+
+        if (prepend) {
+            grid.unshift(row);
+        } else {
+            grid.push(row);
+        }
+    },
+
+    /**
+     * Method: shiftColumn
+     * Shift grid work in the other dimension
+     *
+     * Parameters:
+     * prepend - {Boolean} if true, prepend to beginning.
+     *                          if false, then append to end
+     */
+    shiftColumn: function(prepend) {
+        var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
+        var resolution = this.map.getResolution();
+        var deltaLon = resolution * deltaX;
+
+        for (var i=0, len=this.grid.length; i<len; i++) {
+            var row = this.grid[i];
+            var modelTileIndex = (prepend) ? 0 : (row.length - 1);
+            var modelTile = row[modelTileIndex];
+            
+            var bounds = modelTile.bounds.clone();
+            var position = modelTile.position.clone();
+            bounds.left = bounds.left + deltaLon;
+            bounds.right = bounds.right + deltaLon;
+            position.x = position.x + deltaX;
+
+            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift();
+            tile.moveTo(bounds, position);
+            if (prepend) {
+                row.unshift(tile);
+            } else {
+                row.push(tile);
+            }
+        }
+    },
+    
+    /**
+     * Method: removeExcessTiles
+     * When the size of the map or the buffer changes, we may need to
+     *     remove some excess rows and columns.
+     * 
+     * Parameters:
+     * rows - {Integer} Maximum number of rows we want our grid to have.
+     * colums - {Integer} Maximum number of columns we want our grid to have.
+     */
+    removeExcessTiles: function(rows, columns) {
+        
+        // remove extra rows
+        while (this.grid.length > rows) {
+            var row = this.grid.pop();
+            for (var i=0, l=row.length; i<l; i++) {
+                var tile = row[i];
+                this.removeTileMonitoringHooks(tile);
+                tile.destroy();
+            }
+        }
+        
+        // remove extra columns
+        while (this.grid[0].length > columns) {
+            for (var i=0, l=this.grid.length; i<l; i++) {
+                var row = this.grid[i];
+                var tile = row.pop();
+                this.removeTileMonitoringHooks(tile);
+                tile.destroy();
+            }
+        }
+    },
+
+    /**
+     * Method: onMapResize
+     * For singleTile layers, this will set a new tile size according to the
+     * dimensions of the map pane.
+     */
+    onMapResize: function() {
+        if (this.singleTile) {
+            this.clearGrid();
+            this.setTileSize();
+        }
+    },
+    
+    /**
+     * APIMethod: getTileBounds
+     * Returns The tile bounds for a layer given a pixel location.
+     *
+     * Parameters:
+     * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
+     */
+    getTileBounds: function(viewPortPx) {
+        var maxExtent = this.maxExtent;
+        var resolution = this.getResolution();
+        var tileMapWidth = resolution * this.tileSize.w;
+        var tileMapHeight = resolution * this.tileSize.h;
+        var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
+        var tileLeft = maxExtent.left + (tileMapWidth *
+                                         Math.floor((mapPoint.lon -
+                                                     maxExtent.left) /
+                                                    tileMapWidth));
+        var tileBottom = maxExtent.bottom + (tileMapHeight *
+                                             Math.floor((mapPoint.lat -
+                                                         maxExtent.bottom) /
+                                                        tileMapHeight));
+        return new OpenLayers.Bounds(tileLeft, tileBottom,
+                                     tileLeft + tileMapWidth,
+                                     tileBottom + tileMapHeight);
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.Grid"
+});
+/* ======================================================================
+    OpenLayers/Layer/WMS.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ * @requires OpenLayers/Tile/Image.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.WMS
+ * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web
+ *     Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>
+ *     constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+    /**
+     * Constant: DEFAULT_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs 
+     */
+    DEFAULT_PARAMS: { service: "WMS",
+                      version: "1.1.1",
+                      request: "GetMap",
+                      styles: "",
+                      exceptions: "application/vnd.ogc.se_inimage",
+                      format: "image/jpeg"
+                     },
+    
+    /**
+     * Property: reproject
+     * *Deprecated*. See http://trac.openlayers.org/wiki/SphericalMercator
+     * for information on the replacement for this functionality. 
+     * {Boolean} Try to reproject this layer if its coordinate reference system
+     *           is different than that of the base layer.  Default is true.  
+     *           Set this in the layer options.  Should be set to false in 
+     *           most cases.
+     */
+    reproject: false,
+ 
+    /**
+     * APIProperty: isBaseLayer
+     * {Boolean} Default is true for WMS layer
+     */
+    isBaseLayer: true,
+    
+    /**
+     * APIProperty: encodeBBOX
+     * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', 
+     * but some services want it that way. Default false.
+     */
+    encodeBBOX: false,
+    
+    /** 
+     * 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,
+    
+    /**
+     * Property: yx
+     * {Object} Keys in this object are EPSG codes for which the axis order
+     *     is to be reversed (yx instead of xy, LatLon instead of LonLat), with
+     *     true as value. This is only relevant for WMS versions >= 1.3.0.
+     */
+    yx: {'EPSG:4326': true},
+    
+    /**
+     * Constructor: OpenLayers.Layer.WMS
+     * Create a new WMS layer object
+     *
+     * Example:
+     * (code)
+     * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic",
+     *                                    "http://wms.jpl.nasa.gov/wms.cgi", 
+     *                                    {layers: "modis,global_mosaic"});
+     * (end)
+     *
+     * Parameters:
+     * name - {String} A name for the layer
+     * url - {String} Base url for the WMS
+     *                (e.g. http://wms.jpl.nasa.gov/wms.cgi)
+     * params - {Object} An object with key/value pairs representing the
+     *                   GetMap query string parameters and parameter values.
+     * options - {Ojbect} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, params, options) {
+        var newArguments = [];
+        //uppercase params
+        params = OpenLayers.Util.upperCaseObject(params);
+        if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {
+            params.EXCEPTIONS = "INIMAGE";
+        } 
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+        OpenLayers.Util.applyDefaults(
+                       this.params, 
+                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
+                       );
+
+
+        //layer is transparent        
+        if (!this.noMagic && this.params.TRANSPARENT && 
+            this.params.TRANSPARENT.toString().toLowerCase() == "true") {
+            
+            // unless explicitly set in options, make layer an overlay
+            if ( (options == null) || (!options.isBaseLayer) ) {
+                this.isBaseLayer = false;
+            } 
+            
+            // jpegs can never be transparent, so intelligently switch the 
+            //  format, depending on teh browser's capabilities
+            if (this.params.FORMAT == "image/jpeg") {
+                this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif"
+                                                                 : "image/png";
+            }
+        }
+
+    },    
+
+    /**
+     * Method: destroy
+     * Destroy this layer
+     */
+    destroy: function() {
+        // for now, nothing special to do here. 
+        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);  
+    },
+
+    
+    /**
+     * Method: clone
+     * Create a clone of this layer
+     *
+     * Returns:
+     * {<OpenLayers.Layer.WMS>} An exact clone of this layer
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.WMS(this.name,
+                                           this.url,
+                                           this.params,
+                                           this.getOptions());
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },    
+    
+    /**
+     * APIMethod: reverseAxisOrder
+     * Returns true if the axis order is reversed for the WMS version and
+     * projection of the layer.
+     * 
+     * Returns:
+     * {Boolean} true if the axis order is reversed, false otherwise.
+     */
+    reverseAxisOrder: function() {
+        return (parseFloat(this.params.VERSION) >= 1.3 && 
+            !!this.yx[this.map.getProjectionObject().getCode()]);
+    },
+    
+    /**
+     * Method: getURL
+     * Return a GetMap query string for this layer
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the
+     *                                request.
+     *
+     * Returns:
+     * {String} A string with the layer's url and parameters and also the
+     *          passed-in bounds and appropriate tile size specified as 
+     *          parameters.
+     */
+    getURL: function (bounds) {
+        bounds = this.adjustBounds(bounds);
+        
+        var imageSize = this.getImageSize();
+        var newParams = {};
+        // WMS 1.3 introduced axis order
+        var reverseAxisOrder = this.reverseAxisOrder();
+        newParams.BBOX = this.encodeBBOX ?
+            bounds.toBBOX(null, reverseAxisOrder) :
+            bounds.toArray(reverseAxisOrder);
+        newParams.WIDTH = imageSize.w;
+        newParams.HEIGHT = imageSize.h;
+        var requestString = this.getFullRequestString(newParams);
+        return requestString;
+    },
+
+    /**
+     * Method: addTile
+     * addTile creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+     */
+    addTile:function(bounds,position) {
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                         null, this.tileSize, this.tileOptions);
+    },
+
+    /**
+     * APIMethod: mergeNewParams
+     * Catch changeParams and uppercase the new params to be merged in
+     *     before calling changeParams on the super class.
+     * 
+     *     Once params have been changed, the tiles will be reloaded with
+     *     the new parameters.
+     * 
+     * Parameters:
+     * newParams - {Object} Hashtable of new params to use
+     */
+    mergeNewParams:function(newParams) {
+        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
+        var newArguments = [upperParams];
+        return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, 
+                                                             newArguments);
+    },
+
+    /** 
+     * APIMethod: getFullRequestString
+     * Combine the layer's url with its params and these newParams. 
+     *   
+     *     Add the SRS parameter from projection -- this is probably
+     *     more eloquently done via a setProjection() method, but this 
+     *     works for now and always.
+     *
+     * Parameters:
+     * newParams - {Object}
+     * altUrl - {String} Use this as the url instead of the layer's url
+     * 
+     * Returns:
+     * {String} 
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        var mapProjection = this.map.getProjectionObject();
+        var projectionCode = this.projection.equals(mapProjection) ?
+            this.projection.getCode() :
+            mapProjection.getCode();
+        var value = (projectionCode == "none") ? null : projectionCode
+        if (parseFloat(this.params.VERSION) >= 1.3) {
+            this.params.CRS = value;
+        } else {
+            this.params.SRS = value;
+        }
+
+        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
+                                                    this, arguments);
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.WMS"
+});
+/* ======================================================================
+    OpenLayers/StyleMap.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Style.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+ 
+/**
+ * Class: OpenLayers.StyleMap
+ */
+OpenLayers.StyleMap = OpenLayers.Class({
+    
+    /**
+     * Property: styles
+     * Hash of {<OpenLayers.Style>}, keyed by names of well known
+     * rendering intents (e.g. "default", "temporary", "select", "delete").
+     */
+    styles: null,
+    
+    /**
+     * Property: extendDefault
+     * {Boolean} if true, every render intent will extend the symbolizers
+     * specified for the "default" intent at rendering time. Otherwise, every
+     * rendering intent will be treated as a completely independent style.
+     */
+    extendDefault: true,
+    
+    /**
+     * Constructor: OpenLayers.StyleMap
+     * 
+     * Parameters:
+     * style   - {Object} Optional. Either a style hash, or a style object, or
+     *           a hash of style objects (style hashes) keyed by rendering
+     *           intent. If just one style hash or style object is passed,
+     *           this will be used for all known render intents (default,
+     *           select, temporary)
+     * options - {Object} optional hash of additional options for this
+     *           instance
+     */
+    initialize: function (style, options) {
+        this.styles = {
+            "default": new OpenLayers.Style(
+                OpenLayers.Feature.Vector.style["default"]),
+            "select": new OpenLayers.Style(
+                OpenLayers.Feature.Vector.style["select"]),
+            "temporary": new OpenLayers.Style(
+                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
+        // into parts of stylemap.
+        if(style instanceof OpenLayers.Style) {
+            // user passed a style object
+            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) {
+                    // user passed a hash of style objects
+                    this.styles[key] = style[key];
+                } else if(typeof style[key] == "object") {
+                    // user passsed a hash of style hashes
+                    this.styles[key] = new OpenLayers.Style(style[key]);
+                } else {
+                    // user passed a style hash (i.e. symbolizer)
+                    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;
+                }
+            }
+        }
+        OpenLayers.Util.extend(this, options);
+    },
+
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+        for(var key in this.styles) {
+            this.styles[key].destroy();
+        }
+        this.styles = null;
+    },
+    
+    /**
+     * Method: createSymbolizer
+     * Creates the symbolizer for a feature for a render intent.
+     * 
+     * Parameters:
+     * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
+     *           of the intended style against.
+     * intent  - {String} The intent determines the symbolizer that will be
+     *           used to draw the feature. Well known intents are "default"
+     *           (for just drawing the features), "select" (for selected
+     *           features) and "temporary" (for drawing features).
+     * 
+     * Returns:
+     * {Object} symbolizer hash
+     */
+    createSymbolizer: function(feature, intent) {
+        if(!feature) {
+            feature = new OpenLayers.Feature.Vector();
+        }
+        if(!this.styles[intent]) {
+            intent = "default";
+        }
+        feature.renderIntent = intent;
+        var defaultSymbolizer = {};
+        if(this.extendDefault && intent != "default") {
+            defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
+        }
+        return OpenLayers.Util.extend(defaultSymbolizer,
+            this.styles[intent].createSymbolizer(feature));
+    },
+    
+    /**
+     * Method: addUniqueValueRules
+     * Convenience method to create comparison rules for unique values of a
+     * property. The rules will be added to the style object for a specified
+     * rendering intent. This method is a shortcut for creating something like
+     * the "unique value legends" familiar from well known desktop GIS systems
+     * 
+     * Parameters:
+     * renderIntent - {String} rendering intent to add the rules to
+     * property     - {String} values of feature attributes to create the
+     *                rules for
+     * symbolizers  - {Object} Hash of symbolizers, keyed by the desired
+     *                property values 
+     * context      - {Object} An optional object with properties that
+     *                symbolizers' property values should be evaluated
+     *                against. If no context is specified, feature.attributes
+     *                will be used
+     */
+    addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
+        var rules = [];
+        for (var value in symbolizers) {
+            rules.push(new OpenLayers.Rule({
+                symbolizer: symbolizers[value],
+                context: context,
+                filter: new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                    property: property,
+                    value: value
+                })
+            }));
+        }
+        this.styles[renderIntent].addRules(rules);
+    },
+
+    CLASS_NAME: "OpenLayers.StyleMap"
+});
+/* ======================================================================
+    OpenLayers/Layer/Vector.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer.js
+ * @requires OpenLayers/Renderer.js
+ * @requires OpenLayers/StyleMap.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Vector
+ * Instances of OpenLayers.Layer.Vector are used to render vector data from
+ *     a variety of sources. Create a new vector layer with the
+ *     <OpenLayers.Layer.Vector> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Layer>
+ */
+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
+
+    /**
+     * Constant: EVENT_TYPES
+     * {Array(String)} Supported application event types.  Register a listener
+     *     for a particular event with the following syntax:
+     * (code)
+     * layer.events.register(type, obj, listener);
+     * (end)
+     *
+     * Listeners will be called with a reference to an event object.  The
+     *     properties of this event depends on exactly what happened.
+     *
+     * All event objects have at least the following properties:
+     * object - {Object} A reference to layer.events.object.
+     * element - {DOMElement} A reference to layer.events.element.
+     *
+     * Supported map event types (in addition to those from <OpenLayers.Layer>):
+     * 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.
+     *      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
+     *      object passed to listeners will have a *feature* property with a
+     *      reference to the added feature.
+     * 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
+     *      will receive an object with a *feature* property referencing the
+     *      feature to be removed.
+     * beforefeaturesremoved - Triggered before multiple features are removed. 
+     *      Listeners will receive an object with a *features* property
+     *      referencing the features to be removed.
+     * 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
+     *      object passed to listeners will have a *features* property with a
+     *      reference to an array of removed features.
+     * beforefeatureselected - Triggered after a feature is selected.  Listeners
+     *      will receive an object with a *feature* property referencing the
+     *      feature to be selected. To stop the feature from being selectd, a
+     *      listener should return false.
+     * 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.
+     *      Listeners will receive an object with a *feature* property
+     *      referencing the unselected feature.
+     * 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.
+     *      Listeners will receive an object with a *feature* property referencing 
+     *      the modified feature.
+     * afterfeaturemodified - Triggered when a feature is finished being modified.
+     *      Listeners will receive an object with a *feature* property referencing 
+     *      the modified feature.
+     * 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.
+     * vertexremoved - Triggered when a vertex within any feature geometry
+     *      has been deleted.  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
+     *      removal.
+     * sketchstarted - Triggered when a feature sketch bound for this layer
+     *      is started.  Listeners will receive an object with a *feature*
+     *      property referencing the new sketch feature and a *vertex* property
+     *      referencing the creation point.
+     * 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 and a *feature* property
+     *      referencing the sketch feature.
+     * 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",
+                  "featureadded", "featuresadded", "beforefeatureremoved",
+                  "beforefeaturesremoved", "featureremoved", "featuresremoved",
+                  "beforefeatureselected", "featureselected", "featureunselected", 
+                  "beforefeaturemodified", "featuremodified", "afterfeaturemodified",
+                  "vertexmodified", "vertexremoved", "sketchstarted",
+                  "sketchmodified", "sketchcomplete", "refresh"],
+
+    /**
+     * APIProperty: isBaseLayer
+     * {Boolean} The layer is a base layer.  Default is false.  Set this property
+     * in the layer options.
+     */
+    isBaseLayer: false,
+
+    /** 
+     * APIProperty: isFixed
+     * {Boolean} Whether the layer remains in one place while dragging the
+     * map.
+     */
+    isFixed: false,
+
+    /** 
+     * APIProperty: features
+     * {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    features: null,
+    
+    /** 
+     * Property: filter
+     * {<OpenLayers.Filter>} The filter set in this layer,
+     *     a strategy launching read requests can combined
+     *     this filter with its own filter.
+     */
+    filter: null,
+    
+    /** 
+     * Property: selectedFeatures
+     * {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    selectedFeatures: null,
+    
+    /**
+     * Property: unrenderedFeatures
+     * {Object} hash of features, keyed by feature.id, that the renderer
+     *     failed to draw
+     */
+    unrenderedFeatures: null,
+
+    /**
+     * APIProperty: reportError
+     * {Boolean} report friendly error message when loading of renderer
+     * fails.
+     */
+    reportError: true, 
+
+    /** 
+     * APIProperty: style
+     * {Object} Default style for the layer
+     */
+    style: null,
+    
+    /**
+     * Property: styleMap
+     * {<OpenLayers.StyleMap>}
+     */
+    styleMap: null,
+    
+    /**
+     * Property: strategies
+     * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
+     */
+    strategies: null,
+    
+    /**
+     * Property: protocol
+     * {<OpenLayers.Protocol>} Optional protocol for the layer.
+     */
+    protocol: null,
+    
+    /**
+     * Property: renderers
+     * {Array(String)} List of supported Renderer classes. Add to this list to
+     * add support for additional renderers. This list is ordered:
+     * the first renderer which returns true for the  'supported()'
+     * method will be used, if not defined in the 'renderer' option.
+     */
+    renderers: ['SVG', 'VML', 'Canvas'],
+    
+    /** 
+     * Property: renderer
+     * {<OpenLayers.Renderer>}
+     */
+    renderer: null,
+    
+    /**
+     * APIProperty: rendererOptions
+     * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
+     *     supported options.
+     */
+    rendererOptions: null,
+    
+    /** 
+     * APIProperty: geometryType
+     * {String} geometryType allows you to limit the types of geometries this
+     * layer supports. This should be set to something like
+     * "OpenLayers.Geometry.Point" to limit types.
+     */
+    geometryType: null,
+
+    /** 
+     * Property: drawn
+     * {Boolean} Whether the Vector Layer features have been drawn yet.
+     */
+    drawn: false,
+
+    /**
+     * Constructor: OpenLayers.Layer.Vector
+     * Create a new vector layer
+     *
+     * Parameters:
+     * name - {String} A name for the layer
+     * options - {Object} Optional object with non-default properties to set on
+     *           the layer.
+     *
+     * Returns:
+     * {<OpenLayers.Layer.Vector>} A new vector layer
+     */
+    initialize: function(name, options) {
+        
+        // concatenate events specific to vector with those from the base
+        this.EVENT_TYPES =
+            OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(
+            OpenLayers.Layer.prototype.EVENT_TYPES
+        );
+
+        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
+
+        // allow user-set renderer, otherwise assign one
+        if (!this.renderer || !this.renderer.supported()) {  
+            this.assignRenderer();
+        }
+
+        // if no valid renderer found, display error
+        if (!this.renderer || !this.renderer.supported()) {
+            this.renderer = null;
+            this.displayError();
+        } 
+
+        if (!this.styleMap) {
+            this.styleMap = new OpenLayers.StyleMap();
+        }
+
+        this.features = [];
+        this.selectedFeatures = [];
+        this.unrenderedFeatures = {};
+        
+        // Allow for custom layer behavior
+        if(this.strategies){
+            for(var i=0, len=this.strategies.length; i<len; i++) {
+                this.strategies[i].setLayer(this);
+            }
+        }
+
+    },
+
+    /**
+     * APIMethod: destroy
+     * Destroy this layer
+     */
+    destroy: function() {
+        if (this.strategies) {
+            var strategy, i, len;
+            for(i=0, len=this.strategies.length; i<len; i++) {
+                strategy = this.strategies[i];
+                if(strategy.autoDestroy) {
+                    strategy.destroy();
+                }
+            }
+            this.strategies = null;
+        }
+        if (this.protocol) {
+            if(this.protocol.autoDestroy) {
+                this.protocol.destroy();
+            }
+            this.protocol = null;
+        }
+        this.destroyFeatures();
+        this.features = null;
+        this.selectedFeatures = null;
+        this.unrenderedFeatures = null;
+        if (this.renderer) {
+            this.renderer.destroy();
+        }
+        this.renderer = null;
+        this.geometryType = null;
+        this.drawn = null;
+        OpenLayers.Layer.prototype.destroy.apply(this, arguments);  
+    },
+
+    /**
+     * Method: clone
+     * Create a clone of this layer.
+     * 
+     * Note: Features of the layer are also cloned.
+     *
+     * Returns:
+     * {<OpenLayers.Layer.Vector>} An exact clone of this layer
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+        var features = this.features;
+        var len = features.length;
+        var clonedFeatures = new Array(len);
+        for(var i=0; i<len; ++i) {
+            clonedFeatures[i] = features[i].clone();
+        }
+        obj.features = clonedFeatures;
+
+        return obj;
+    },    
+    
+    /**
+     * Method: refresh
+     * Ask the layer to request features again and redraw them.  Triggers
+     *     the refresh event if the layer is in range and visible.
+     *
+     * Parameters:
+     * obj - {Object} Optional object with properties for any listener of
+     *     the refresh event.
+     */
+    refresh: function(obj) {
+        if(this.calculateInRange() && this.visibility) {
+            this.events.triggerEvent("refresh", obj);
+        }
+    },
+
+    /** 
+     * Method: assignRenderer
+     * Iterates through the available renderer implementations and selects 
+     * and assigns the first one whose "supported()" function returns true.
+     */    
+    assignRenderer: function()  {
+        for (var i=0, len=this.renderers.length; i<len; i++) {
+            var rendererClass = this.renderers[i];
+            var renderer = (typeof rendererClass == "function") ?
+                rendererClass :
+                OpenLayers.Renderer[rendererClass];
+            if (renderer && renderer.prototype.supported()) {
+                this.renderer = new renderer(this.div, this.rendererOptions);
+                break;
+            }  
+        }  
+    },
+
+    /** 
+     * Method: displayError 
+     * Let the user know their browser isn't supported.
+     */
+    displayError: function() {
+        if (this.reportError) {
+            OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", 
+                                     {'renderers':this.renderers.join("\n")}));
+        }    
+    },
+
+    /** 
+     * Method: setMap
+     * The layer has been added to the map. 
+     * 
+     * If there is no renderer set, the layer can't be used. Remove it.
+     * Otherwise, give the renderer a reference to the map and set its size.
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>} 
+     */
+    setMap: function(map) {        
+        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
+
+        if (!this.renderer) {
+            this.map.removeLayer(this);
+        } else {
+            this.renderer.map = this.map;
+            this.renderer.setSize(this.map.getSize());
+        }
+    },
+
+    /**
+     * Method: afterAdd
+     * Called at the end of the map.addLayer sequence.  At this point, the map
+     *     will have a base layer.  Any autoActivate strategies will be
+     *     activated here.
+     */
+    afterAdd: function() {
+        if(this.strategies) {
+            var strategy, i, len;
+            for(i=0, len=this.strategies.length; i<len; i++) {
+                strategy = this.strategies[i];
+                if(strategy.autoActivate) {
+                    strategy.activate();
+                }
+            }
+        }
+    },
+
+    /**
+     * Method: removeMap
+     * The layer has been removed from the map.
+     *
+     * Parameters:
+     * map - {<OpenLayers.Map>}
+     */
+    removeMap: function(map) {
+        this.drawn = false;
+        if(this.strategies) {
+            var strategy, i, len;
+            for(i=0, len=this.strategies.length; i<len; i++) {
+                strategy = this.strategies[i];
+                if(strategy.autoActivate) {
+                    strategy.deactivate();
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method: onMapResize
+     * Notify the renderer of the change in size. 
+     * 
+     */
+    onMapResize: function() {
+        OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
+        this.renderer.setSize(this.map.getSize());
+    },
+
+    /**
+     * Method: moveTo
+     *  Reset the vector layer's div so that it once again is lined up with 
+     *   the map. Notify the renderer of the change of extent, and in the
+     *   case of a change of zoom level (resolution), have the 
+     *   renderer redraw features.
+     * 
+     *  If the layer has not yet been drawn, cycle through the layer's 
+     *   features and draw each one.
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} 
+     * zoomChanged - {Boolean} 
+     * dragging - {Boolean} 
+     */
+    moveTo: function(bounds, zoomChanged, dragging) {
+        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
+        
+        var coordSysUnchanged = true;
+
+        if (!dragging) {
+            this.renderer.root.style.visibility = "hidden";
+            
+            this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
+            this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
+            var extent = this.map.getExtent();
+            coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
+            
+            this.renderer.root.style.visibility = "visible";
+
+            // Force a reflow on gecko based browsers to prevent jump/flicker.
+            // This seems to happen on only certain configurations; it was originally
+            // noticed in FF 2.0 and Linux.
+            if (OpenLayers.IS_GECKO === true) {
+                this.div.scrollLeft = this.div.scrollLeft;
+            }
+            
+            if(!zoomChanged && coordSysUnchanged) {
+                for(var i in this.unrenderedFeatures) {
+                    var feature = this.unrenderedFeatures[i];
+                    this.drawFeature(feature);
+                }
+            }
+        }
+        
+        if (!this.drawn || zoomChanged || !coordSysUnchanged) {
+            this.drawn = true;
+            var feature;
+            for(var i=0, len=this.features.length; i<len; i++) {
+                this.renderer.locked = (i !== (len - 1));
+                feature = this.features[i];
+                this.drawFeature(feature);
+            }
+        }    
+    },
+    
+    /** 
+     * 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
+     * Add Features to the layer.
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} 
+     * options - {Object}
+     */
+    addFeatures: function(features, options) {
+        if (!(features instanceof Array)) {
+            features = [features];
+        }
+        
+        var notify = !options || !options.silent;
+        if(notify) {
+            var event = {features: features};
+            var ret = this.events.triggerEvent("beforefeaturesadded", event);
+            if(ret === false) {
+                return;
+            }
+            features = event.features;
+        }
+        
+        // Track successfully added features for featuresadded event, since
+        // beforefeatureadded can veto single features.
+        var featuresAdded = [];
+        for (var i=0, len=features.length; i<len; i++) {
+            if (i != (features.length - 1)) {
+                this.renderer.locked = true;
+            } else {
+                this.renderer.locked = false;
+            }    
+            var feature = features[i];
+            
+            if (this.geometryType &&
+              !(feature.geometry instanceof this.geometryType)) {
+                var throwStr = OpenLayers.i18n('componentShouldBe',
+                          {'geomType':this.geometryType.prototype.CLASS_NAME});
+                throw throwStr;
+              }
+
+            //give feature reference to its layer
+            feature.layer = this;
+
+            if (!feature.style && this.style) {
+                feature.style = OpenLayers.Util.extend({}, this.style);
+            }
+
+            if (notify) {
+                if(this.events.triggerEvent("beforefeatureadded",
+                                            {feature: feature}) === false) {
+                    continue;
+                };
+                this.preFeatureInsert(feature);
+            }
+
+            featuresAdded.push(feature);
+            this.features.push(feature);
+            this.drawFeature(feature);
+            
+            if (notify) {
+                this.events.triggerEvent("featureadded", {
+                    feature: feature
+                });
+                this.onFeatureInsert(feature);
+            }
+        }
+        
+        if(notify) {
+            this.events.triggerEvent("featuresadded", {features: featuresAdded});
+        }
+    },
+
+
+    /**
+     * 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>)} 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) {
+            return;
+        }
+        if (features === this.features) {
+            return this.removeAllFeatures(options);
+        }
+        if (!(features instanceof Array)) {
+            features = [features];
+        }
+        if (features === this.selectedFeatures) {
+            features = features.slice();
+        }
+
+        var notify = !options || !options.silent;
+        
+        if (notify) {
+            this.events.triggerEvent(
+                "beforefeaturesremoved", {features: features}
+            );
+        }
+
+        for (var i = features.length - 1; i >= 0; i--) {
+            // We remain locked so long as we're not at 0
+            // and the 'next' feature has a geometry. We do the geometry check
+            // because if all the features after the current one are 'null', we
+            // won't call eraseGeometry, so we break the 'renderer functions
+            // will always be called with locked=false *last*' rule. The end result
+            // is a possible gratiutious unlocking to save a loop through the rest 
+            // of the list checking the remaining features every time. So long as
+            // null geoms are rare, this is probably okay.    
+            if (i != 0 && features[i-1].geometry) {
+                this.renderer.locked = true;
+            } else {
+                this.renderer.locked = false;
+            }
+    
+            var feature = features[i];
+            delete this.unrenderedFeatures[feature.id];
+
+            if (notify) {
+                this.events.triggerEvent("beforefeatureremoved", {
+                    feature: feature
+                });
+            }
+
+            this.features = OpenLayers.Util.removeItem(this.features, feature);
+            // feature has no layer at this point
+            feature.layer = null;
+
+            if (feature.geometry) {
+                this.renderer.eraseFeatures(feature);
+            }
+                    
+            //in the case that this feature is one of the selected features, 
+            // remove it from that array as well.
+            if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
+                OpenLayers.Util.removeItem(this.selectedFeatures, feature);
+            }
+
+            if (notify) {
+                this.events.triggerEvent("featureremoved", {
+                    feature: feature
+                });
+            }
+        }
+
+        if (notify) {
+            this.events.triggerEvent("featuresremoved", {features: features});
+        }
+    },
+    
+    /** 
+     * APIMethod: removeAllFeatures
+     * Remove all features from the layer.
+     *
+     * Parameters:
+     * options - {Object} Optional properties for changing behavior of the
+     *     removal.
+     *
+     * Valid options:
+     * silent - {Boolean} Supress event triggering.  Default is false.
+     */
+    removeAllFeatures: function(options) {
+        var notify = !options || !options.silent;
+        var features = this.features;
+        if (notify) {
+            this.events.triggerEvent(
+                "beforefeaturesremoved", {features: features}
+            );
+        }
+        var feature;
+        for (var i = features.length-1; i >= 0; i--) {
+            feature = features[i];
+            if (notify) {
+                this.events.triggerEvent("beforefeatureremoved", {
+                    feature: feature
+                });
+            }
+            feature.layer = null;
+            if (notify) {
+                this.events.triggerEvent("featureremoved", {
+                    feature: feature
+                });
+            }
+        }
+        this.renderer.clear();
+        this.features = [];
+        this.unrenderedFeatures = {};
+        this.selectedFeatures = [];
+        if (notify) {
+            this.events.triggerEvent("featuresremoved", {features: features});
+        }
+    },
+
+    /**
+     * APIMethod: destroyFeatures
+     * Erase and destroy features on the layer.
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
+     *     features to destroy.  If not supplied, all features on the layer
+     *     will be destroyed.
+     * options - {Object}
+     */
+    destroyFeatures: function(features, options) {
+        var all = (features == undefined); // evaluates to true if
+                                           // features is null
+        if(all) {
+            features = this.features;
+        }
+        if(features) {
+            this.removeFeatures(features, options);
+            for(var i=features.length-1; i>=0; i--) {
+                features[i].destroy();
+            }
+        }
+    },
+
+    /**
+     * APIMethod: drawFeature
+     * Draw (or redraw) a feature on the layer.  If the optional style argument
+     * is included, this style will be used.  If no style is included, the
+     * 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 - {String | Object} Named render intent or full symbolizer object.
+     */
+    drawFeature: function(feature, style) {
+        // don't try to draw the feature with the renderer if the layer is not 
+        // drawn itself
+        if (!this.drawn) {
+            return
+        }
+        if (typeof style != "object") {
+            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);
+            }
+        }
+        
+        if (!this.renderer.drawFeature(feature, style)) {
+            this.unrenderedFeatures[feature.id] = feature;
+        } else {
+            delete this.unrenderedFeatures[feature.id];
+        };
+    },
+    
+    /**
+     * Method: eraseFeatures
+     * Erase features from the layer.
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>)} 
+     */
+    eraseFeatures: function(features) {
+        this.renderer.eraseFeatures(features);
+    },
+
+    /**
+     * Method: getFeatureFromEvent
+     * Given an event, return a feature if the event occurred over one.
+     * Otherwise, return null.
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
+     */
+    getFeatureFromEvent: function(evt) {
+        if (!this.renderer) {
+            OpenLayers.Console.error(OpenLayers.i18n("getFeatureError")); 
+            return null;
+        }    
+        var featureId = this.renderer.getFeatureIdFromEvent(evt);
+        return this.getFeatureById(featureId);
+    },
+
+    /**
+     * APIMethod: getFeatureBy
+     * Given a property value, return the feature if it exists in the features array
+     *
+     * Parameters:
+     * property - {String}
+     * value - {String}
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
+     * property value or null if there is no such feature.
+     */
+    getFeatureBy: function(property, value) {
+        //TBD - would it be more efficient to use a hash for this.features?
+        var feature = null;
+        for(var i=0, len=this.features.length; i<len; ++i) {
+            if(this.features[i][property] == value) {
+                feature = this.features[i];
+                break;
+            }
+        }
+        return feature;
+    },
+
+    /**
+     * APIMethod: getFeatureById
+     * Given a feature id, return the feature if it exists in the features array
+     *
+     * Parameters:
+     * featureId - {String}
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
+     * featureId or null if there is no such feature.
+     */
+    getFeatureById: function(featureId) {
+        return this.getFeatureBy('id', featureId);
+    },
+
+    /**
+     * APIMethod: getFeatureByFid
+     * Given a feature fid, return the feature if it exists in the features array
+     *
+     * Parameters:
+     * featureFid - {String}
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
+     * featureFid or null if there is no such feature.
+     */
+    getFeatureByFid: function(featureFid) {
+        return this.getFeatureBy('fid', featureFid);
+    },
+    
+    /**
+     * APIMethod: getFeaturesByAttribute
+     * Returns an array of features that have the given attribute key set to the
+     * given value. Comparison of attribute values takes care of datatypes, e.g.
+     * the string '1234' is not equal to the number 1234.
+     *
+     * Parameters:
+     * attrName - {String}
+     * attrValue - {Mixed}
+     *
+     * Returns:
+     * Array(<OpenLayers.Feature.Vector>) An array of features that have the 
+     * passed named attribute set to the given value.
+     */
+    getFeaturesByAttribute: function(attrName, attrValue) {
+        var i,
+            feature,    
+            len = this.features.length,
+            foundFeatures = [];
+        for(i = 0; i < len; i++) {            
+            feature = this.features[i];
+            if(feature && feature.attributes) {
+                if (feature.attributes[attrName] === attrValue) {
+                    foundFeatures.push(feature);
+                }
+            }
+        }
+        return foundFeatures;
+    },
+
+    /**
+     * Unselect the selected features
+     * i.e. clears the featureSelection array
+     * change the style back
+    clearSelection: function() {
+
+       var vectorLayer = this.map.vectorLayer;
+        for (var i = 0; i < this.map.featureSelection.length; i++) {
+            var featureSelection = this.map.featureSelection[i];
+            vectorLayer.drawFeature(featureSelection, vectorLayer.style);
+        }
+        this.map.featureSelection = [];
+    },
+     */
+
+
+    /**
+     * APIMethod: onFeatureInsert
+     * method called after a feature is inserted.
+     * Does nothing by default. Override this if you
+     * need to do something on feature updates.
+     *
+     * Paarameters: 
+     * feature - {<OpenLayers.Feature.Vector>} 
+     */
+    onFeatureInsert: function(feature) {
+    },
+    
+    /**
+     * APIMethod: preFeatureInsert
+     * method called before a feature is inserted.
+     * Does nothing by default. Override this if you
+     * need to do something when features are first added to the
+     * layer, but before they are drawn, such as adjust the style.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     */
+    preFeatureInsert: function(feature) {
+    },
+
+    /** 
+     * APIMethod: getDataExtent
+     * Calculates the max extent which includes all of the features.
+     * 
+     * Returns:
+     * {<OpenLayers.Bounds>}
+     */
+    getDataExtent: function () {
+        var maxExtent = null;
+        var features = this.features;
+        if(features && (features.length > 0)) {
+            maxExtent = new OpenLayers.Bounds();
+            var geometry = null;
+            for(var i=0, len=features.length; i<len; i++) {
+                geometry = features[i].geometry;
+                if (geometry) {
+                    maxExtent.extend(geometry.getBounds());
+                }
+            }
+        }
+        return maxExtent;
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.Vector"
+});
+/* ======================================================================
+    OpenLayers/Layer/XYZ.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ * @requires OpenLayers/Tile/Image.js
+ */
+
+/** 
+ * Class: OpenLayers.Layer.XYZ
+ * The XYZ class is designed to make it easier for people who have tiles
+ * arranged by a standard XYZ grid. 
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
+    
+    /**
+     * APIProperty: isBaseLayer
+     * Default is true, as this is designed to be a base tile source. 
+     */
+    isBaseLayer: true,
+    
+    /**
+     * APIProperty: sphericalMecator
+     * Whether the tile extents should be set to the defaults for 
+     *    spherical mercator. Useful for things like OpenStreetMap.
+     *    Default is false, except for the OSM subclass.
+     */
+    sphericalMercator: false,
+
+    /**
+     * APIProperty: zoomOffset
+     * {Number} If your cache has more zoom levels than you want to provide
+     *     access to with this layer, supply a zoomOffset.  This zoom offset
+     *     is added to the current map zoom level to determine the level
+     *     for a requested tile.  For example, if you supply a zoomOffset
+     *     of 3, when the map is at the zoom 0, tiles will be requested from
+     *     level 3 of your cache.  Default is 0 (assumes cache level and map
+     *     zoom are equivalent).  Using <zoomOffset> is an alternative to
+     *     setting <serverResolutions> if you only want to expose a subset
+     *     of the server resolutions.
+     */
+    zoomOffset: 0,
+    
+    /**
+     * APIProperty: serverResolutions
+     * {Array} A list of all resolutions available on the server.  Only set this
+     *     property if the map resolutions differs from the server.
+     */
+    serverResolutions: null,
+
+    /**
+     * Constructor: OpenLayers.Layer.XYZ
+     *
+     * Parameters:
+     * name - {String}
+     * url - {String}
+     * options - {Object} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, options) {
+        if (options && options.sphericalMercator || this.sphericalMercator) {
+            options = OpenLayers.Util.extend({
+                maxExtent: new OpenLayers.Bounds(
+                    -128 * 156543.0339,
+                    -128 * 156543.0339,
+                    128 * 156543.0339,
+                    128 * 156543.0339
+                ),
+                maxResolution: 156543.0339,
+                numZoomLevels: 19,
+                units: "m",
+                projection: "EPSG:900913"
+            }, options);
+        }
+        url = url || this.url;
+        name = name || this.name;
+        var newArguments = [name, url, {}, options];
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+    },
+    
+    /**
+     * APIMethod: clone
+     * Create a clone of this layer
+     *
+     * Parameters:
+     * obj - {Object} Is this ever used?
+     * 
+     * Returns:
+     * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
+     */
+    clone: function (obj) {
+        
+        if (obj == null) {
+            obj = new OpenLayers.Layer.XYZ(this.name,
+                                            this.url,
+                                            this.getOptions());
+        }
+
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+        return obj;
+    },    
+
+    /**
+     * Method: getURL
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {String} A string with the layer's url and parameters and also the
+     *          passed-in bounds and appropriate tile size specified as
+     *          parameters
+     */
+    getURL: function (bounds) {
+        var xyz = this.getXYZ(bounds);
+        var url = this.url;
+        if (url instanceof Array) {
+            var s = '' + xyz.x + xyz.y + xyz.z;
+            url = this.selectUrl(s, url);
+        }
+        
+        return OpenLayers.String.format(url, xyz);
+    },
+    
+    /**
+     * Method: getXYZ
+     * Calculates x, y and z for the given bounds.
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {Object} - an object with x, y and z properties.
+     */
+    getXYZ: function(bounds) {
+        var res = this.map.getResolution();
+        var x = Math.round((bounds.left - this.maxExtent.left) 
+            / (res * this.tileSize.w));
+        var y = Math.round((this.maxExtent.top - bounds.top) 
+            / (res * this.tileSize.h));
+        var z = this.serverResolutions != null ?
+            OpenLayers.Util.indexOf(this.serverResolutions, res) :
+            this.map.getZoom() + this.zoomOffset;
+
+        return {'x': x, 'y': y, 'z': z};
+    },
+    
+    /**
+     * Method: addTile
+     * addTile creates a tile, initializes it, and adds it to the layer div. 
+     * 
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+     */
+    addTile:function(bounds,position) {
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                         null, this.tileSize);
+    },
+     
+    /* APIMethod: setMap
+     * When the layer is added to a map, then we can fetch our origin 
+     *    (if we don't have one.) 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>}
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
+        if (!this.tileOrigin) { 
+            this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
+                                                this.maxExtent.bottom);
+        }                                       
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.XYZ"
+});
+
+
+/**
+ * Class: OpenLayers.Layer.OSM
+ * A class to access OpenStreetMap tiles. By default, uses the OpenStreetMap
+ *    hosted tile.openstreetmap.org 'Mapnik' tileset. If you wish to use
+ *    tiles at home / osmarender layer instead, you can pass a layer like:
+ * 
+ * (code)
+ *     new OpenLayers.Layer.OSM("t at h", 
+ *       "http://tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png"); 
+ * (end)
+ *
+ * This layer defaults to Spherical Mercator.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer.XYZ>
+ */
+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+     name: "OpenStreetMap",
+     attribution: "Data CC-By-SA by <a href='http://openstreetmap.org/'>OpenStreetMap</a>",
+     sphericalMercator: true,
+     url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
+     clone: function(obj) {
+         if (obj == null) {
+             obj = new OpenLayers.Layer.OSM(
+                 this.name, this.url, this.getOptions());
+         }
+         obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+         return obj;
+     },
+     CLASS_NAME: "OpenLayers.Layer.OSM"
+});
+/* ======================================================================
+    OpenLayers/Renderer/SVG.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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: symbolMetrics
+     * {Object} Cache for symbol metrics according to their svg coordinate
+     *     space. This is an object keyed by the symbol's id, and values are
+     *     an array of [width, centerX, centerY].
+     */
+    symbolMetrics: null,
+    
+    /**
+     * Constructor: OpenLayers.Renderer.SVG
+     * 
+     * Parameters:
+     * containerID - {String}
+     */
+    initialize: function(containerID) {
+        if (!this.supported()) { 
+            return; 
+        }
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        this.translationParameters = {x: 0, y: 0};
+        
+        this.symbolMetrics = {};
+    },
+
+    /**
+     * 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
+     *
+     * 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.
+     * 
+     * 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.
+     */
+    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: 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;
+        }
+    },
+
+    /**
+     * 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 = "svg";
+                } 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;
+        }
+        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) {
+            node.style.visibility = "";
+            if (style.graphic === false) {
+                node.style.visibility = "hidden";
+            } else 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 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);
+                pos = this.getPosition(node);
+                widthFactor = this.symbolMetrics[id][0] * 3 / size;
+                
+                // remove the node from the dom before we modify it. This
+                // prevents various rendering issues in Safari and FF
+                var parent = node.parentNode;
+                var nextSibling = node.nextSibling;
+                if(parent) {
+                    parent.removeChild(node);
+                }
+                
+                // The more appropriate way to implement this would be use/defs,
+                // but due to various issues in several browsers, it is safer to
+                // copy the symbols instead of referencing them. 
+                // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 
+                // and this email thread
+                // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
+                var src = document.getElementById(id);
+                node.firstChild && node.removeChild(node.firstChild);
+                node.appendChild(src.firstChild.cloneNode(true));
+                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
+                
+                node.setAttributeNS(null, "width", size);
+                node.setAttributeNS(null, "height", size);
+                node.setAttributeNS(null, "x", pos.x - offset);
+                node.setAttributeNS(null, "y", pos.y - offset);
+                
+                // now that the node has all its new properties, insert it
+                // back into the dom where it was
+                if(nextSibling) {
+                    parent.insertBefore(node, nextSibling);
+                } else if(parent) {
+                    parent.appendChild(node);
+                }
+            } else {
+                node.setAttributeNS(null, "r", style.pointRadius);
+            }
+
+            var rotation = style.rotation;
+            
+            if ((rotation !== undefined || node._rotation !== undefined) && pos) {
+                node._rotation = rotation;
+                rotation |= 0;
+                if (node.nodeName !== "svg") { 
+                    node.setAttributeNS(null, "transform", 
+                        "rotate(" + rotation + " " + pos.x + " " + 
+                        pos.y + ")"); 
+                } else {
+                    var metrics = this.symbolMetrics[id];
+                    node.firstChild.setAttributeNS(null, "transform", "rotate(" 
+                        + rotation + " " 
+                        + metrics[1] + " "
+                        + metrics[2] + ")");
+                }
+            }
+        }
+        
+        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 || "round");
+            // 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");
+            style.strokeDashstyle && 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: dashStyle
+     * 
+     * Parameters:
+     * style - {Object}
+     * widthFactor - {Number}
+     * 
+     * Returns:
+     * {String} A SVG compliant 'stroke-dasharray' value
+     */
+    dashStyle: function(style, widthFactor) {
+        var w = style.strokeWidth * widthFactor;
+        var str = style.strokeDashstyle;
+        switch (str) {
+            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 OpenLayers.String.trim(str).replace(/\s+/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
+     * 
+     * Parameter:
+     * suffix - {String} suffix to append to the id
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    createRoot: function(suffix) {
+        return this.nodeFactory(this.container.id + suffix, "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>}
+     * 
+     * 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: drawText
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String}
+     * style -
+     * location - {<OpenLayers.Geometry.Point>}
+     */
+    drawText: function(featureId, style, location) {
+        var resolution = this.getResolution();
+        
+        var x = (location.x / resolution + this.left);
+        var y = (location.y / resolution - this.top);
+        
+        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
+        var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan");
+
+        label.setAttributeNS(null, "x", x);
+        label.setAttributeNS(null, "y", -y);
+        
+        if (style.fontColor) {
+            label.setAttributeNS(null, "fill", style.fontColor);
+        }
+        if (style.fontOpacity) {
+            label.setAttributeNS(null, "opacity", style.fontOpacity);
+        }
+        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);
+        }
+        if(style.labelSelect === true) {
+            label.setAttributeNS(null, "pointer-events", "visible");
+            label._featureId = featureId;
+            tspan._featureId = featureId;
+            tspan._geometry = location;
+            tspan._geometryClass = location.CLASS_NAME;
+        } else {
+            label.setAttributeNS(null, "pointer-events", "none");
+        }
+        var align = style.labelAlign || "cm";
+        label.setAttributeNS(null, "text-anchor",
+            OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
+
+        if (OpenLayers.IS_GECKO === true) {
+            label.setAttributeNS(null, "dominant-baseline",
+                OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
+        } else {
+            tspan.setAttributeNS(null, "baseline-shift",
+                OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
+        }
+
+        tspan.textContent = style.label;
+        
+        if(!label.parentNode) {
+            label.appendChild(tspan);
+            this.textRoot.appendChild(label);
+        }   
+    },
+    
+    /** 
+     * 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;
+        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: 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;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Method: getPosition
+     * Finds the position of an svg node.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * 
+     * Returns:
+     * {Object} hash with x and y properties, representing the coordinates
+     *     within the svg coordinate system
+     */
+    getPosition: function(node) {
+        return({
+            x: parseFloat(node.getAttributeNS(null, "cx")),
+            y: parseFloat(node.getAttributeNS(null, "cy"))
+        });
+    },
+
+    /**
+     * 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');
+        }
+
+        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.push(x, ",", y);
+        }
+        
+        node.setAttributeNS(null, "points", points.join(" "));
+        
+        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.symbolMetrics[id] = [
+            Math.max(width, height),
+            symbolExtent.getCenterLonLat().lon,
+            symbolExtent.getCenterLonLat().lat
+        ];
+        
+        this.defs.appendChild(symbolNode);
+        return symbolNode.id;
+    },
+    
+    /**
+     * 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 featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
+        if(!featureId) {
+            var target = evt.target;
+            featureId = target.parentNode && target != this.rendererRoot &&
+                target.parentNode._featureId;
+        }
+        return featureId;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.SVG"
+});
+
+/**
+ * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
+ * {Object}
+ */
+OpenLayers.Renderer.SVG.LABEL_ALIGN = {
+    "l": "start",
+    "r": "end",
+    "b": "bottom",
+    "t": "hanging"
+};
+
+/**
+ * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
+ * {Object}
+ */
+OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
+    // according to
+    // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
+    // a baseline-shift of -70% shifts the text exactly from the
+    // bottom to the top of the baseline, so -35% moves the text to
+    // the center of the baseline.
+    "t": "-70%",
+    "b": "0"    
+};
+/* ======================================================================
+    OpenLayers/Control/ScaleLine.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ScaleLine
+ * The ScaleLine displays a small line indicator representing the current 
+ * map scale on the map. By default it is drawn in the lower left corner of
+ * the map.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ *  
+ * Is a very close copy of:
+ *  - <OpenLayers.Control.Scale>
+ */
+OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Property: maxWidth
+     * {Integer} Maximum width of the scale line in pixels.  Default is 100.
+     */
+    maxWidth: 100,
+
+    /**
+     * Property: topOutUnits
+     * {String} Units for zoomed out on top bar.  Default is km.
+     */
+    topOutUnits: "km",
+    
+    /**
+     * Property: topInUnits
+     * {String} Units for zoomed in on top bar.  Default is m.
+     */
+    topInUnits: "m",
+
+    /**
+     * Property: bottomOutUnits
+     * {String} Units for zoomed out on bottom bar.  Default is mi.
+     */
+    bottomOutUnits: "mi",
+
+    /**
+     * Property: bottomInUnits
+     * {String} Units for zoomed in on bottom bar.  Default is ft.
+     */
+    bottomInUnits: "ft",
+    
+    /**
+     * Property: eTop
+     * {DOMElement}
+     */
+    eTop: null,
+
+    /**
+     * Property: eBottom
+     * {DOMElement}
+     */
+    eBottom:null,
+    
+    /**
+     * APIProperty: geodesic
+     * {Boolean} Use geodesic measurement. Default is false. The recommended
+     * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to
+     * true, the scale will be calculated based on the horizontal size of the
+     * pixel in the center of the map viewport.
+     */
+    geodesic: false,
+
+    /**
+     * Constructor: OpenLayers.Control.ScaleLine
+     * Create a new scale line control.
+     * 
+     * Parameters:
+     * options - {Object} An optional object whose properties will be used
+     *     to extend the control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);     
+    },
+
+    /**
+     * Method: draw
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        if (!this.eTop) {
+            // stick in the top bar
+            this.eTop = document.createElement("div");
+            this.eTop.className = this.displayClass + "Top";
+            var theLen = this.topInUnits.length;
+            this.div.appendChild(this.eTop);
+            if((this.topOutUnits == "") || (this.topInUnits == "")) {
+                this.eTop.style.visibility = "hidden";
+            } else {
+                this.eTop.style.visibility = "visible";
+            }
+
+            // and the bottom bar
+            this.eBottom = document.createElement("div");
+            this.eBottom.className = this.displayClass + "Bottom";
+            this.div.appendChild(this.eBottom);
+            if((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
+                this.eBottom.style.visibility = "hidden";
+            } else {
+                this.eBottom.style.visibility = "visible";
+            }
+        }
+        this.map.events.register('moveend', this, this.update);
+        this.update();
+        return this.div;
+    },
+
+    /** 
+     * Method: getBarLen
+     * Given a number, round it down to the nearest 1,2,5 times a power of 10.
+     * That seems a fairly useful set of number groups to use.
+     * 
+     * Parameters:
+     * maxLen - {float}  the number we're rounding down from
+     * 
+     * Returns:
+     * {Float} the rounded number (less than or equal to maxLen)
+     */
+    getBarLen: function(maxLen) {
+        // nearest power of 10 lower than maxLen
+        var digits = parseInt(Math.log(maxLen) / Math.log(10));
+        var pow10 = Math.pow(10, digits);
+        
+        // ok, find first character
+        var firstChar = parseInt(maxLen / pow10);
+
+        // right, put it into the correct bracket
+        var barLen;
+        if(firstChar > 5) {
+            barLen = 5;
+        } else if(firstChar > 2) {
+            barLen = 2;
+        } else {
+            barLen = 1;
+        }
+
+        // scale it up the correct power of 10
+        return barLen * pow10;
+    },
+
+    /**
+     * Method: update
+     * Update the size of the bars, and the labels they contain.
+     */
+    update: function() {
+        var res = this.map.getResolution();
+        if (!res) {
+            return;
+        }
+
+        var curMapUnits = this.map.getUnits();
+        var inches = OpenLayers.INCHES_PER_UNIT;
+
+        // convert maxWidth to map units
+        var maxSizeData = this.maxWidth * res * inches[curMapUnits];
+        var geodesicRatio = 1;
+        if(this.geodesic === true) {
+            var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||
+                0.000001) * this.maxWidth;
+            var maxSizeKilometers = maxSizeData / inches["km"];
+            geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
+            maxSizeData *= geodesicRatio;
+        }
+
+        // decide whether to use large or small scale units     
+        var topUnits;
+        var bottomUnits;
+        if(maxSizeData > 100000) {
+            topUnits = this.topOutUnits;
+            bottomUnits = this.bottomOutUnits;
+        } else {
+            topUnits = this.topInUnits;
+            bottomUnits = this.bottomInUnits;
+        }
+
+        // and to map units units
+        var topMax = maxSizeData / inches[topUnits];
+        var bottomMax = maxSizeData / inches[bottomUnits];
+
+        // now trim this down to useful block length
+        var topRounded = this.getBarLen(topMax);
+        var bottomRounded = this.getBarLen(bottomMax);
+
+        // and back to display units
+        topMax = topRounded / inches[curMapUnits] * inches[topUnits];
+        bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
+
+        // and to pixel units
+        var topPx = topMax / res / geodesicRatio;
+        var bottomPx = bottomMax / res / geodesicRatio;
+        
+        // now set the pixel widths
+        // and the values inside them
+        
+        if (this.eBottom.style.visibility == "visible"){
+            this.eBottom.style.width = Math.round(bottomPx) + "px"; 
+            this.eBottom.innerHTML = bottomRounded + " " + bottomUnits ;
+        }
+            
+        if (this.eTop.style.visibility == "visible"){
+            this.eTop.style.width = Math.round(topPx) + "px";
+            this.eTop.innerHTML = topRounded + " " + topUnits;
+        }
+        
+    }, 
+
+    CLASS_NAME: "OpenLayers.Control.ScaleLine"
+});
+
+/* ======================================================================
+    OpenLayers/Control/PanZoom.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.PanZoom
+ * 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>
+ */
+OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
+
+    /** 
+     * APIProperty: slideFactor
+     * {Integer} Number of pixels by which we'll pan the map in any direction 
+     *     on clicking the arrow buttons.  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 
+     */
+    buttons: null,
+
+    /** 
+     * Property: position
+     * {<OpenLayers.Pixel>} 
+     */
+    position: null,
+
+    /**
+     * Constructor: OpenLayers.Control.PanZoom
+     * 
+     * Parameters:
+     * options - {Object}
+     */
+    initialize: function(options) {
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
+                                             OpenLayers.Control.PanZoom.Y);
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);
+        this.removeButtons();
+        this.buttons = null;
+        this.position = null;
+    },
+
+    /**
+     * Method: draw
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} 
+     * 
+     * Returns:
+     * {DOMElement} A reference to the container div for the PanZoom control.
+     */
+    draw: function(px) {
+        // initialize our internal div
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        px = this.position;
+
+        // place the controls
+        this.buttons = [];
+
+        var sz = new OpenLayers.Size(18,18);
+        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+
+        this._addButton("panup", "north-mini.png", centered, sz);
+        px.y = centered.y+sz.h;
+        this._addButton("panleft", "west-mini.png", px, sz);
+        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
+        this._addButton("pandown", "south-mini.png", 
+                        centered.add(0, sz.h*2), sz);
+        this._addButton("zoomin", "zoom-plus-mini.png", 
+                        centered.add(0, sz.h*3+5), sz);
+        this._addButton("zoomworld", "zoom-world-mini.png", 
+                        centered.add(0, sz.h*4+5), sz);
+        this._addButton("zoomout", "zoom-minus-mini.png", 
+                        centered.add(0, sz.h*5+5), sz);
+        return this.div;
+    },
+    
+    /**
+     * Method: _addButton
+     * 
+     * Parameters:
+     * id - {String} 
+     * img - {String} 
+     * xy - {<OpenLayers.Pixel>} 
+     * sz - {<OpenLayers.Size>} 
+     * 
+     * Returns:
+     * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
+     *     image of the button, and has all the proper event handlers set.
+     */
+    _addButton:function(id, img, xy, sz) {
+        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+        var btn = OpenLayers.Util.createAlphaImageDiv(
+                                    this.id + "_" + id, 
+                                    xy, sz, imgLocation, "absolute");
+
+        //we want to add the outer div
+        this.div.appendChild(btn);
+
+        OpenLayers.Event.observe(btn, "mousedown", 
+            OpenLayers.Function.bindAsEventListener(this.buttonDown, btn));
+        OpenLayers.Event.observe(btn, "dblclick", 
+            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
+        OpenLayers.Event.observe(btn, "click", 
+            OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
+        btn.action = id;
+        btn.map = this.map;
+    
+        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;
+        btn.getSlideFactor = 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:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    doubleClick: function (evt) {
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+    
+    /**
+     * Method: buttonDown
+     *
+     * Parameters:
+     * evt - {Event} 
+     */
+    buttonDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) {
+            return;
+        }
+
+        switch (this.action) {
+            case "panup": 
+                this.map.pan(0, -this.getSlideFactor("h"));
+                break;
+            case "pandown": 
+                this.map.pan(0, this.getSlideFactor("h"));
+                break;
+            case "panleft": 
+                this.map.pan(-this.getSlideFactor("w"), 0);
+                break;
+            case "panright": 
+                this.map.pan(this.getSlideFactor("w"), 0);
+                break;
+            case "zoomin": 
+                this.map.zoomIn(); 
+                break;
+            case "zoomout": 
+                this.map.zoomOut(); 
+                break;
+            case "zoomworld": 
+                this.map.zoomToMaxExtent(); 
+                break;
+        }
+
+        OpenLayers.Event.stop(evt);
+    },
+
+    CLASS_NAME: "OpenLayers.Control.PanZoom"
+});
+
+/**
+ * Constant: X
+ * {Integer}
+ */
+OpenLayers.Control.PanZoom.X = 4;
+
+/**
+ * Constant: Y
+ * {Integer}
+ */
+OpenLayers.Control.PanZoom.Y = 4;
+/* ======================================================================
+    OpenLayers/Strategy/Paging.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Paging
+ * Strategy for vector feature paging
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * Property: features
+     * {Array(<OpenLayers.Feature.Vector>)} Cached features.
+     */
+    features: null,
+    
+    /**
+     * Property: length
+     * {Integer} Number of features per page.  Default is 10.
+     */
+    length: 10,
+    
+    /**
+     * Property: num
+     * {Integer} The currently displayed page number.
+     */
+    num: null,
+    
+    /**
+     * Property: paging
+     * {Boolean} The strategy is currently changing pages.
+     */
+    paging: false,
+
+    /**
+     * Constructor: OpenLayers.Strategy.Paging
+     * Create a new paging strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * APIMethod: activate
+     * Activate the strategy.  Register any listeners, do appropriate setup.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.call(this);
+        if(activated) {
+            this.layer.events.on({
+                "beforefeaturesadded": this.cacheFeatures,
+                scope: this
+            });
+        }
+        return activated;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Deactivate the strategy.  Unregister any listeners, do appropriate
+     *     tear-down.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            this.clearCache();
+            this.layer.events.un({
+                "beforefeaturesadded": this.cacheFeatures,
+                scope: this
+            });
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: cacheFeatures
+     * Cache features before they are added to the layer.
+     *
+     * Parameters:
+     * event - {Object} The event that this was listening for.  This will come
+     *     with a batch of features to be paged.
+     */
+    cacheFeatures: function(event) {
+        if(!this.paging) {
+            this.clearCache();
+            this.features = event.features;
+            this.pageNext(event);
+        }
+    },
+    
+    /**
+     * Method: clearCache
+     * Clear out the cached features.  This destroys features, assuming
+     *     nothing else has a reference.
+     */
+    clearCache: function() {
+        if(this.features) {
+            for(var i=0; i<this.features.length; ++i) {
+                this.features[i].destroy();
+            }
+        }
+        this.features = null;
+        this.num = null;
+    },
+    
+    /**
+     * APIMethod: pageCount
+     * Get the total count of pages given the current cache of features.
+     *
+     * Returns:
+     * {Integer} The page count.
+     */
+    pageCount: function() {
+        var numFeatures = this.features ? this.features.length : 0;
+        return Math.ceil(numFeatures / this.length);
+    },
+
+    /**
+     * APIMethod: pageNum
+     * Get the zero based page number.
+     *
+     * Returns:
+     * {Integer} The current page number being displayed.
+     */
+    pageNum: function() {
+        return this.num;
+    },
+
+    /**
+     * APIMethod: pageLength
+     * Gets or sets page length.
+     *
+     * Parameters:
+     * newLength: {Integer} Optional length to be set.
+     *
+     * Returns:
+     * {Integer} The length of a page (number of features per page).
+     */
+    pageLength: function(newLength) {
+        if(newLength && newLength > 0) {
+            this.length = newLength;
+        }
+        return this.length;
+    },
+
+    /**
+     * APIMethod: pageNext
+     * Display the next page of features.
+     *
+     * Returns:
+     * {Boolean} A new page was displayed.
+     */
+    pageNext: function(event) {
+        var changed = false;
+        if(this.features) {
+            if(this.num === null) {
+                this.num = -1;
+            }
+            var start = (this.num + 1) * this.length;
+            changed = this.page(start, event);
+        }
+        return changed;
+    },
+
+    /**
+     * APIMethod: pagePrevious
+     * Display the previous page of features.
+     *
+     * Returns:
+     * {Boolean} A new page was displayed.
+     */
+    pagePrevious: function() {
+        var changed = false;
+        if(this.features) {
+            if(this.num === null) {
+                this.num = this.pageCount();
+            }
+            var start = (this.num - 1) * this.length;
+            changed = this.page(start);
+        }
+        return changed;
+    },
+    
+    /**
+     * Method: page
+     * Display the page starting at the given index from the cache.
+     *
+     * Returns:
+     * {Boolean} A new page was displayed.
+     */
+    page: function(start, event) {
+        var changed = false;
+        if(this.features) {
+            if(start >= 0 && start < this.features.length) {
+                var num = Math.floor(start / this.length);
+                if(num != this.num) {
+                    this.paging = true;
+                    var features = this.features.slice(start, start + this.length);
+                    this.layer.removeFeatures(this.layer.features);
+                    this.num = num;
+                    // modify the event if any
+                    if(event && event.features) {
+                        // this.was called by an event listener
+                        event.features = features;
+                    } else {
+                        // this was called directly on the strategy
+                        this.layer.addFeatures(features);
+                    }
+                    this.paging = false;
+                    changed = true;
+                }
+            }
+        }
+        return changed;
+    },
+    
+    CLASS_NAME: "OpenLayers.Strategy.Paging" 
+});
+/* ======================================================================
+    OpenLayers/Strategy/BBOX.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ * @requires OpenLayers/Filter/Spatial.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.BBOX
+ * A simple strategy that reads new features when the viewport invalidates
+ *     some bounds.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * Property: bounds
+     * {<OpenLayers.Bounds>} The current data bounds (in the same projection
+     *     as the layer - not always the same projection as the map).
+     */
+    bounds: null,
+    
+    /** 
+     * Property: resolution 
+     * {Float} The current data resolution. 
+     */ 
+    resolution: null, 
+           
+    /**
+     * APIProperty: ratio
+     * {Float} The ratio of the data bounds to the viewport bounds (in each
+     *     dimension).  Default is 2.
+     */
+    ratio: 2,
+
+    /** 
+     * Property: resFactor 
+     * {Float} Optional factor used to determine when previously requested 
+     *     features are invalid.  If set, the resFactor will be compared to the
+     *     resolution of the previous request to the current map resolution.
+     *     If resFactor > (old / new) and 1/resFactor < (old / new).  If you
+     *     set a resFactor of 1, data will be requested every time the
+     *     resolution changes.  If you set a resFactor of 3, data will be
+     *     requested if the old resolution is 3 times the new, or if the new is
+     *     3 times the old.  If the old bounds do not contain the new bounds
+     *     new data will always be requested (with or without considering
+     *     resFactor). 
+     */ 
+    resFactor: null, 
+    
+    /**
+     * Property: response
+     * {<OpenLayers.Protocol.Response>} The protocol response object returned
+     *      by the layer protocol.
+     */
+    response: null,
+
+    /**
+     * Constructor: OpenLayers.Strategy.BBOX
+     * Create a new BBOX strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Method: activate
+     * Set up strategy with regard to reading new batches of remote data.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.call(this);
+        if(activated) {
+            this.layer.events.on({
+                "moveend": this.update,
+                scope: this
+            });
+            this.layer.events.on({
+                "refresh": this.update,
+                scope: this
+            });
+        }
+        return activated;
+    },
+    
+    /**
+     * Method: deactivate
+     * Tear down strategy with regard to reading new batches of remote data.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            this.layer.events.un({
+                "moveend": this.update,
+                scope: this
+            });
+            this.layer.events.un({
+                "refresh": this.update,
+                scope: this
+            });
+        }
+        return deactivated;
+    },
+
+    /**
+     * Method: update
+     * Callback function called on "moveend" or "refresh" layer events.
+     *
+     * Parameters:
+     * options - {Object} An object with a property named "force", this
+     *      property references a boolean value indicating if new data
+     *      must be incondtionally read.
+     */
+    update: function(options) {
+        var mapBounds = this.getMapBounds();
+        if ((options && options.force) || this.invalidBounds(mapBounds)) {
+            this.calculateBounds(mapBounds);
+            this.resolution = this.layer.map.getResolution(); 
+            this.triggerRead();
+        }
+    },
+    
+    /**
+     * Method: getMapBounds
+     * Get the map bounds expressed in the same projection as this layer.
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
+     */
+    getMapBounds: function() {
+        var bounds = this.layer.map.getExtent();
+        if(!this.layer.projection.equals(this.layer.map.getProjectionObject())) {
+            bounds = bounds.clone().transform(
+                this.layer.map.getProjectionObject(), this.layer.projection
+            );
+        }
+        return bounds;
+    },
+
+    /**
+     * Method: invalidBounds
+     * Determine whether the previously requested set of features is invalid. 
+     *     This occurs when the new map bounds do not contain the previously 
+     *     requested bounds.  In addition, if <resFactor> is set, it will be 
+     *     considered.
+     *
+     * Parameters:
+     * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
+     *      retrieved from the map object if not provided
+     *
+     * Returns:
+     * {Boolean} 
+     */
+    invalidBounds: function(mapBounds) {
+        if(!mapBounds) {
+            mapBounds = this.getMapBounds();
+        }
+        var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
+        if(!invalid && this.resFactor) {
+            var ratio = this.resolution / this.layer.map.getResolution();
+            invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
+        }
+        return invalid;
+    },
+ 
+    /**
+     * Method: calculateBounds
+     *
+     * Parameters:
+     * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
+     *      retrieved from the map object if not provided
+     */
+    calculateBounds: function(mapBounds) {
+        if(!mapBounds) {
+            mapBounds = this.getMapBounds();
+        }
+        var center = mapBounds.getCenterLonLat();
+        var dataWidth = mapBounds.getWidth() * this.ratio;
+        var dataHeight = mapBounds.getHeight() * this.ratio;
+        this.bounds = new OpenLayers.Bounds(
+            center.lon - (dataWidth / 2),
+            center.lat - (dataHeight / 2),
+            center.lon + (dataWidth / 2),
+            center.lat + (dataHeight / 2)
+        );
+    },
+    
+    /**
+     * Method: triggerRead
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} The protocol response object
+     *      returned by the layer protocol.
+     */
+    triggerRead: function() {
+        if (this.response) {
+            this.layer.protocol.abort(this.response);
+            this.layer.events.triggerEvent("loadend");
+        }
+        this.layer.events.triggerEvent("loadstart");
+        this.response = this.layer.protocol.read({
+            filter: this.createFilter(),
+            callback: this.merge,
+            scope: this
+        });
+    },
+ 
+    /**
+     * Method: createFilter
+     * Creates a spatial BBOX filter. If the layer that this strategy belongs
+     * to has a filter property, this filter will be combined with the BBOX 
+     * filter.
+     * 
+     * Returns
+     * {<OpenLayers.Filter>} The filter object.
+     */
+    createFilter: function() {
+        var filter = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.BBOX,
+            value: this.bounds,
+            projection: this.layer.projection
+        });
+        if (this.layer.filter) {
+            filter = new OpenLayers.Filter.Logical({
+                type: OpenLayers.Filter.Logical.AND,
+                filters: [this.layer.filter, filter]
+            });
+        }
+        return filter;
+    },
+   
+    /**
+     * Method: merge
+     * Given a list of features, determine which ones to add to the layer.
+     *     If the layer projection differs from the map projection, features
+     *     will be transformed from the layer projection to the map projection.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object passed
+     *      by the protocol.
+     */
+    merge: function(resp) {
+        this.layer.destroyFeatures();
+        var features = resp.features;
+        if(features && features.length > 0) {
+            var remote = this.layer.projection;
+            var local = this.layer.map.getProjectionObject();
+            if(!local.equals(remote)) {
+                var geom;
+                for(var i=0, len=features.length; i<len; ++i) {
+                    geom = features[i].geometry;
+                    if(geom) {
+                        geom.transform(remote, local);
+                    }
+                }
+            }
+            this.layer.addFeatures(features);
+        }
+        this.response = null;
+        this.layer.events.triggerEvent("loadend");
+    },
+   
+    CLASS_NAME: "OpenLayers.Strategy.BBOX" 
+});
+/* ======================================================================
+    OpenLayers/Layer/Google/v3.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/Google.js
+ */
+
+/**
+ * Constant: OpenLayers.Layer.Google.v3
+ * 
+ * Mixin providing functionality specific to the Google Maps API v3. Note that
+ * this layer configures the google.maps.map object with the "disableDefaultUI"
+ * option set to true. Using UI controls that the Google Maps API provides is
+ * not supported by the OpenLayers API.
+ */
+OpenLayers.Layer.Google.v3 = {
+    
+    /**
+     * Constant: DEFAULTS
+     * {Object} It is not recommended to change the properties set here. Note
+     * that Google.v3 layers only work when sphericalMercator is set to true.
+     * 
+     * (code)
+     * {
+     *     maxExtent: new OpenLayers.Bounds(
+     *         -128 * 156543.0339,
+     *         -128 * 156543.0339,
+     *         128 * 156543.0339,
+     *         128 * 156543.0339
+     *     ),
+     *     sphericalMercator: true,
+     *     maxResolution: 156543.0339,
+     *     units: "m",
+     *     projection: "EPSG:900913"
+     * }
+     * (end)
+     */
+    DEFAULTS: {
+        maxExtent: new OpenLayers.Bounds(
+            -128 * 156543.0339,
+            -128 * 156543.0339,
+            128 * 156543.0339,
+            128 * 156543.0339
+        ),
+        sphericalMercator: true,
+        maxResolution: 156543.0339,
+        units: "m",
+        projection: "EPSG:900913"
+    },
+
+    /**
+     * APIProperty: animationEnabled
+     * {Boolean} If set to true, the transition between zoom levels will be
+     *     animated (if supported by the GMaps API for the device used). Set to
+     *     false to match the zooming experience of other layer types. Default
+     *     is true. Note that the GMaps API does not give us control over zoom
+     *     animation, so if set to false, when zooming, this will make the
+     *     layer temporarily invisible, wait until GMaps reports the map being
+     *     idle, and make it visible again. The result will be a blank layer
+     *     for a few moments while zooming.
+     */
+    animationEnabled: true, 
+
+    /** 
+     * Method: loadMapObject
+     * Load the GMap and register appropriate event listeners. If we can't 
+     *     load GMap2, then display a warning message.
+     */
+    loadMapObject:function() {
+        if (!this.type) {
+            this.type = google.maps.MapTypeId.ROADMAP;
+        }
+        var mapObject;
+        var cache = OpenLayers.Layer.Google.cache[this.map.id];
+        if (cache) {
+            // there are already Google layers added to this map
+            mapObject = cache.mapObject;
+            // increment the layer count
+            ++cache.count;
+        } else {
+            // this is the first Google layer for this map
+
+            var container = this.map.viewPortDiv;
+            var div = document.createElement("div");
+            div.id = this.map.id + "_GMapContainer";
+            div.style.position = "absolute";
+            div.style.width = "100%";
+            div.style.height = "100%";
+            container.appendChild(div);
+
+            // create GMap and shuffle elements
+            var center = this.map.getCenter();
+            mapObject = new google.maps.Map(div, {
+                center: center ?
+                    new google.maps.LatLng(center.lat, center.lon) :
+                    new google.maps.LatLng(0, 0),
+                zoom: this.map.getZoom() || 0,
+                mapTypeId: this.type,
+                disableDefaultUI: true,
+                keyboardShortcuts: false,
+                draggable: false,
+                disableDoubleClickZoom: true,
+                scrollwheel: false,
+                streetViewControl: false
+            });
+            
+            // cache elements for use by any other google layers added to
+            // this same map
+            cache = {
+                mapObject: mapObject,
+                count: 1
+            };
+            OpenLayers.Layer.Google.cache[this.map.id] = cache;
+            this.repositionListener = google.maps.event.addListenerOnce(
+                mapObject, 
+                "center_changed", 
+                OpenLayers.Function.bind(this.repositionMapElements, this)
+            );
+        }
+        this.mapObject = mapObject;
+        this.setGMapVisibility(this.visibility);
+    },
+    
+    /**
+     * Method: repositionMapElements
+     *
+     * Waits until powered by and terms of use elements are available and then
+     * moves them so they are clickable.
+     */
+    repositionMapElements: function() {
+
+        // This is the first time any Google layer in this mapObject has been
+        // made visible.  The mapObject needs to know the container size.
+        google.maps.event.trigger(this.mapObject, "resize");
+        
+        var div = this.mapObject.getDiv().firstChild;
+        if (!div || div.childNodes.length < 3) {
+            this.repositionTimer = window.setTimeout(
+                OpenLayers.Function.bind(this.repositionMapElements, this),
+                250
+            );
+            return false;
+        }
+
+        var cache = OpenLayers.Layer.Google.cache[this.map.id];
+        var container = this.map.viewPortDiv;
+        
+        // move the Map Data popup to the container, if any
+        while (div.lastChild.style.display == "none") {
+            container.appendChild(div.lastChild);
+        }
+
+        // move the ToS and branding stuff up to the container div
+        var termsOfUse = div.lastChild;
+        container.appendChild(termsOfUse);
+        termsOfUse.style.zIndex = "1100";
+        termsOfUse.style.bottom = "";
+        termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
+        termsOfUse.style.display = "";
+        cache.termsOfUse = termsOfUse;
+
+        var poweredBy = div.lastChild;
+        container.appendChild(poweredBy);
+        poweredBy.style.zIndex = "1100";
+        poweredBy.style.bottom = "";
+        poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
+        poweredBy.style.display = "";
+        cache.poweredBy = poweredBy;
+
+        this.setGMapVisibility(this.visibility);
+
+    },
+
+    /**
+     * APIMethod: onMapResize
+     */
+    onMapResize: function() {
+        if (this.visibility) {
+            google.maps.event.trigger(this.mapObject, "resize");
+        } else {
+            if (!this._resized) {
+                var layer = this;
+                google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
+                    delete layer._resized;
+                    google.maps.event.trigger(layer.mapObject, "resize");
+                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
+                });
+            }
+            this._resized = true;
+        }
+    },
+
+    /**
+     * Method: setGMapVisibility
+     * Display the GMap container and associated elements.
+     * 
+     * Parameters:
+     * visible - {Boolean} Display the GMap elements.
+     */
+    setGMapVisibility: function(visible) {
+        var cache = OpenLayers.Layer.Google.cache[this.map.id];
+        if (cache) {
+            var type = this.type;
+            var layers = this.map.layers;
+            var layer;
+            for (var i=layers.length-1; i>=0; --i) {
+                layer = layers[i];
+                if (layer instanceof OpenLayers.Layer.Google &&
+                            layer.visibility === true && layer.inRange === true) {
+                    type = layer.type;
+                    visible = true;
+                    break;
+                }
+            }
+            var container = this.mapObject.getDiv();
+            if (visible === true) {
+                this.mapObject.setMapTypeId(type);                
+                container.style.left = "";
+                if (cache.termsOfUse && cache.termsOfUse.style) {
+                    cache.termsOfUse.style.left = "";
+                    cache.termsOfUse.style.display = "";
+                    cache.poweredBy.style.display = "";            
+                }
+                cache.displayed = this.id;
+            } else {
+                delete cache.displayed;
+                container.style.left = "-9999px";
+                if (cache.termsOfUse && cache.termsOfUse.style) {
+                    cache.termsOfUse.style.display = "none";
+                    // move ToU far to the left in addition to setting
+                    // display to "none", because at the end of the GMap
+                    // load sequence, display: none will be unset and ToU
+                    // would be visible after loading a map with a google
+                    // layer that is initially hidden. 
+                    cache.termsOfUse.style.left = "-9999px";
+                    cache.poweredBy.style.display = "none";
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method: getMapContainer
+     * 
+     * Returns:
+     * {DOMElement} the GMap container's div
+     */
+    getMapContainer: function() {
+        return this.mapObject.getDiv();
+    },
+    
+  //
+  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
+  //
+
+    /**
+     * APIMethod: getMapObjectBoundsFromOLBounds
+     * 
+     * Parameters:
+     * olBounds - {<OpenLayers.Bounds>}
+     * 
+     * Returns:
+     * {Object} A MapObject Bounds, translated from olBounds
+     *          Returns null if null value is passed in
+     */
+    getMapObjectBoundsFromOLBounds: function(olBounds) {
+        var moBounds = null;
+        if (olBounds != null) {
+            var sw = this.sphericalMercator ? 
+              this.inverseMercator(olBounds.bottom, olBounds.left) : 
+              new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
+            var ne = this.sphericalMercator ? 
+              this.inverseMercator(olBounds.top, olBounds.right) : 
+              new OpenLayers.LonLat(olBounds.top, olBounds.right);
+            moBounds = new google.maps.LatLngBounds(
+                new google.maps.LatLng(sw.lat, sw.lon),
+                new google.maps.LatLng(ne.lat, ne.lon)
+            );
+        }
+        return moBounds;
+    },
+
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // LonLat - Pixel Translation
+  
+    /**
+     * APIMethod: getMapObjectLonLatFromMapObjectPixel
+     * 
+     * Parameters:
+     * moPixel - {Object} MapObject Pixel format
+     * 
+     * Returns:
+     * {Object} MapObject LonLat translated from MapObject Pixel
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        var size = this.map.getSize();
+        var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
+        var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
+        var res = this.map.getResolution();
+
+        var delta_x = moPixel.x - (size.w / 2);
+        var delta_y = moPixel.y - (size.h / 2);
+    
+        var lonlat = new OpenLayers.LonLat(
+            lon + delta_x * res,
+            lat - delta_y * res
+        ); 
+
+        if (this.wrapDateLine) {
+            lonlat = lonlat.wrapDateLine(this.maxExtent);
+        }
+        return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
+    },
+
+    /**
+     * APIMethod: getMapObjectPixelFromMapObjectLonLat
+     * 
+     * Parameters:
+     * moLonLat - {Object} MapObject LonLat format
+     * 
+     * Returns:
+     * {Object} MapObject Pixel transtlated from MapObject LonLat
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
+        var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
+        var res = this.map.getResolution();
+        var extent = this.map.getExtent();
+        var px = new OpenLayers.Pixel(
+            (1/res * (lon - extent.left)),
+            (1/res * (extent.top - lat))
+        );    
+        return this.getMapObjectPixelFromXY(px.x, px.y);
+    },
+
+  
+    /** 
+     * APIMethod: setMapObjectCenter
+     * Set the mapObject to the specified center and zoom
+     * 
+     * Parameters:
+     * center - {Object} MapObject LonLat format
+     * zoom - {int} MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
+            var mapContainer = this.getMapContainer();
+            google.maps.event.addListenerOnce(
+                this.mapObject, 
+                "idle", 
+                function() {
+                    mapContainer.style.visibility = "";
+                }
+            );
+            mapContainer.style.visibility = "hidden";
+        }
+        this.mapObject.setOptions({
+            center: center,
+            zoom: zoom
+        });
+    },
+   
+    
+  // Bounds
+  
+    /** 
+     * APIMethod: getMapObjectZoomFromMapObjectBounds
+     * 
+     * Parameters:
+     * moBounds - {Object} MapObject Bounds format
+     * 
+     * Returns:
+     * {Object} MapObject Zoom for specified MapObject Bounds
+     */
+    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
+        return this.mapObject.getBoundsZoomLevel(moBounds);
+    },
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * APIMethod: getMapObjectLonLatFromLonLat
+     * 
+     * Parameters:
+     * lon - {Float}
+     * lat - {Float}
+     * 
+     * Returns:
+     * {Object} MapObject LonLat built from lon and lat params
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        var gLatLng;
+        if(this.sphericalMercator) {
+            var lonlat = this.inverseMercator(lon, lat);
+            gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
+        } else {
+            gLatLng = new google.maps.LatLng(lat, lon);
+        }
+        return gLatLng;
+    },
+    
+  // Pixel
+    
+    /**
+     * APIMethod: getMapObjectPixelFromXY
+     * 
+     * Parameters:
+     * x - {Integer}
+     * y - {Integer}
+     * 
+     * Returns:
+     * {Object} MapObject Pixel from x and y parameters
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new google.maps.Point(x, y);
+    },
+        
+    /**
+     * APIMethod: destroy
+     * Clean up this layer.
+     */
+    destroy: function() {
+        if (this.repositionListener) {
+            google.maps.event.removeListener(this.repositionListener);
+        }
+        if (this.repositionTimer) {
+            window.clearTimeout(this.repositionTimer);
+        }
+        OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments);
+    }
+    
+};
+/* ======================================================================
+    OpenLayers/Marker/Box.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Marker.js
+ */
+
+/**
+ * Class: OpenLayers.Marker.Box
+ *
+ * Inherits from:
+ *  - <OpenLayers.Marker> 
+ */
+OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {
+
+    /** 
+     * Property: bounds 
+     * {<OpenLayers.Bounds>} 
+     */
+    bounds: null,
+
+    /** 
+     * Property: div 
+     * {DOMElement} 
+     */
+    div: null,
+    
+    /** 
+     * Constructor: OpenLayers.Marker.Box
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} 
+     * borderColor - {String} 
+     * borderWidth - {int} 
+     */
+    initialize: function(bounds, borderColor, borderWidth) {
+        this.bounds = bounds;
+        this.div    = OpenLayers.Util.createDiv();
+        this.div.style.overflow = 'hidden';
+        this.events = new OpenLayers.Events(this, this.div, null);
+        this.setBorder(borderColor, borderWidth);
+    },
+
+    /**
+     * Method: destroy 
+     */    
+    destroy: function() {
+
+        this.bounds = null;
+        this.div = null;
+
+        OpenLayers.Marker.prototype.destroy.apply(this, arguments);
+    },
+
+    /** 
+     * Method: setBorder
+     * Allow the user to change the box's color and border width
+     * 
+     * Parameters:
+     * color - {String} Default is "red"
+     * width - {int} Default is 2
+     */
+    setBorder: function (color, width) {
+        if (!color) {
+            color = "red";
+        }
+        if (!width) {
+            width = 2;
+        }
+        this.div.style.border = width + "px solid " + color;
+    },
+    
+    /** 
+    * Method: draw
+    * 
+    * Parameters:
+    * px - {<OpenLayers.Pixel>} 
+    * sz - {<OpenLayers.Size>} 
+    * 
+    * Returns: 
+    * {DOMElement} A new DOM Image with this marker´s icon set at the 
+    *         location passed-in
+    */
+    draw: function(px, sz) {
+        OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
+        return this.div;
+    }, 
+
+    /**
+     * Method: onScreen
+     * 
+     * Rreturn:
+     * {Boolean} Whether or not the marker is currently visible on screen.
+     */
+    onScreen:function() {
+        var onScreen = false;
+        if (this.map) {
+            var screenBounds = this.map.getExtent();
+            onScreen = screenBounds.containsBounds(this.bounds, true, true);
+        }    
+        return onScreen;
+    },
+    
+    /**
+     * Method: display
+     * Hide or show the icon
+     * 
+     * Parameters:
+     * display - {Boolean} 
+     */
+    display: function(display) {
+        this.div.style.display = (display) ? "" : "none";
+    },
+
+    CLASS_NAME: "OpenLayers.Marker.Box"
+});
+
+/* ======================================================================
+    OpenLayers/Layer/MapGuide.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MapGuide
+ * Instances of OpenLayers.Layer.MapGuide are used to display
+ * data from a MapGuide OS instance.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+    /** 
+     * APIProperty: isBaseLayer
+     * {Boolean} Treat this layer as a base layer.  Default is true.
+     **/
+    isBaseLayer: true,
+    
+    /**
+     * APIProperty: 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. 
+     **/
+    singleTile: false,
+    
+    /** 
+     * APIProperty: useOverlay
+     * {Boolean} flag to indicate if the layer should be retrieved using
+     * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.
+     **/
+    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
+     */
+    TILE_PARAMS: {
+         operation: 'GETTILEIMAGE',
+         version: '1.2.0'
+    },
+
+    /**
+     * Constant: SINGLE_TILE_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for untiled layer
+     */
+    SINGLE_TILE_PARAMS: {
+        operation: 'GETMAPIMAGE',
+        format: 'PNG',
+        locale: 'en',
+        clip: '1',
+        version: '1.0.0'
+    },
+    
+    /**
+     * Constant: OVERLAY_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs for untiled layer
+     */
+    OVERLAY_PARAMS: {
+        operation: 'GETDYNAMICMAPOVERLAYIMAGE',
+        format: 'PNG',
+        locale: 'en',
+        clip: '1',
+        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
+     **/
+    defaultSize: new OpenLayers.Size(300,300),
+
+    /**
+     * Constructor: OpenLayers.Layer.MapGuide
+     * Create a new Mapguide layer, either tiled or untiled.  
+     *
+     * For tiled layers, the 'groupName' and 'mapDefinition' values 
+     * must be specified as parameters in the constructor.
+     *
+     * For untiled base layers, specify either combination of 'mapName' and
+     * 'session', or 'mapDefinition' and 'locale'.  
+     *
+     * 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, 
+     * so these must be adjusted accordingly in your application.  
+     * See the MapGuide example for how to set these values for MGOS.
+     *
+     * Parameters:
+     * name - {String} Name of the layer displayed in the interface
+     * url - {String} Location of the MapGuide mapagent executable
+     *            (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)
+     * params - {Object} hashtable of additional parameters to use. Some
+     *     parameters may require additional code on the server. The ones that
+     *     you may want to use are: 
+     *   - mapDefinition - {String} The MapGuide resource definition
+     *            (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)
+     *   - locale - Locale setting 
+     *            (for untiled overlays layers only)
+     *   - mapName - {String} Name of the map as stored in the MapGuide session.
+     *          (for untiled layers with a session parameter only)
+     *   - session - { String} MapGuide session ID 
+     *            (for untiled overlays layers only)
+     *   - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only
+     *   - format - Image format to be returned (for untiled overlay layers only)
+     *   - showLayers - {String} A comma separated list of GUID's for the
+     *       layers to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideLayers - {String} A comma separated list of GUID's for the
+     *       layers to hide eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - showGroups - {String} A comma separated list of GUID's for the
+     *       groups to display eg: 'cvc-xcv34,453-345-345sdf'.
+     *   - hideGroups - {String} A comma separated list of GUID's for the
+     *       groups to hide eg: 'cvc-xcv34,453-345-345sdf'
+     *   - selectionXml - {String} A selection xml string Some server plumbing
+     *       is required to read such a value.
+     * options - {Ojbect} Hashtable of extra options to tag onto the layer; 
+     *          will vary depending if tiled or untiled maps are being requested
+     */
+    initialize: function(name, url, params, options) {
+        
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
+        
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay
+        if (options == null || options.isBaseLayer == null) {
+            this.isBaseLayer = ((this.transparent != "true") && 
+                                (this.transparent != true));
+        }
+
+        if (options && options.useOverlay!=null) {
+          this.useOverlay = options.useOverlay;
+        }
+        
+        //initialize for untiled layers
+        if (this.singleTile) {
+          if (this.useOverlay) {
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.OVERLAY_PARAMS
+                           );
+            if (!this.useAsyncOverlay) {
+              this.params.version = "1.0.0";
+            }
+          } else {
+            OpenLayers.Util.applyDefaults(
+                           this.params,
+                           this.SINGLE_TILE_PARAMS
+                           );
+          }         
+        } else {
+            //initialize for tiled layers
+            if (this.useHttpTile) {
+                OpenLayers.Util.applyDefaults(
+                               this.params,
+                               this.FOLDER_PARAMS
+                               );
+            } else {
+                OpenLayers.Util.applyDefaults(
+                               this.params,
+                               this.TILE_PARAMS
+                               );
+            }
+            this.setTileSize(this.defaultSize); 
+        }
+    },
+
+    /**
+     * Method: clone
+     * Create a clone of this layer
+     *
+     * Returns:
+     * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer
+     */
+    clone: function (obj) {
+      if (obj == null) {
+            obj = new OpenLayers.Layer.MapGuide(this.name,
+                                           this.url,
+                                           this.params,
+                                           this.getOptions());
+      }
+      //get all additions from superclasses
+      obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+      return obj;
+    },
+
+    /**
+     * Method: addTile
+     * Creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+     */
+    addTile:function(bounds,position) {
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                         null, this.tileSize);
+    },
+
+    /**
+     * Method: getURL
+     * Return a query string for this layer
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
+     *                                for the request
+     *
+     * Returns:
+     * {String} A string with the layer's url and parameters and also 
+     *          the passed-in bounds and appropriate tile size specified 
+     *          as parameters.
+     */
+    getURL: function (bounds) {
+        var url;
+        var center = bounds.getCenterLonLat();
+        var mapSize = this.map.getSize();
+
+        if (this.singleTile) {
+          //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with
+          //dynamic map parameters
+          var params = {
+            setdisplaydpi: OpenLayers.DOTS_PER_INCH,
+            setdisplayheight: mapSize.h*this.ratio,
+            setdisplaywidth: mapSize.w*this.ratio,
+            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 {
+
+          //tiled version
+          var currentRes = this.map.getResolution();
+          var colidx = Math.floor((bounds.left-this.maxExtent.left)/currentRes);
+          colidx = Math.round(colidx/this.tileSize.w);
+          var rowidx = Math.floor((this.maxExtent.top-bounds.top)/currentRes);
+          rowidx = Math.round(rowidx/this.tileSize.h);
+
+          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;
+    },
+
+    /**
+     * Method: getFullRequestString
+     * getFullRequestString on MapGuide layers is special, because we 
+     * do a regular expression replace on ',' in parameters to '+'.
+     * This is why it is subclassed here.
+     *
+     * Parameters:
+     * altUrl - {String} Alternative base URL to use.
+     *
+     * Returns:
+     * {String} A string with the layer's url appropriately encoded for MapGuide
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        // use layer's url unless altUrl passed in
+        var url = (altUrl == null) ? this.url : altUrl;
+        
+        // if url is not a string, it should be an array of strings, 
+        //  in which case we will randomly select one of them in order
+        //  to evenly distribute requests to different urls.
+        if (typeof url == "object") {
+            url = url[Math.floor(Math.random()*url.length)];
+        }   
+        // requestString always starts with url
+        var requestString = url;        
+
+        // create a new params hashtable with all the layer params and the 
+        // new params together. then convert to string
+        var allParams = OpenLayers.Util.extend({}, this.params);
+        allParams = OpenLayers.Util.extend(allParams, newParams);
+        // ignore parameters that are already in the url search string
+        var urlParams = OpenLayers.Util.upperCaseObject(
+                            OpenLayers.Util.getParameters(url));
+        for(var key in allParams) {
+            if(key.toUpperCase() in urlParams) {
+                delete allParams[key];
+            }
+        }
+        var paramsString = OpenLayers.Util.getParameterString(allParams);
+        
+        /* MapGuide needs '+' seperating things like bounds/height/width.
+           Since typically this is URL encoded, we use a slight hack: we
+           depend on the list-like functionality of getParameterString to
+           leave ',' only in the case of list items (since otherwise it is
+           encoded) then do a regular expression replace on the , characters
+           to '+' */
+        paramsString = paramsString.replace(/,/g, "+");
+        
+        if (paramsString != "") {
+            var lastServerChar = url.charAt(url.length - 1);
+            if ((lastServerChar == "&") || (lastServerChar == "?")) {
+                requestString += paramsString;
+            } else {
+                if (url.indexOf('?') == -1) {
+                    //serverPath has no ? -- add one
+                    requestString += '?' + paramsString;
+                } else {
+                    //serverPath contains ?, so must already have paramsString at the end
+                    requestString += '&' + paramsString;
+                }
+            }
+        }
+        return requestString;
+    },
+
+     /** 
+     * Method: 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  
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bound>}
+     * extent - {<OpenLayers.Bounds>}
+     * resolution - {Number}
+     *
+     * Returns:
+     * Object containing properties tilelon, tilelat, tileoffsetlat,
+     * tileoffsetlat, tileoffsetx, tileoffsety
+     */
+    calculateGridLayout: function(bounds, extent, resolution) {
+        var tilelon = resolution * this.tileSize.w;
+        var tilelat = resolution * this.tileSize.h;
+        
+        var offsetlon = bounds.left - extent.left;
+        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
+        var tilecolremain = offsetlon/tilelon - tilecol;
+        var tileoffsetx = -tilecolremain * this.tileSize.w;
+        var tileoffsetlon = extent.left + tilecol * tilelon;
+        
+        var offsetlat = extent.top - bounds.top + tilelat; 
+        var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
+        var tilerowremain = tilerow - offsetlat/tilelat;
+        var tileoffsety = tilerowremain * this.tileSize.h;
+        var tileoffsetlat = extent.top - tilelat*tilerow;
+        
+        return { 
+          tilelon: tilelon, tilelat: tilelat,
+          tileoffsetlon: tileoffsetlon, tileoffsetlat: tileoffsetlat,
+          tileoffsetx: tileoffsetx, tileoffsety: tileoffsety
+        };
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.MapGuide"
+});
+/* ======================================================================
+    OpenLayers/Control/Measure.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Control.Measure
+ * Allows for drawing of features for measurements.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * 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.
+     *
+     * Supported control event types (in addition to those from <OpenLayers.Control>):
+     * 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
+     *      measurement sketch or if the <immediate> property is true and the
+     *      measurement sketch is modified.  Listeners receive an event with measure,
+     *      units, order, and geometry.
+     */
+    EVENT_TYPES: ['measure', 'measurepartial'],
+
+    /**
+     * APIProperty: handlerOptions
+     * {Object} Used to set non-default properties on the control's handler
+     */
+    handlerOptions: null,
+    
+    /**
+     * Property: callbacks
+     * {Object} The functions that are sent to the handler for callback
+     */
+    callbacks: null,
+    
+    /**
+     * APIProperty: displaySystem
+     * {String} Display system for output measurements.  Supported values
+     *     are 'english', 'metric', and 'geographic'.  Default is 'metric'.
+     */
+    displaySystem: 'metric',
+    
+    /**
+     * APIProperty: geodesic
+     * {Boolean} Calculate geodesic metrics instead of planar metrics.  This
+     *     requires that geometries can be transformed into Geographic/WGS84
+     *     (if that is not already the map projection).  Default is false.
+     */
+    geodesic: false,
+    
+    /**
+     * APIProperty: displaySystemUnits
+     * {Object} Units for various measurement systems.  Values are arrays
+     *     of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
+     *     order of length.
+     */
+    displaySystemUnits: {
+        geographic: ['dd'],
+        english: ['mi', 'ft', 'in'],
+        metric: ['km', 'm']
+    },
+
+    /**
+     * Property: delay
+     * {Number} Number of milliseconds between clicks before the event is
+     *     considered a double-click.  The "measurepartial" event will not
+     *     be triggered if the sketch is completed within this time.  This
+     *     is required for IE where creating a browser reflow (if a listener
+     *     is modifying the DOM by displaying the measurement values) messes
+     *     with the dblclick listener in the sketch handler.
+     */
+    partialDelay: 300,
+
+    /**
+     * Property: delayedTrigger
+     * {Number} Timeout id of trigger for measurepartial.
+     */
+    delayedTrigger: null,
+
+    /**
+     * APIProperty: persist
+     * {Boolean} Keep the temporary measurement sketch drawn after the
+     *     measurement is complete.  The geometry will persist until a new
+     *     measurement is started, the control is deactivated, or <cancel> is
+     *     called.
+     */
+    persist: false,
+
+    /**
+     * APIProperty: immediate
+     * {Boolean} Activates the immediate measurement so that the "measurepartial"
+     *     event is also fired once the measurement sketch is modified.
+     *     Default is false.
+     */
+    immediate : false,
+
+    /**
+     * Constructor: OpenLayers.Control.Measure
+     * 
+     * Parameters:
+     * handler - {<OpenLayers.Handler>} 
+     * options - {Object} 
+     */
+    initialize: function(handler, options) {
+        // concatenate events specific to measure with those from the base
+        this.EVENT_TYPES =
+            OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(
+            OpenLayers.Control.prototype.EVENT_TYPES
+        );
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+        var callbacks = {done: this.measureComplete,
+            point: this.measurePartial};
+        if (this.immediate){
+            callbacks.modify = this.measureImmediate;
+        }
+        this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
+
+        // let the handler options override, so old code that passes 'persist' 
+        // directly to the handler does not need an update
+        this.handlerOptions = OpenLayers.Util.extend(
+            {persist: this.persist}, this.handlerOptions
+        );
+        this.handler = new handler(this, this.callbacks, this.handlerOptions);
+    },
+    
+    /**
+     * APIMethod: deactivate
+     */
+    deactivate: function() {
+        this.cancelDelay();
+        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
+    },
+
+    /**
+     * APIMethod: cancel
+     * Stop the control from measuring.  If <persist> is true, the temporary
+     *     sketch will be erased.
+     */
+    cancel: function() {
+        this.cancelDelay();
+        this.handler.cancel();
+    },
+
+    /**
+     * APIMethod: setImmediate
+     * Sets the <immediate> property. Changes the activity of immediate
+     * measurement.
+     */
+    setImmediate: function(immediate) {
+        this.immediate = immediate;
+        if (this.immediate){
+            this.callbacks.modify = this.measureImmediate;
+        } else {
+            delete this.callbacks.modify;
+        }
+    },
+    
+    /**
+     * Method: updateHandler
+     *
+     * Parameters:
+     * handler - {Function} One of the sketch handler constructors.
+     * options - {Object} Options for the handler.
+     */
+    updateHandler: function(handler, options) {
+        var active = this.active;
+        if(active) {
+            this.deactivate();
+        }
+        this.handler = new handler(this, this.callbacks, options);
+        if(active) {
+            this.activate();
+        }
+    },
+
+    /**
+     * Method: measureComplete
+     * Called when the measurement sketch is done.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     */
+    measureComplete: function(geometry) {
+        this.cancelDelay();
+        this.measure(geometry, "measure");
+    },
+    
+    /**
+     * Method: measurePartial
+     * Called each time a new point is added to the measurement sketch.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>} The last point added.
+     * geometry - {<OpenLayers.Geometry>} The sketch geometry.
+     */
+    measurePartial: function(point, geometry) {
+        this.cancelDelay();
+        geometry = geometry.clone();
+        // when we're wating for a dblclick, we have to trigger measurepartial
+        // after some delay to deal with reflow issues in IE
+        if (this.handler.freehandMode(this.handler.evt)) {
+            // no dblclick in freehand mode
+            this.measure(geometry, "measurepartial");
+        } else {
+            this.delayedTrigger = window.setTimeout(
+                OpenLayers.Function.bind(function() {
+                    this.delayedTrigger = null;
+                    this.measure(geometry, "measurepartial");
+                }, this),
+                this.partialDelay
+            );
+        }
+    },
+
+    /**
+     * Method: measureImmediate
+     * Called each time the measurement sketch is modified.
+     * 
+     * Parameters: point - {<OpenLayers.Geometry.Point>} The point at the
+     * mouseposition. feature - {<OpenLayers.Feature.Vector>} The sketch feature.
+     */
+    measureImmediate : function(point, feature) {
+        if (this.delayedTrigger === null &&
+                                !this.handler.freehandMode(this.handler.evt)) {
+            this.measure(feature.geometry, "measurepartial");
+        }
+    },
+
+    /**
+     * Method: cancelDelay
+     * Cancels the delay measurement that measurePartial began.
+     */
+    cancelDelay: function() {
+        if (this.delayedTrigger !== null) {
+            window.clearTimeout(this.delayedTrigger);
+            this.delayedTrigger = null;
+        }
+    },
+
+    /**
+     * Method: measure
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * eventType - {String}
+     */
+    measure: function(geometry, eventType) {
+        var stat, order;
+        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
+            stat = this.getBestLength(geometry);
+            order = 1;
+        } else {
+            stat = this.getBestArea(geometry);
+            order = 2;
+        }
+        this.events.triggerEvent(eventType, {
+            measure: stat[0],
+            units: stat[1],
+            order: order,
+            geometry: geometry
+        });
+    },
+    
+    /**
+     * Method: getBestArea
+     * Based on the <displaySystem> returns the area of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     area and the units abbreviation.
+     */
+    getBestArea: function(geometry) {
+        var units = this.displaySystemUnits[this.displaySystem];
+        var unit, area;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            area = this.getArea(geometry, unit);
+            if(area > 1) {
+                break;
+            }
+        }
+        return [area, unit];
+    },
+    
+    /**
+     * Method: getArea
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry area in the given units.
+     */
+    getArea: function(geometry, units) {
+        var area, geomUnits;
+        if(this.geodesic) {
+            area = geometry.getGeodesicArea(this.map.getProjectionObject());
+            geomUnits = "m";
+        } else {
+            area = geometry.getArea();
+            geomUnits = this.map.getUnits();
+        }
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
+            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
+        }
+        return area;
+    },
+    
+    /**
+     * Method: getBestLength
+     * Based on the <displaySystem> returns the length of a geometry.
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     *
+     * Returns:
+     * {Array([Float, String])}  Returns a two item array containing the
+     *     length and the units abbreviation.
+     */
+    getBestLength: function(geometry) {
+        var units = this.displaySystemUnits[this.displaySystem];
+        var unit, length;
+        for(var i=0, len=units.length; i<len; ++i) {
+            unit = units[i];
+            length = this.getLength(geometry, unit);
+            if(length > 1) {
+                break;
+            }
+        }
+        return [length, unit];
+    },
+
+    /**
+     * Method: getLength
+     *
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * units - {String} Unit abbreviation
+     *
+     * Returns:
+     * {Float} The geometry length in the given units.
+     */
+    getLength: function(geometry, units) {
+        var length, geomUnits;
+        if(this.geodesic) {
+            length = geometry.getGeodesicLength(this.map.getProjectionObject());
+            geomUnits = "m";
+        } else {
+            length = geometry.getLength();
+            geomUnits = this.map.getUnits();
+        }
+        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
+        if(inPerDisplayUnit) {
+            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
+            length *= (inPerMapUnit / inPerDisplayUnit);
+        }
+        return length;
+    },
+
+    CLASS_NAME: "OpenLayers.Control.Measure"
+});
+/* ======================================================================
+    OpenLayers/Control/DrawFeature.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Feature/Vector.js
+ */
+
+/**
+ * Class: OpenLayers.Control.DrawFeature
+ * The DrawFeature control draws point, line or polygon features on a vector
+ * layer when active.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
+    
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>}
+     */
+    layer: null,
+
+    /**
+     * Property: callbacks
+     * {Object} The functions that are sent to the handler for callback
+     */
+    callbacks: null,
+    
+    /**
+     * Constant: EVENT_TYPES
+     *
+     * Supported event types:
+     * featureadded - Triggered when a feature is added
+     */
+    EVENT_TYPES: ["featureadded"],
+    
+    /**
+     * APIProperty: multi
+     * {Boolean} Cast features to multi-part geometries before passing to the
+     *     layer.  Default is false.
+     */
+    multi: false,
+
+    /**
+     * APIProperty: featureAdded
+     * {Function} Called after each feature is added
+     */
+    featureAdded: function() {},
+
+    /**
+     * APIProperty: handlerOptions
+     * {Object} Used to set non-default properties on the control's handler
+     */
+    handlerOptions: null,
+    
+    /**
+     * Constructor: OpenLayers.Control.DrawFeature
+     * 
+     * Parameters:
+     * layer - {<OpenLayers.Layer.Vector>} 
+     * handler - {<OpenLayers.Handler>} 
+     * options - {Object} 
+     */
+    initialize: function(layer, handler, options) {
+        
+        // concatenate events specific to vector with those from the base
+        this.EVENT_TYPES =
+            OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(
+            OpenLayers.Control.prototype.EVENT_TYPES
+        );
+        
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+        this.callbacks = OpenLayers.Util.extend(
+            {
+                done: this.drawFeature,
+                modify: function(vertex, feature) {
+                    this.layer.events.triggerEvent(
+                        "sketchmodified", {vertex: vertex, feature: feature}
+                    );
+                },
+                create: function(vertex, feature) {
+                    this.layer.events.triggerEvent(
+                        "sketchstarted", {vertex: vertex, feature: feature}
+                    );
+                }
+            },
+            this.callbacks
+        );
+        this.layer = layer;
+        this.handlerOptions = this.handlerOptions || {};
+        if (!("multi" in this.handlerOptions)) {
+            this.handlerOptions.multi = this.multi;
+        }
+        var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
+        if(sketchStyle) {
+            this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(
+                this.handlerOptions.layerOptions,
+                {styleMap: new OpenLayers.StyleMap({"default": sketchStyle})}
+            );
+        }
+        this.handler = new handler(this, this.callbacks, this.handlerOptions);
+    },
+
+    /**
+     * Method: drawFeature
+     */
+    drawFeature: function(geometry) {
+        var feature = new OpenLayers.Feature.Vector(geometry);
+        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"
+});
+/* ======================================================================
+    OpenLayers/Symbolizer/Point.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Symbolizer.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer.Point
+ * A symbolizer used to render point features.
+ */
+OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {
+    
+    /**
+     * APIProperty: strokeColor
+     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
+     *     for red).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeOpacity
+     * {Number} Stroke opacity (0-1).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeWidth
+     * {Number} Pixel stroke width.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeLinecap
+     * {String} Stroke cap type ("butt", "round", or "square").
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * Property: strokeDashstyle
+     * {String} Stroke dash style according to the SLD spec. Note that the
+     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
+     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
+     *     most SLD patterns will render correctly in OpenLayers.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * APIProperty: fillColor
+     * {String} RGB hex fill color (e.g. "#ff0000" for red).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: fillOpacity
+     * {Number} Fill opacity (0-1).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * APIProperty: pointRadius
+     * {Number} Pixel point radius.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * APIProperty: externalGraphic
+     * {String} Url to an external graphic that will be used for rendering 
+     *     points.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicWidth
+     * {Number} Pixel width for sizing an external graphic.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicHeight
+     * {Number} Pixel height for sizing an external graphic.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicOpacity
+     * {Number} Opacity (0-1) for an external graphic.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicXOffset
+     * {Number} Pixel offset along the positive x axis for displacing an 
+     *     external graphic.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicYOffset
+     * {Number} Pixel offset along the positive y axis for displacing an 
+     *     external graphic.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * APIProperty: rotation
+     * {Number} The rotation of a graphic in the clockwise direction about its 
+     *     center point (or any point off center as specified by 
+     *     <graphicXOffset> and <graphicYOffset>).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: graphicName
+     * {String} Named graphic to use when rendering points.  Supported values 
+     *     include "circle", "square", "star", "x", "cross", and "triangle".
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * Constructor: OpenLayers.Symbolizer.Point
+     * Create a symbolizer for rendering points.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new point symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer.Point"
+    
+});
+
+/* ======================================================================
+    OpenLayers/Symbolizer/Line.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Symbolizer.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer.Line
+ * A symbolizer used to render line features.
+ */
+OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {
+
+    /**
+     * APIProperty: strokeColor
+     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
+     *     for red).  
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeOpacity
+     * {Number} Stroke opacity (0-1).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeWidth
+     * {Number} Pixel stroke width.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeLinecap
+     * {String} Stroke cap type ("butt", "round", or "square").
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * Property: strokeDashstyle
+     * {String} Stroke dash style according to the SLD spec. Note that the
+     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
+     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
+     *     most SLD patterns will render correctly in OpenLayers.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * Constructor: OpenLayers.Symbolizer.Line
+     * Create a symbolizer for rendering lines.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new line symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer.Line"
+    
+});
+
+/* ======================================================================
+    OpenLayers/Symbolizer/Polygon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Symbolizer.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer.Polygon
+ * A symbolizer used to render line features.
+ */
+OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {
+    
+    /**
+     * APIProperty: strokeColor
+     * {String} Color for line stroke.  This is a RGB hex value (e.g. "#ff0000"
+     *     for red).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeOpacity
+     * {Number} Stroke opacity (0-1).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeWidth
+     * {Number} Pixel stroke width.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: strokeLinecap
+     * {String} Stroke cap type ("butt", "round", or "square").
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * Property: strokeDashstyle
+     * {String} Stroke dash style according to the SLD spec. Note that the
+     *     OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot",
+     *     "longdash", "longdashdot", or "solid") will not work in SLD, but
+     *     most SLD patterns will render correctly in OpenLayers.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * APIProperty: fillColor
+     * {String} RGB hex fill color (e.g. "#ff0000" for red).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * APIProperty: fillOpacity
+     * {Number} Fill opacity (0-1).
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * Constructor: OpenLayers.Symbolizer.Polygon
+     * Create a symbolizer for rendering polygons.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new polygon symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer.Polygon"
+    
+});
+
+/* ======================================================================
+    OpenLayers/Symbolizer/Text.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Symbolizer.js
+ */
+
+/**
+ * Class: OpenLayers.Symbolizer.Text
+ * A symbolizer used to render text labels for features.
+ */
+OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {
+    
+    /** 
+     * APIProperty: label
+     * {String} The text for the label.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /** 
+     * APIProperty: fontFamily
+     * {String} The font family for the label.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /** 
+     * APIProperty: fontSize
+     * {String} The font size for the label.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /** 
+     * APIProperty: fontWeight
+     * {String} The font weight for the label.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+    
+    /**
+     * Property: fontStyle
+     * {String} The font style for the label.
+     * 
+     * No default set here.  Use OpenLayers.Renderer.defaultRenderer for defaults.
+     */
+
+    /**
+     * Constructor: OpenLayers.Symbolizer.Text
+     * Create a symbolizer for rendering text labels.
+     *
+     * Parameters:
+     * config - {Object} An object containing properties to be set on the 
+     *     symbolizer.  Any documented symbolizer property can be set at 
+     *     construction.
+     *
+     * Returns:
+     * A new text symbolizer.
+     */
+    initialize: function(config) {
+        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
+    },
+    
+    CLASS_NAME: "OpenLayers.Symbolizer.Text"
+    
+});
+
+/* ======================================================================
+    OpenLayers/Rule.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Style.js
+ * @requires OpenLayers/Symbolizer/Point.js
+ * @requires OpenLayers/Symbolizer/Line.js
+ * @requires OpenLayers/Symbolizer/Polygon.js
+ * @requires OpenLayers/Symbolizer/Text.js
+ * @requires OpenLayers/Symbolizer/Raster.js
+ */
+
+/**
+ * Class: OpenLayers.Rule
+ * This class represents an SLD Rule, as being used for rule-based SLD styling.
+ */
+OpenLayers.Rule = OpenLayers.Class({
+    
+    /**
+     * Property: id
+     * {String} A unique id for this session.
+     */
+    id: null,
+    
+    /**
+     * APIProperty: name
+     * {String} name of this rule
+     */
+    name: null,
+    
+    /**
+     * Property: title
+     * {String} Title of this rule (set if included in SLD)
+     */
+    title: null,
+    
+    /**
+     * Property: description
+     * {String} Description of this rule (set if abstract is included in SLD)
+     */
+    description: null,
+
+    /**
+     * Property: context
+     * {Object} An optional object with properties that the rule should be
+     * evaluated against. If no context is specified, feature.attributes will
+     * be used.
+     */
+    context: null,
+    
+    /**
+     * Property: filter
+     * {<OpenLayers.Filter>} Optional filter for the rule.
+     */
+    filter: null,
+
+    /**
+     * Property: elseFilter
+     * {Boolean} Determines whether this rule is only to be applied only if
+     * no other rules match (ElseFilter according to the SLD specification). 
+     * Default is false.  For instances of OpenLayers.Rule, if elseFilter is
+     * false, the rule will always apply.  For subclasses, the else property is 
+     * ignored.
+     */
+    elseFilter: false,
+    
+    /**
+     * Property: symbolizer
+     * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
+     * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
+     * latter if useful if it is required to style e.g. vertices of a line
+     * with a point symbolizer. Note, however, that this is not implemented
+     * yet in OpenLayers, but it is the way how symbolizers are defined in
+     * SLD.
+     */
+    symbolizer: null,
+    
+    /**
+     * Property: symbolizers
+     * {Array} Collection of symbolizers associated with this rule.  If 
+     *     provided at construction, the symbolizers array has precedence
+     *     over the deprecated symbolizer property.  Note that multiple 
+     *     symbolizers are not currently supported by the vector renderers.
+     *     Rules with multiple symbolizers are currently only useful for
+     *     maintaining elements in an SLD document.
+     */
+    symbolizers: null,
+    
+    /**
+     * APIProperty: minScaleDenominator
+     * {Number} or {String} minimum scale at which to draw the feature.
+     * In the case of a String, this can be a combination of text and
+     * propertyNames in the form "literal ${propertyName}"
+     */
+    minScaleDenominator: null,
+
+    /**
+     * APIProperty: maxScaleDenominator
+     * {Number} or {String} maximum scale at which to draw the feature.
+     * In the case of a String, this can be a combination of text and
+     * propertyNames in the form "literal ${propertyName}"
+     */
+    maxScaleDenominator: null,
+    
+    /** 
+     * Constructor: OpenLayers.Rule
+     * Creates a Rule.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *           rule
+     * 
+     * Returns:
+     * {<OpenLayers.Rule>}
+     */
+    initialize: function(options) {
+        this.symbolizer = {};
+        OpenLayers.Util.extend(this, options);
+        if (this.symbolizers) {
+            delete this.symbolizer;
+        }
+        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
+    },
+
+    /** 
+     * APIMethod: destroy
+     * nullify references to prevent circular references and memory leaks
+     */
+    destroy: function() {
+        for (var i in this.symbolizer) {
+            this.symbolizer[i] = null;
+        }
+        this.symbolizer = null;
+        delete this.symbolizers;
+    },
+    
+    /**
+     * APIMethod: evaluate
+     * evaluates this rule for a specific feature
+     * 
+     * Parameters:
+     * feature - {<OpenLayers.Feature>} feature to apply the rule to.
+     * 
+     * Returns:
+     * {Boolean} true if the rule applies, false if it does not.
+     * This rule is the default rule and always returns true.
+     */
+    evaluate: function(feature) {
+        var context = this.getContext(feature);
+        var applies = true;
+
+        if (this.minScaleDenominator || this.maxScaleDenominator) {
+            var scale = feature.layer.map.getScale();
+        }
+        
+        // check if within minScale/maxScale bounds
+        if (this.minScaleDenominator) {
+            applies = scale >= OpenLayers.Style.createLiteral(
+                    this.minScaleDenominator, context);
+        }
+        if (applies && this.maxScaleDenominator) {
+            applies = scale < OpenLayers.Style.createLiteral(
+                    this.maxScaleDenominator, context);
+        }
+        
+        // check if optional filter applies
+        if(applies && this.filter) {
+            // feature id filters get the feature, others get the context
+            if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
+                applies = this.filter.evaluate(feature);
+            } else {
+                applies = this.filter.evaluate(context);
+            }
+        }
+
+        return applies;
+    },
+    
+    /**
+     * Method: getContext
+     * Gets the context for evaluating this rule
+     * 
+     * Paramters:
+     * feature - {<OpenLayers.Feature>} feature to take the context from if
+     *           none is specified.
+     */
+    getContext: function(feature) {
+        var context = this.context;
+        if (!context) {
+            context = feature.attributes || feature.data;
+        }
+        if (typeof this.context == "function") {
+            context = this.context(feature);
+        }
+        return context;
+    },
+    
+    /**
+     * APIMethod: clone
+     * Clones this rule.
+     * 
+     * Returns:
+     * {<OpenLayers.Rule>} Clone of this rule.
+     */
+    clone: function() {
+        var options = OpenLayers.Util.extend({}, this);
+        if (this.symbolizers) {
+            // clone symbolizers
+            var len = this.symbolizers.length;
+            options.symbolizers = new Array(len);
+            for (var i=0; i<len; ++i) {
+                options.symbolizers[i] = this.symbolizers[i].clone();
+            }
+        } else {
+            // clone symbolizer
+            options.symbolizer = {};
+            var value, type;
+            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"
+});
+/* ======================================================================
+    OpenLayers/Handler/Hover.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Hover
+ * The hover handler is to be used to emulate mouseovers on objects
+ *      on the map that aren't DOM elements. For example one can use
+ *      this handler to send WMS/GetFeatureInfo requests as the user
+ *      moves the mouve over the map.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler> 
+ */
+OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * APIProperty: delay
+     * {Integer} - Number of milliseconds between mousemoves before
+     *      the event is considered a hover. Default is 500.
+     */
+    delay: 500,
+    
+    /**
+     * APIProperty: pixelTolerance
+     * {Integer} - Maximum number of pixels between mousemoves for
+     *      an event to be considered a hover. Default is null.
+     */
+    pixelTolerance: null,
+
+    /**
+     * APIProperty: stopMove
+     * {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
+     *      in pixels.
+     */
+    px: null,
+
+    /**
+     * Property: timerId
+     * {Number} - The id of the timer.
+     */
+    timerId: null,
+ 
+    /**
+     * Constructor: OpenLayers.Handler.Hover
+     * Construct a hover handler.
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} The control that initialized this
+     *     handler.  The control is assumed to have a valid map property; that
+     *     map is used in the handler's own setMap method.
+     * callbacks - {Object} An object 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.
+     */
+    initialize: function(control, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: mousemove
+     * Called when the mouse moves on the map.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mousemove: function(evt) {
+        if(this.passesTolerance(evt.xy)) {
+            this.clearTimer();
+            this.callback('move', [evt]);
+            this.px = evt.xy;
+            // clone the evt so original properties can be accessed even
+            // if the browser deletes them during the delay
+            evt = OpenLayers.Util.extend({}, evt);
+            this.timerId = window.setTimeout(
+                OpenLayers.Function.bind(this.delayedCall, this, evt),
+                this.delay
+            );
+        }
+        return !this.stopMove;
+    },
+
+    /**
+     * Method: mouseout
+     * Called when the mouse goes out of the map.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    mouseout: function(evt) {
+        if (OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            this.clearTimer();
+            this.callback('move', [evt]);
+        }
+        return true;
+    },
+
+    /**
+     * Method: passesTolerance
+     * Determine whether the mouse move is within the optional pixel tolerance.
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {Boolean} The mouse move is within the pixel tolerance.
+     */
+    passesTolerance: function(px) {
+        var passes = true;
+        if(this.pixelTolerance && this.px) {
+            var dpx = Math.sqrt(
+                Math.pow(this.px.x - px.x, 2) +
+                Math.pow(this.px.y - px.y, 2)
+            );
+            if(dpx < this.pixelTolerance) {
+                passes = false;
+            }
+        }
+        return passes;
+    },
+
+    /**
+     * Method: clearTimer
+     * Clear the timer and set <timerId> to null.
+     */
+    clearTimer: function() {
+        if(this.timerId != null) {
+            window.clearTimeout(this.timerId);
+            this.timerId = null;
+        }
+    },
+
+    /**
+     * Method: delayedCall
+     * Triggers pause callback.
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>}
+     */
+    delayedCall: function(evt) {
+        this.callback('pause', [evt]);
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Deactivate the handler.
+     *
+     * Returns:
+     * {Boolean} The handler was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.clearTimer();
+            deactivated = true;
+        }
+        return deactivated;
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Hover"
+});
+/* ======================================================================
+    OpenLayers/Format/WFSDescribeFeatureType.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/XML.js
+ *
+ * Class: OpenLayers.Format.WFSDescribeFeatureType
+ * Read WFS DescribeFeatureType response
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(
+    OpenLayers.Format.XML, {
+    
+    /**
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.
+     */
+    namespaces: {
+        xsd: "http://www.w3.org/2001/XMLSchema"
+    },
+    
+    /**
+     * Constructor: OpenLayers.Format.WFSDescribeFeatureType
+     * Create a new parser for WFS DescribeFeatureType responses.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Property: readers
+     * Contains public functions, grouped by namespace prefix, that will
+     *     be applied when a namespaced node is found matching the function
+     *     name.  The function will be applied in the scope of this parser
+     *     with two arguments: the node being read and a context object passed
+     *     from the parent.
+     */
+    readers: {
+        "xsd": {
+            "schema": function(node, obj) {
+                var complexTypes = [];
+                var customTypes = {};
+                var schema = {
+                    complexTypes: complexTypes,
+                    customTypes: customTypes
+                };
+                
+                this.readChildNodes(node, schema);
+
+                var attributes = node.attributes;
+                var attr, name;
+                for(var i=0, len=attributes.length; i<len; ++i) {
+                    attr = attributes[i];
+                    name = attr.name;
+                    if(name.indexOf("xmlns") == 0) {
+                        this.setNamespace(name.split(":")[1] || "", attr.value);
+                    } else {
+                        obj[name] = attr.value;
+                    }
+                }
+                obj.featureTypes = complexTypes;                
+                obj.targetPrefix = this.namespaceAlias[obj.targetNamespace];
+                
+                // map complexTypes to names of customTypes
+                var complexType, customType;
+                for(var i=0, len=complexTypes.length; i<len; ++i) {
+                    complexType = complexTypes[i];
+                    customType = customTypes[complexType.typeName];
+                    if(customTypes[complexType.typeName]) {
+                        complexType.typeName = customType.name;
+                    }
+                }
+            },
+            "complexType": function(node, obj) {
+                var complexType = {
+                    // this is a temporary typeName, it will be overwritten by
+                    // the schema reader with the metadata found in the
+                    // customTypes hash
+                    "typeName": node.getAttribute("name")
+                };
+                this.readChildNodes(node, complexType);
+                obj.complexTypes.push(complexType);
+            },
+            "complexContent": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "extension": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "sequence": function(node, obj) {
+                var sequence = {
+                    elements: []
+                };
+                this.readChildNodes(node, sequence);
+                obj.properties = sequence.elements;
+            },
+            "element": function(node, obj) {
+                if(obj.elements) {
+                    var element = {};
+                    var attributes = node.attributes;
+                    var attr;
+                    for(var i=0, len=attributes.length; i<len; ++i) {
+                        attr = attributes[i];
+                        element[attr.name] = attr.value;
+                    }
+                    
+                    var type = element.type;
+                    if(!type) {
+                        type = {};
+                        this.readChildNodes(node, type);
+                        element.restriction = type;
+                        element.type = type.base;
+                    }
+                    var fullType = type.base || type;
+                    element.localType = fullType.split(":").pop();
+                    obj.elements.push(element);
+                }
+                
+                if(obj.complexTypes) {
+                    var type = node.getAttribute("type");
+                    var localType = type.split(":").pop();
+                    obj.customTypes[localType] = {
+                        "name": node.getAttribute("name"),
+                        "type": type
+                    };
+                }
+            },
+            "simpleType": function(node, obj) {
+                this.readChildNodes(node, obj);
+            },
+            "restriction": function(node, obj) {
+                obj.base = node.getAttribute("base");
+                this.readRestriction(node, obj);
+            }
+        }
+    },
+    
+    /**
+     * Method: readRestriction
+     * Reads restriction defined in the child nodes of a restriction element
+     * 
+     * Parameters:
+     * node {DOMElement} - the node to parse
+     * obj {Object} - the object that receives the read result
+     */
+    readRestriction: function(node, obj) {
+        var children = node.childNodes;
+        var child, nodeName, value;
+        for(var i=0, len=children.length; i<len; ++i) {
+            child = children[i];
+            if(child.nodeType == 1) {
+                nodeName = child.nodeName.split(":").pop();
+                value = child.getAttribute("value");
+                if(!obj[nodeName]) {
+                    obj[nodeName] = value;
+                } else {
+                    if(typeof obj[nodeName] == "string") {
+                        obj[nodeName] = [obj[nodeName]];
+                    }
+                    obj[nodeName].push(value);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method: read
+     *
+     * Parameters:
+     * data - {DOMElement|String} A WFS DescribeFeatureType document.
+     *
+     * Returns:
+     * {Object} An object representing the WFS DescribeFeatureType response.
+     */
+    read: function(data) {
+        if(typeof data == "string") { 
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+        }
+        if(data && data.nodeType == 9) {
+            data = data.documentElement;
+        }
+        var schema = {};
+        this.readNode(data, schema);
+        
+        return schema;
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" 
+
+});
+/* ======================================================================
+    OpenLayers/Strategy/Refresh.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Refresh
+ * A strategy that refreshes the layer. By default the strategy waits for a
+ *     call to <refresh> before refreshing.  By configuring the strategy with 
+ *     the <interval> option, refreshing can take place automatically.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * Property: force
+     * {Boolean} Force a refresh on the layer. Default is false.
+     */
+    force: false,
+
+    /**
+     * Property: interval
+     * {Number} Auto-refresh. Default is 0.  If > 0, layer will be refreshed 
+     *     every N milliseconds.
+     */
+    interval: 0,
+    
+    /**
+     * Property: timer
+     * {Number} The id of the timer.
+     */
+    timer: null,
+
+    /**
+     * Constructor: OpenLayers.Strategy.Refresh
+     * Create a new Refresh strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+   
+    /**
+     * APIMethod: activate
+     * Activate the strategy. Register any listeners, do appropriate setup.
+     * 
+     * Returns:
+     * {Boolean} True if the strategy was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.call(this);
+        if(activated) {
+            if(this.layer.visibility === true) {
+                this.start();
+            } 
+            this.layer.events.on({
+                "visibilitychanged": this.reset,
+                scope: this
+            });
+        }
+        return activated;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Deactivate the strategy. Unregister any listeners, do appropriate
+     *     tear-down.
+     * 
+     * Returns:
+     * {Boolean} True if the strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            this.stop();
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: reset
+     * Start or cancel the refresh interval depending on the visibility of 
+     *     the layer.
+     */
+    reset: function() {
+        if(this.layer.visibility === true) {
+            this.start();
+        } else {
+            this.stop();
+        }
+    },
+    
+    /**
+     * Method: start
+     * Start the refresh interval. 
+     */
+    start: function() {
+        if(this.interval && typeof this.interval === "number" && 
+            this.interval > 0) {
+
+            this.timer = window.setInterval(
+                OpenLayers.Function.bind(this.refresh, this),
+                this.interval);
+        }
+    },
+    
+    /**
+     * APIMethod: refresh
+     * Tell the strategy to refresh which will refresh the layer.
+     */
+    refresh: function() {
+        if (this.layer && this.layer.refresh && 
+            typeof this.layer.refresh == "function") {
+
+            this.layer.refresh({force: this.force});
+        }
+    },
+   
+    /**
+     * Method: stop
+     * Cancels the refresh interval. 
+     */
+    stop: function() {
+        if(this.timer !== null) {
+            window.clearInterval(this.timer);
+            this.timer = null;
+        }
+    },
+    
+    CLASS_NAME: "OpenLayers.Strategy.Refresh" 
+});
+/* ======================================================================
+    OpenLayers/Control/MousePosition.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.MousePosition
+ * The MousePosition control displays geographic coordinates of the mouse
+ * pointer, as it is moved about the map.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {
+    
+    /**
+     * APIProperty: autoActivate
+     * {Boolean} Activate the control when it is added to a map.  Default is
+     *     true.
+     */
+    autoActivate: true,
+
+    /** 
+     * Property: element
+     * {DOMElement} 
+     */
+    element: null,
+    
+    /** 
+     * APIProperty: prefix
+     * {String}
+     */
+    prefix: '',
+    
+    /** 
+     * APIProperty: separator
+     * {String}
+     */
+    separator: ', ',
+    
+    /** 
+     * APIProperty: suffix
+     * {String}
+     */
+    suffix: '',
+    
+    /** 
+     * APIProperty: numDigits
+     * {Integer}
+     */
+    numDigits: 5,
+    
+    /** 
+     * APIProperty: granularity
+     * {Integer} 
+     */
+    granularity: 10,
+
+    /**
+     * APIProperty: emptyString 
+     * {String} Set this to some value to set when the mouse is outside the
+     *     map.
+     */
+    emptyString: null,
+    
+    /** 
+     * Property: lastXy
+     * {<OpenLayers.Pixel>}
+     */
+    lastXy: null,
+
+    /**
+     * APIProperty: displayProjection
+     * {<OpenLayers.Projection>} The projection in which the 
+     * mouse position is displayed
+     */
+    displayProjection: null, 
+    
+    /**
+     * Constructor: OpenLayers.Control.MousePosition
+     * 
+     * Parameters:
+     * options - {Object} Options for control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: destroy
+     */
+     destroy: function() {
+         this.deactivate();
+         OpenLayers.Control.prototype.destroy.apply(this, arguments);
+     },
+
+    /**
+     * APIMethod: activate
+     */
+    activate: function() {
+        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
+            this.map.events.register('mousemove', this, this.redraw);
+            this.map.events.register('mouseout', this, this.reset);
+            this.redraw();
+            return true;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * APIMethod: deactivate
+     */
+    deactivate: function() {
+        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
+            this.map.events.unregister('mousemove', this, this.redraw);
+            this.map.events.unregister('mouseout', this, this.reset);
+            this.element.innerHTML = "";
+            return true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Method: draw
+     * {DOMElement}
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if (!this.element) {
+            this.div.left = "";
+            this.div.top = "";
+            this.element = this.div;
+        }
+        
+        return this.div;
+    },
+   
+    /**
+     * Method: redraw  
+     */
+    redraw: function(evt) {
+
+        var lonLat;
+
+        if (evt == null) {
+            this.reset();
+            return;
+        } else {
+            if (this.lastXy == null ||
+                Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
+                Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
+            {
+                this.lastXy = evt.xy;
+                return;
+            }
+
+            lonLat = this.map.getLonLatFromPixel(evt.xy);
+            if (!lonLat) { 
+                // map has not yet been properly initialized
+                return;
+            }    
+            if (this.displayProjection) {
+                lonLat.transform(this.map.getProjectionObject(), 
+                                 this.displayProjection );
+            }      
+            this.lastXy = evt.xy;
+            
+        }
+        
+        var newHtml = this.formatOutput(lonLat);
+
+        if (newHtml != this.element.innerHTML) {
+            this.element.innerHTML = newHtml;
+        }
+    },
+
+    /**
+     * Method: reset
+     */
+    reset: function(evt) {
+        if (this.emptyString != null) {
+            this.element.innerHTML = this.emptyString;
+        }
+    },
+
+    /**
+     * Method: formatOutput
+     * Override to provide custom display output
+     *
+     * Parameters:
+     * lonLat - {<OpenLayers.LonLat>} Location to display
+     */
+    formatOutput: function(lonLat) {
+        var digits = parseInt(this.numDigits);
+        var newHtml =
+            this.prefix +
+            lonLat.lon.toFixed(digits) +
+            this.separator + 
+            lonLat.lat.toFixed(digits) +
+            this.suffix;
+        return newHtml;
+    },
+
+    CLASS_NAME: "OpenLayers.Control.MousePosition"
+});
+/* ======================================================================
+    OpenLayers/Protocol/HTTP.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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
+ * @requires OpenLayers/Protocol.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Filter/Spatial.js
+ * @requires OpenLayers/Filter/Comparison.js
+ * @requires OpenLayers/Filter/Logical.js
+ * @requires OpenLayers/Request/XMLHttpRequest.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol.HTTP
+ * A basic HTTP protocol for vector layers.  Create a new instance with the
+ *     <OpenLayers.Protocol.HTTP> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Protocol>
+ */
+OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
+
+    /**
+     * Property: url
+     * {String} Service URL, read-only, set through the options
+     *     passed to constructor.
+     */
+    url: null,
+
+    /**
+     * Property: headers
+     * {Object} HTTP request headers, read-only, set through the options
+     *     passed to the constructor,
+     *     Example: {'Content-Type': 'plain/text'}
+     */
+    headers: null,
+
+    /**
+     * Property: params
+     * {Object} Parameters of GET requests, read-only, set through the options
+     *     passed to the constructor,
+     *     Example: {'bbox': '5,5,5,5'}
+     */
+    params: null,
+    
+    /**
+     * Property: callback
+     * {Object} Function to be called when the <read>, <create>,
+     *     <update>, <delete> or <commit> operation completes, read-only,
+     *     set through the options passed to the constructor.
+     */
+    callback: null,
+
+    /**
+     * Property: scope
+     * {Object} Callback execution scope, read-only, set through the
+     *     options passed to the constructor.
+     */
+    scope: null,
+
+    /**
+     * Property: readWithPOST
+     * {Boolean} true if read operations are done with POST requests
+     *     instead of GET, defaults to false.
+     */
+    readWithPOST: false,
+
+    /**
+     * Property: wildcarded.
+     * {Boolean} If true percent signs are added around values
+     *     read from LIKE filters, for example if the protocol
+     *     read method is passed a LIKE filter whose property
+     *     is "foo" and whose value is "bar" the string
+     *     "foo__ilike=%bar%" will be sent in the query string;
+     *     defaults to false.
+     */
+    wildcarded: false,
+
+    /**
+     * APIProperty: srsInBBOX
+     * {Boolean} Include the SRS identifier in BBOX query string parameter.  
+     *     Default is false.  If true and the layer has a projection object set,
+     *     any BBOX filter will be serialized with a fifth item identifying the
+     *     projection.  E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
+     */
+    srsInBBOX: false,
+
+    /**
+     * Constructor: OpenLayers.Protocol.HTTP
+     * A class for giving layers generic HTTP protocol.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options include:
+     * url - {String}
+     * headers - {Object} 
+     * params - {Object}
+     * format - {<OpenLayers.Format>}
+     * callback - {Function}
+     * scope - {Object}
+     */
+    initialize: function(options) {
+        options = options || {};
+        this.params = {};
+        this.headers = {};
+        OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Clean up the protocol.
+     */
+    destroy: function() {
+        this.params = null;
+        this.headers = null;
+        OpenLayers.Protocol.prototype.destroy.apply(this);
+    },
+   
+    /**
+     * APIMethod: read
+     * Construct a request for reading new features.
+     *
+     * Parameters:
+     * options - {Object} Optional object for configuring the request.
+     *     This object is modified and should not be reused.
+     *
+     * Valid options:
+     * url - {String} Url for the request.
+     * params - {Object} Parameters to get serialized as a query string.
+     * headers - {Object} Headers to be set on the request.
+     * filter - {<OpenLayers.Filter>} Filter to get serialized as a
+     *     query string.
+     * readWithPOST - {Boolean} If the request should be done with POST.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
+     *     references the HTTP request, this object is also passed to the
+     *     callback function when the request completes, its "features" property
+     *     is then populated with the the features received from the server.
+     */
+    read: function(options) {
+        OpenLayers.Protocol.prototype.read.apply(this, arguments);
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+        options.params = OpenLayers.Util.applyDefaults(
+            options.params, this.options.params);
+        if(options.filter) {
+            options.params = this.filterToParams(
+                options.filter, options.params);
+        }
+        var readWithPOST = (options.readWithPOST !== undefined) ?
+                           options.readWithPOST : this.readWithPOST;
+        var resp = new OpenLayers.Protocol.Response({requestType: "read"});
+        if(readWithPOST) {
+            resp.priv = OpenLayers.Request.POST({
+                url: options.url,
+                callback: this.createCallback(this.handleRead, resp, options),
+                data: OpenLayers.Util.getParameterString(options.params),
+                headers: {
+                    "Content-Type": "application/x-www-form-urlencoded"
+                }
+            });
+        } else {
+            resp.priv = OpenLayers.Request.GET({
+                url: options.url,
+                callback: this.createCallback(this.handleRead, resp, options),
+                params: options.params,
+                headers: options.headers
+            });
+        }
+        return resp;
+    },
+
+    /**
+     * Method: handleRead
+     * Individual callbacks are created for read, create and update, should
+     *     a subclass need to override each one separately.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     the user callback.
+     * options - {Object} The user options passed to the read call.
+     */
+    handleRead: function(resp, options) {
+        this.handleResponse(resp, options);
+    },
+
+    /**
+     * Method: filterToParams
+     * Convert an <OpenLayers.Filter> object to parameters.
+     *
+     * Parameters:
+     * filter - {OpenLayers.Filter} filter to convert.
+     * params - {Object} The parameters object.
+     *
+     * Returns:
+     * {Object} The resulting parameters object.
+     */
+    filterToParams: function(filter, params) {
+        params = params || {};
+        var className = filter.CLASS_NAME;
+        var filterType = className.substring(className.lastIndexOf(".") + 1);
+        switch(filterType) {
+            case "Spatial":
+                switch(filter.type) {
+                    case OpenLayers.Filter.Spatial.BBOX:
+                        params.bbox = filter.value.toArray();
+                        if (this.srsInBBOX && filter.projection) {
+                            params.bbox.push(filter.projection.getCode());
+                        }
+                        break;
+                    case OpenLayers.Filter.Spatial.DWITHIN:
+                        params.tolerance = filter.distance;
+                        // no break here
+                    case OpenLayers.Filter.Spatial.WITHIN:
+                        params.lon = filter.value.x;
+                        params.lat = filter.value.y;
+                        break;
+                    default:
+                        OpenLayers.Console.warn(
+                            "Unknown spatial filter type " + filter.type);
+                }
+                break;
+            case "Comparison":
+                var op = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR[filter.type];
+                if(op !== undefined) {
+                    var value = filter.value;
+                    if(filter.type == OpenLayers.Filter.Comparison.LIKE) {
+                        value = this.regex2value(value);
+                        if(this.wildcarded) {
+                            value = "%" + value + "%";
+                        }
+                    }
+                    params[filter.property + "__" + op] = value;
+                    params.queryable = params.queryable || [];
+                    params.queryable.push(filter.property);
+                } else {
+                    OpenLayers.Console.warn(
+                        "Unknown comparison filter type " + filter.type);
+                }
+                break;
+            case "Logical":
+                if(filter.type === OpenLayers.Filter.Logical.AND) {
+                    for(var i=0,len=filter.filters.length; i<len; i++) {
+                        params = this.filterToParams(filter.filters[i], params);
+                    }
+                } else {
+                    OpenLayers.Console.warn(
+                        "Unsupported logical filter type " + filter.type);
+                }
+                break;
+            default:
+                OpenLayers.Console.warn("Unknown filter type " + filterType);
+        }
+        return params;
+    },
+
+    /**
+     * Method: regex2value
+     * Convert the value from a regular expression string to a LIKE/ILIKE
+     * string known to the web service.
+     *
+     * Parameters:
+     * value - {String} The regex string.
+     *
+     * Returns:
+     * {String} The converted string.
+     */
+    regex2value: function(value) {
+
+        // highly sensitive!! Do not change this without running the
+        // Protocol/HTTP.html unit tests
+
+        // convert % to \%
+        value = value.replace(/%/g, "\\%");
+
+        // convert \\. to \\_ (\\.* occurences converted later)
+        value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
+            return $1 ? $0 : "\\\\_";
+        });
+
+        // convert \\.* to \\%
+        value = value.replace(/\\\\\.\*/g, "\\\\%");
+
+        // convert . to _ (\. and .* occurences converted later)
+        value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
+            return $1 || $2 ? $0 : "_";
+        });
+
+        // convert .* to % (\.* occurnces converted later)
+        value = value.replace(/(\\)?\.\*/g, function($0, $1) {
+            return $1 ? $0 : "%";
+        });
+
+        // convert \. to .
+        value = value.replace(/\\\./g, ".");
+
+        // replace \* with * (watching out for \\*)
+        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
+            return $1 ? $0 : "*";
+        });
+
+        return value;
+    },
+
+    /**
+     * APIMethod: create
+     * Construct a request for writing newly created features.
+     *
+     * Parameters:
+     * features - {Array({<OpenLayers.Feature.Vector>})} or
+     *     {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *     This object is modified and should not be reused.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     *     object, whose "priv" property references the HTTP request, this 
+     *     object is also passed to the callback function when the request
+     *     completes, its "features" property is then populated with the
+     *     the features received from the server.
+     */
+    create: function(features, options) {
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+
+        var resp = new OpenLayers.Protocol.Response({
+            reqFeatures: features,
+            requestType: "create"
+        });
+
+        resp.priv = OpenLayers.Request.POST({
+            url: options.url,
+            callback: this.createCallback(this.handleCreate, resp, options),
+            headers: options.headers,
+            data: this.format.write(features)
+        });
+
+        return resp;
+    },
+
+    /**
+     * Method: handleCreate
+     * Called the the request issued by <create> is complete.  May be overridden
+     *     by subclasses.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     any user callback.
+     * options - {Object} The user options passed to the create call.
+     */
+    handleCreate: function(resp, options) {
+        this.handleResponse(resp, options);
+    },
+
+    /**
+     * APIMethod: update
+     * Construct a request updating modified feature.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *     This object is modified and should not be reused.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     *     object, whose "priv" property references the HTTP request, this 
+     *     object is also passed to the callback function when the request
+     *     completes, its "features" property is then populated with the
+     *     the feature received from the server.
+     */
+    update: function(feature, options) {
+        options = options || {};
+        var url = options.url ||
+                  feature.url ||
+                  this.options.url + "/" + feature.fid;
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+
+        var resp = new OpenLayers.Protocol.Response({
+            reqFeatures: feature,
+            requestType: "update"
+        });
+
+        resp.priv = OpenLayers.Request.PUT({
+            url: url,
+            callback: this.createCallback(this.handleUpdate, resp, options),
+            headers: options.headers,
+            data: this.format.write(feature)
+        });
+
+        return resp;
+    },
+
+    /**
+     * Method: handleUpdate
+     * Called the the request issued by <update> is complete.  May be overridden
+     *     by subclasses.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     any user callback.
+     * options - {Object} The user options passed to the update call.
+     */
+    handleUpdate: function(resp, options) {
+        this.handleResponse(resp, options);
+    },
+
+    /**
+     * APIMethod: delete
+     * Construct a request deleting a removed feature.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     * options - {Object} Optional object for configuring the request.
+     *     This object is modified and should not be reused.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
+     *     object, whose "priv" property references the HTTP request, this 
+     *     object is also passed to the callback function when the request
+     *     completes.
+     */
+    "delete": function(feature, options) {
+        options = options || {};
+        var url = options.url ||
+                  feature.url ||
+                  this.options.url + "/" + feature.fid;
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+
+        var resp = new OpenLayers.Protocol.Response({
+            reqFeatures: feature,
+            requestType: "delete"
+        });
+
+        resp.priv = OpenLayers.Request.DELETE({
+            url: url,
+            callback: this.createCallback(this.handleDelete, resp, options),
+            headers: options.headers
+        });
+
+        return resp;
+    },
+
+    /**
+     * Method: handleDelete
+     * Called the the request issued by <delete> is complete.  May be overridden
+     *     by subclasses.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     any user callback.
+     * options - {Object} The user options passed to the delete call.
+     */
+    handleDelete: function(resp, options) {
+        this.handleResponse(resp, options);
+    },
+
+    /**
+     * Method: handleResponse
+     * Called by CRUD specific handlers.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     any user callback.
+     * options - {Object} The user options passed to the create, read, update,
+     *     or delete call.
+     */
+    handleResponse: function(resp, options) {
+        var request = resp.priv;
+        if(options.callback) {
+            if(request.status >= 200 && request.status < 300) {
+                // success
+                if(resp.requestType != "delete") {
+                    resp.features = this.parseFeatures(request);
+                }
+                resp.code = OpenLayers.Protocol.Response.SUCCESS;
+            } else {
+                // failure
+                resp.code = OpenLayers.Protocol.Response.FAILURE;
+            }
+            options.callback.call(options.scope, resp);
+        }
+    },
+
+    /**
+     * Method: parseFeatures
+     * Read HTTP response body and return features.
+     *
+     * Parameters:
+     * request - {XMLHttpRequest} The request object
+     *
+     * Returns:
+     * {Array({<OpenLayers.Feature.Vector>})} or
+     *     {<OpenLayers.Feature.Vector>} Array of features or a single feature.
+     */
+    parseFeatures: function(request) {
+        var doc = request.responseXML;
+        if (!doc || !doc.documentElement) {
+            doc = request.responseText;
+        }
+        if (!doc || doc.length <= 0) {
+            return null;
+        }
+        return this.format.read(doc);
+    },
+
+    /**
+     * APIMethod: commit
+     * Iterate over each feature and take action based on the feature state.
+     *     Possible actions are create, update and delete.
+     *
+     * Parameters:
+     * features - {Array({<OpenLayers.Feature.Vector>})}
+     * options - {Object} Optional object for setting up intermediate commit
+     *     callbacks.
+     *
+     * Valid options:
+     * create - {Object} Optional object to be passed to the <create> method.
+     * update - {Object} Optional object to be passed to the <update> method.
+     * delete - {Object} Optional object to be passed to the <delete> method.
+     * callback - {Function} Optional function to be called when the commit
+     *     is complete.
+     * scope - {Object} Optional object to be set as the scope of the callback.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
+     *     one per request made to the server, each object's "priv" property
+     *     references the corresponding HTTP request.
+     */
+    commit: function(features, options) {
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+        var resp = [], nResponses = 0;
+        
+        // Divide up features before issuing any requests.  This properly
+        // counts requests in the event that any responses come in before
+        // all requests have been issued.
+        var types = {};
+        types[OpenLayers.State.INSERT] = [];
+        types[OpenLayers.State.UPDATE] = [];
+        types[OpenLayers.State.DELETE] = [];
+        var feature, list, requestFeatures = [];
+        for(var i=0, len=features.length; i<len; ++i) {
+            feature = features[i];
+            list = types[feature.state];
+            if(list) {
+                list.push(feature);
+                requestFeatures.push(feature); 
+            }
+        }
+        // tally up number of requests
+        var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
+            types[OpenLayers.State.UPDATE].length +
+            types[OpenLayers.State.DELETE].length;
+        
+        // This response will be sent to the final callback after all the others
+        // have been fired.
+        var success = true;
+        var finalResponse = new OpenLayers.Protocol.Response({
+            reqFeatures: requestFeatures        
+        });
+        
+        function insertCallback(response) {
+            var len = response.features ? response.features.length : 0;
+            var fids = new Array(len);
+            for(var i=0; i<len; ++i) {
+                fids[i] = response.features[i].fid;
+            }   
+            finalResponse.insertIds = fids;
+            callback.apply(this, [response]);
+        }
+ 
+        function callback(response) {
+            this.callUserCallback(response, options);
+            success = success && response.success();
+            nResponses++;
+            if (nResponses >= nRequests) {
+                if (options.callback) {
+                    finalResponse.code = success ? 
+                        OpenLayers.Protocol.Response.SUCCESS :
+                        OpenLayers.Protocol.Response.FAILURE;
+                    options.callback.apply(options.scope, [finalResponse]);
+                }    
+            }
+        }
+
+        // start issuing requests
+        var queue = types[OpenLayers.State.INSERT];
+        if(queue.length > 0) {
+            resp.push(this.create(
+                queue, OpenLayers.Util.applyDefaults(
+                    {callback: insertCallback, scope: this}, options.create
+                )
+            ));
+        }
+        queue = types[OpenLayers.State.UPDATE];
+        for(var i=queue.length-1; i>=0; --i) {
+            resp.push(this.update(
+                queue[i], OpenLayers.Util.applyDefaults(
+                    {callback: callback, scope: this}, options.update
+                ))
+            );
+        }
+        queue = types[OpenLayers.State.DELETE];
+        for(var i=queue.length-1; i>=0; --i) {
+            resp.push(this["delete"](
+                queue[i], OpenLayers.Util.applyDefaults(
+                    {callback: callback, scope: this}, options["delete"]
+                ))
+            );
+        }
+        return resp;
+    },
+
+    /**
+     * APIMethod: abort
+     * Abort an ongoing request, the response object passed to
+     * this method must come from this HTTP protocol (as a result
+     * of a create, read, update, delete or commit operation).
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>}
+     */
+    abort: function(response) {
+        if (response) {
+            response.priv.abort();
+        }
+    },
+
+    /**
+     * Method: callUserCallback
+     * This method is used from within the commit method each time an
+     *     an HTTP response is received from the server, it is responsible
+     *     for calling the user-supplied callbacks.
+     *
+     * Parameters:
+     * resp - {<OpenLayers.Protocol.Response>}
+     * options - {Object} The map of options passed to the commit call.
+     */
+    callUserCallback: function(resp, options) {
+        var opt = options[resp.requestType];
+        if(opt && opt.callback) {
+            opt.callback.call(opt.scope, resp);
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Protocol.HTTP" 
+});
+
+/**
+ * Property: OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR
+ * {Object} A private class-level property mapping the
+ *     OpenLayers.Filter.Comparison types to the operation
+ *     strings of the protocol.
+ */
+(function() {
+    var o = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR = {};
+    o[OpenLayers.Filter.Comparison.EQUAL_TO]                 = "eq";
+    o[OpenLayers.Filter.Comparison.NOT_EQUAL_TO]             = "ne";
+    o[OpenLayers.Filter.Comparison.LESS_THAN]                = "lt";
+    o[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO]    = "lte";
+    o[OpenLayers.Filter.Comparison.GREATER_THAN]             = "gt";
+    o[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
+    o[OpenLayers.Filter.Comparison.LIKE]                     = "ilike";
+})();
+
+/* ======================================================================
+    OpenLayers/Strategy/Cluster.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Strategy.js
+ */
+
+/**
+ * Class: OpenLayers.Strategy.Cluster
+ * Strategy for vector feature clustering.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Strategy>
+ */
+OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
+    
+    /**
+     * APIProperty: distance
+     * {Integer} Pixel distance between features that should be considered a
+     *     single cluster.  Default is 20 pixels.
+     */
+    distance: 20,
+    
+    /**
+     * APIProperty: threshold
+     * {Integer} Optional threshold below which original features will be
+     *     added to the layer instead of clusters.  For example, a threshold
+     *     of 3 would mean that any time there are 2 or fewer features in
+     *     a cluster, those features will be added directly to the layer instead
+     *     of a cluster representing those features.  Default is null (which is
+     *     equivalent to 1 - meaning that clusters may contain just one feature).
+     */
+    threshold: null,
+    
+    /**
+     * Property: features
+     * {Array(<OpenLayers.Feature.Vector>)} Cached features.
+     */
+    features: null,
+    
+    /**
+     * Property: clusters
+     * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.
+     */
+    clusters: null,
+    
+    /**
+     * Property: clustering
+     * {Boolean} The strategy is currently clustering features.
+     */
+    clustering: false,
+    
+    /**
+     * Property: resolution
+     * {Float} The resolution (map units per pixel) of the current cluster set.
+     */
+    resolution: null,
+
+    /**
+     * Constructor: OpenLayers.Strategy.Cluster
+     * Create a new clustering strategy.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * APIMethod: activate
+     * Activate the strategy.  Register any listeners, do appropriate setup.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Strategy.prototype.activate.call(this);
+        if(activated) {
+            this.layer.events.on({
+                "beforefeaturesadded": this.cacheFeatures,
+                "moveend": this.cluster,
+                scope: this
+            });
+        }
+        return activated;
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * Deactivate the strategy.  Unregister any listeners, do appropriate
+     *     tear-down.
+     * 
+     * Returns:
+     * {Boolean} The strategy was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
+        if(deactivated) {
+            this.clearCache();
+            this.layer.events.un({
+                "beforefeaturesadded": this.cacheFeatures,
+                "moveend": this.cluster,
+                scope: this
+            });
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: cacheFeatures
+     * Cache features before they are added to the layer.
+     *
+     * Parameters:
+     * event - {Object} The event that this was listening for.  This will come
+     *     with a batch of features to be clustered.
+     *     
+     * Returns:
+     * {Boolean} False to stop features from being added to the layer.
+     */
+    cacheFeatures: function(event) {
+        var propagate = true;
+        if(!this.clustering) {
+            this.clearCache();
+            this.features = event.features;
+            this.cluster();
+            propagate = false;
+        }
+        return propagate;
+    },
+    
+    /**
+     * Method: clearCache
+     * Clear out the cached features.
+     */
+    clearCache: function() {
+        this.features = null;
+    },
+    
+    /**
+     * Method: cluster
+     * Cluster features based on some threshold distance.
+     *
+     * Parameters:
+     * event - {Object} The event received when cluster is called as a
+     *     result of a moveend event.
+     */
+    cluster: function(event) {
+        if((!event || event.zoomChanged) && this.features) {
+            var resolution = this.layer.map.getResolution();
+            if(resolution != this.resolution || !this.clustersExist()) {
+                this.resolution = resolution;
+                var clusters = [];
+                var feature, clustered, cluster;
+                for(var i=0; i<this.features.length; ++i) {
+                    feature = this.features[i];
+                    if(feature.geometry) {
+                        clustered = false;
+                        for(var j=clusters.length-1; j>=0; --j) {
+                            cluster = clusters[j];
+                            if(this.shouldCluster(cluster, feature)) {
+                                this.addToCluster(cluster, feature);
+                                clustered = true;
+                                break;
+                            }
+                        }
+                        if(!clustered) {
+                            clusters.push(this.createCluster(this.features[i]));
+                        }
+                    }
+                }
+                this.layer.removeAllFeatures();
+                if(clusters.length > 0) {
+                    if(this.threshold > 1) {
+                        var clone = clusters.slice();
+                        clusters = [];
+                        var candidate;
+                        for(var i=0, len=clone.length; i<len; ++i) {
+                            candidate = clone[i];
+                            if(candidate.attributes.count < this.threshold) {
+                                Array.prototype.push.apply(clusters, candidate.cluster);
+                            } else {
+                                clusters.push(candidate);
+                            }
+                        }
+                    }
+                    this.clustering = true;
+                    // A legitimate feature addition could occur during this
+                    // addFeatures call.  For clustering to behave well, features
+                    // should be removed from a layer before requesting a new batch.
+                    this.layer.addFeatures(clusters);
+                    this.clustering = false;
+                }
+                this.clusters = clusters;
+            }
+        }
+    },
+    
+    /**
+     * Method: clustersExist
+     * Determine whether calculated clusters are already on the layer.
+     *
+     * Returns:
+     * {Boolean} The calculated clusters are already on the layer.
+     */
+    clustersExist: function() {
+        var exist = false;
+        if(this.clusters && this.clusters.length > 0 &&
+           this.clusters.length == this.layer.features.length) {
+            exist = true;
+            for(var i=0; i<this.clusters.length; ++i) {
+                if(this.clusters[i] != this.layer.features[i]) {
+                    exist = false;
+                    break;
+                }
+            }
+        }
+        return exist;
+    },
+    
+    /**
+     * Method: shouldCluster
+     * Determine whether to include a feature in a given cluster.
+     *
+     * Parameters:
+     * cluster - {<OpenLayers.Feature.Vector>} A cluster.
+     * feature - {<OpenLayers.Feature.Vector>} A feature.
+     *
+     * Returns:
+     * {Boolean} The feature should be included in the cluster.
+     */
+    shouldCluster: function(cluster, feature) {
+        var cc = cluster.geometry.getBounds().getCenterLonLat();
+        var fc = feature.geometry.getBounds().getCenterLonLat();
+        var distance = (
+            Math.sqrt(
+                Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)
+            ) / this.resolution
+        );
+        return (distance <= this.distance);
+    },
+    
+    /**
+     * Method: addToCluster
+     * Add a feature to a cluster.
+     *
+     * Parameters:
+     * cluster - {<OpenLayers.Feature.Vector>} A cluster.
+     * feature - {<OpenLayers.Feature.Vector>} A feature.
+     */
+    addToCluster: function(cluster, feature) {
+        cluster.cluster.push(feature);
+        cluster.attributes.count += 1;
+    },
+    
+    /**
+     * Method: createCluster
+     * Given a feature, create a cluster.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     *
+     * Returns:
+     * {<OpenLayers.Feature.Vector>} A cluster.
+     */
+    createCluster: function(feature) {
+        var center = feature.geometry.getBounds().getCenterLonLat();
+        var cluster = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(center.lon, center.lat),
+            {count: 1}
+        );
+        cluster.cluster = [feature];
+        return cluster;
+    },
+
+    CLASS_NAME: "OpenLayers.Strategy.Cluster" 
+});
+/* ======================================================================
+    OpenLayers/Control/OverviewMap.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/** 
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/BaseTypes.js
+ * @requires OpenLayers/Events.js
+ */
+
+/**
+ * Class: OpenLayers.Control.OverviewMap
+ * 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:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Property: element
+     * {DOMElement} The DOM element that contains the overview map
+     */
+    element: null,
+    
+    /**
+     * APIProperty: ovmap
+     * {<OpenLayers.Map>} A reference to the overview map itself.
+     */
+    ovmap: null,
+
+    /**
+     * APIProperty: size
+     * {<OpenLayers.Size>} The overvew map size in pixels.  Note that this is
+     * the size of the map itself - the element that contains the map (default
+     * class name olControlOverviewMapElement) may have padding or other style
+     * attributes added via CSS.
+     */
+    size: new OpenLayers.Size(180, 90),
+
+    /**
+     * APIProperty: layers
+     * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.
+     * If none are sent at construction, the base layer for the main map is used.
+     */
+    layers: null,
+    
+    /**
+     * APIProperty: minRectSize
+     * {Integer} The minimum width or height (in pixels) of the extent
+     *     rectangle on the overview map.  When the extent rectangle reaches
+     *     this size, it will be replaced depending on the value of the
+     *     <minRectDisplayClass> property.  Default is 15 pixels.
+     */
+    minRectSize: 15,
+    
+    /**
+     * APIProperty: minRectDisplayClass
+     * {String} Replacement style class name for the extent rectangle when
+     *     <minRectSize> is reached.  This string will be suffixed on to the
+     *     displayClass.  Default is "RectReplacement".
+     *
+     * Example CSS declaration:
+     * (code)
+     * .olControlOverviewMapRectReplacement {
+     *     overflow: hidden;
+     *     cursor: move;
+     *     background-image: url("img/overview_replacement.gif");
+     *     background-repeat: no-repeat;
+     *     background-position: center;
+     * }
+     * (end)
+     */
+    minRectDisplayClass: "RectReplacement",
+
+    /**
+     * APIProperty: minRatio
+     * {Float} The ratio of the overview map resolution to the main map
+     *     resolution at which to zoom farther out on the overview map.
+     */
+    minRatio: 8,
+
+    /**
+     * APIProperty: maxRatio
+     * {Float} The ratio of the overview map resolution to the main map
+     *     resolution at which to zoom farther in on the overview map.
+     */
+    maxRatio: 32,
+    
+    /**
+     * APIProperty: mapOptions
+     * {Object} An object containing any non-default properties to be sent to
+     *     the overview map's map constructor.  These should include any
+     *     non-default options that the main map was constructed with.
+     */
+    mapOptions: null,
+
+    /**
+     * 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
+     * {Object}
+     */
+    handlers: null,
+
+    /**
+     * Property: resolutionFactor
+     * {Object}
+     */
+    resolutionFactor: 1,
+
+    /**
+     * APIProperty: maximized
+     * {Boolean} Start as maximized (visible). Defaults to false.
+     */
+    maximized: false,
+
+    /**
+     * Constructor: OpenLayers.Control.OverviewMap
+     * Create a new overview map
+     *
+     * Parameters:
+     * object - {Object} Properties of this object will be set on the overview
+     * map object.  Note, to set options on the map object contained in this
+     * control, set <mapOptions> as one of the options properties.
+     */
+    initialize: function(options) {
+        this.layers = [];
+        this.handlers = {};
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Deconstruct the control
+     */
+    destroy: function() {
+        if (!this.mapDiv) { // we've already been destroyed
+            return;
+        }
+        if (this.handlers.click) {
+            this.handlers.click.destroy();
+        }
+        if (this.handlers.drag) {
+            this.handlers.drag.destroy();
+        }
+
+        this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);
+        this.extentRectangle = null;
+
+        if (this.rectEvents) {
+            this.rectEvents.destroy();
+            this.rectEvents = null;
+        }
+
+        if (this.ovmap) {
+            this.ovmap.destroy();
+            this.ovmap = null;
+        }
+        
+        this.element.removeChild(this.mapDiv);
+        this.mapDiv = null;
+
+        this.div.removeChild(this.element);
+        this.element = null;
+
+        if (this.maximizeDiv) {
+            OpenLayers.Event.stopObservingElement(this.maximizeDiv);
+            this.div.removeChild(this.maximizeDiv);
+            this.maximizeDiv = null;
+        }
+        
+        if (this.minimizeDiv) {
+            OpenLayers.Event.stopObservingElement(this.minimizeDiv);
+            this.div.removeChild(this.minimizeDiv);
+            this.minimizeDiv = null;
+        }
+
+        this.map.events.un({
+            "moveend": this.update,
+            "changebaselayer": this.baseLayerDraw,
+            scope: this
+        });
+
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);    
+    },
+
+    /**
+     * Method: draw
+     * Render the control in the browser.
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        if(!(this.layers.length > 0)) {
+            if (this.map.baseLayer) {
+                var layer = this.map.baseLayer.clone();
+                this.layers = [layer];
+            } else {
+                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
+                return this.div;
+            }
+        }
+
+        // create overview map DOM elements
+        this.element = document.createElement('div');
+        this.element.className = this.displayClass + 'Element';
+        this.element.style.display = 'none';
+
+        this.mapDiv = document.createElement('div');
+        this.mapDiv.style.width = this.size.w + 'px';
+        this.mapDiv.style.height = this.size.h + 'px';
+        this.mapDiv.style.position = 'relative';
+        this.mapDiv.style.overflow = 'hidden';
+        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
+        
+        this.extentRectangle = document.createElement('div');
+        this.extentRectangle.style.position = 'absolute';
+        this.extentRectangle.style.zIndex = 1000;  //HACK
+        this.extentRectangle.className = this.displayClass+'ExtentRectangle';
+
+        this.element.appendChild(this.mapDiv);  
+
+        this.div.appendChild(this.element);
+
+        // Optionally add min/max buttons if the control will go in the
+        // map viewport.
+        if(!this.outsideViewport) {
+            this.div.className += " " + this.displayClass + 'Container';
+            var imgLocation = OpenLayers.Util.getImagesLocation();
+            // maximize button div
+            var img = imgLocation + 'layer-switcher-maximize.png';
+            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                        this.displayClass + 'MaximizeButton', 
+                                        null, 
+                                        new OpenLayers.Size(18,18), 
+                                        img, 
+                                        'absolute');
+            this.maximizeDiv.style.display = 'none';
+            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
+            OpenLayers.Event.observe(this.maximizeDiv, 'click', 
+                OpenLayers.Function.bindAsEventListener(this.maximizeControl,
+                                                        this)
+            );
+            this.div.appendChild(this.maximizeDiv);
+    
+            // minimize button div
+            var img = imgLocation + 'layer-switcher-minimize.png';
+            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                        'OpenLayers_Control_minimizeDiv', 
+                                        null, 
+                                        new OpenLayers.Size(18,18), 
+                                        img, 
+                                        'absolute');
+            this.minimizeDiv.style.display = 'none';
+            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
+            OpenLayers.Event.observe(this.minimizeDiv, 'click', 
+                OpenLayers.Function.bindAsEventListener(this.minimizeControl,
+                                                        this)
+            );
+            this.div.appendChild(this.minimizeDiv);
+            
+            var eventsToStop = ['dblclick','mousedown'];
+            
+            for (var i=0, len=eventsToStop.length; i<len; i++) {
+
+                OpenLayers.Event.observe(this.maximizeDiv, 
+                                         eventsToStop[i], 
+                                         OpenLayers.Event.stop);
+
+                OpenLayers.Event.observe(this.minimizeDiv,
+                                         eventsToStop[i], 
+                                         OpenLayers.Event.stop);
+            }
+            
+            this.minimizeControl();
+        } else {
+            // show the overview map
+            this.element.style.display = '';
+        }
+        if(this.map.getExtent()) {
+            this.update();
+        }
+        
+        this.map.events.register('moveend', this, this.update);
+        
+        if (this.maximized) {
+            this.maximizeControl();
+        }
+        return this.div;
+    },
+    
+    /**
+     * Method: baseLayerDraw
+     * Draw the base layer - called if unable to complete in the initial draw
+     */
+    baseLayerDraw: function() {
+        this.draw();
+        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
+    },
+
+    /**
+     * Method: rectDrag
+     * Handle extent rectangle drag
+     *
+     * Parameters:
+     * px - {<OpenLayers.Pixel>} The pixel location of the drag.
+     */
+    rectDrag: function(px) {
+        var deltaX = this.handlers.drag.last.x - px.x;
+        var deltaY = this.handlers.drag.last.y - px.y;
+        if(deltaX != 0 || deltaY != 0) {
+            var rectTop = this.rectPxBounds.top;
+            var rectLeft = this.rectPxBounds.left;
+            var rectHeight = Math.abs(this.rectPxBounds.getHeight());
+            var rectWidth = this.rectPxBounds.getWidth();
+            // don't allow dragging off of parent element
+            var newTop = Math.max(0, (rectTop - deltaY));
+            newTop = Math.min(newTop,
+                              this.ovmap.size.h - this.hComp - rectHeight);
+            var newLeft = Math.max(0, (rectLeft - deltaX));
+            newLeft = Math.min(newLeft,
+                               this.ovmap.size.w - this.wComp - rectWidth);
+            this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                       newTop + rectHeight,
+                                                       newLeft + rectWidth,
+                                                       newTop));
+        }
+    },
+    
+    /**
+     * Method: mapDivClick
+     * Handle browser events
+     *
+     * Parameters:
+     * evt - {<OpenLayers.Event>} evt
+     */
+    mapDivClick: function(evt) {
+        var pxCenter = this.rectPxBounds.getCenterPixel();
+        var deltaX = evt.xy.x - pxCenter.x;
+        var deltaY = evt.xy.y - pxCenter.y;
+        var top = this.rectPxBounds.top;
+        var left = this.rectPxBounds.left;
+        var height = Math.abs(this.rectPxBounds.getHeight());
+        var width = this.rectPxBounds.getWidth();
+        var newTop = Math.max(0, (top + deltaY));
+        newTop = Math.min(newTop, this.ovmap.size.h - height);
+        var newLeft = Math.max(0, (left + deltaX));
+        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
+        this.setRectPxBounds(new OpenLayers.Bounds(newLeft,
+                                                   newTop + height,
+                                                   newLeft + width,
+                                                   newTop));
+        this.updateMapToRect();
+    },
+
+    /**
+     * Method: maximizeControl
+     * Unhide the control.  Called when the control is in the map viewport.
+     *
+     * Parameters:
+     * e - {<OpenLayers.Event>}
+     */
+    maximizeControl: function(e) {
+        this.element.style.display = '';
+        this.showToggle(false);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /**
+     * Method: minimizeControl
+     * Hide all the contents of the control, shrink the size, 
+     * add the maximize icon
+     * 
+     * Parameters:
+     * e - {<OpenLayers.Event>}
+     */
+    minimizeControl: function(e) {
+        this.element.style.display = 'none';
+        this.showToggle(true);
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /**
+     * Method: showToggle
+     * Hide/Show the toggle depending on whether the control is minimized
+     *
+     * Parameters:
+     * minimize - {Boolean} 
+     */
+    showToggle: function(minimize) {
+        this.maximizeDiv.style.display = minimize ? '' : 'none';
+        this.minimizeDiv.style.display = minimize ? 'none' : '';
+    },
+
+    /**
+     * Method: update
+     * Update the overview map after layers move.
+     */
+    update: function() {
+        if(this.ovmap == null) {
+            this.createMap();
+        }
+        
+        if(this.autoPan || !this.isSuitableOverview()) {
+            this.updateOverview();
+        }
+        
+        // update extent rectangle
+        this.updateRectToMap();
+    },
+    
+    /**
+     * Method: isSuitableOverview
+     * Determines if the overview map is suitable given the extent and
+     * resolution of the main map.
+     */
+    isSuitableOverview: function() {
+        var mapExtent = this.map.getExtent();
+        var maxExtent = this.map.maxExtent;
+        var testExtent = new OpenLayers.Bounds(
+                                Math.max(mapExtent.left, maxExtent.left),
+                                Math.max(mapExtent.bottom, maxExtent.bottom),
+                                Math.min(mapExtent.right, maxExtent.right),
+                                Math.min(mapExtent.top, maxExtent.top));        
+
+        if (this.ovmap.getProjection() != this.map.getProjection()) {
+            testExtent = testExtent.transform(
+                this.map.getProjectionObject(),
+                this.ovmap.getProjectionObject() );
+        }
+
+        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
+        return ((resRatio > this.minRatio) &&
+                (resRatio <= this.maxRatio) &&
+                (this.ovmap.getExtent().containsBounds(testExtent)));
+    },
+    
+    /**
+     * Method updateOverview
+     * Called by <update> if <isSuitableOverview> returns true
+     */
+    updateOverview: function() {
+        var mapRes = this.map.getResolution();
+        var targetRes = this.ovmap.getResolution();
+        var resRatio = targetRes / mapRes;
+        if(resRatio > this.maxRatio) {
+            // zoom in overview map
+            targetRes = this.minRatio * mapRes;            
+        } else if(resRatio <= this.minRatio) {
+            // zoom out overview map
+            targetRes = this.maxRatio * mapRes;
+        }
+        var center;
+        if (this.ovmap.getProjection() != this.map.getProjection()) {
+            center = this.map.center.clone();
+            center.transform(this.map.getProjectionObject(),
+                this.ovmap.getProjectionObject() );
+        } else {
+            center = this.map.center;
+        }
+        this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(
+            targetRes * this.resolutionFactor));
+        this.updateRectToMap();
+    },
+    
+    /**
+     * Method: createMap
+     * Construct the map that this control contains
+     */
+    createMap: function() {
+        // create the overview map
+        var options = OpenLayers.Util.extend(
+                        {controls: [], maxResolution: 'auto', 
+                         fallThrough: false}, this.mapOptions);
+        this.ovmap = new OpenLayers.Map(this.mapDiv, options);
+        this.ovmap.viewPortDiv.appendChild(this.extentRectangle);
+        
+        // prevent ovmap from being destroyed when the page unloads, because
+        // the OverviewMap control has to do this (and does it).
+        OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
+        
+        this.ovmap.addLayers(this.layers);
+        this.ovmap.zoomToMaxExtent();
+        // check extent rectangle border width
+        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-left-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-right-width'));
+        this.wComp = (this.wComp) ? this.wComp : 2;
+        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-top-width')) +
+                     parseInt(OpenLayers.Element.getStyle(this.extentRectangle,
+                                               'border-bottom-width'));
+        this.hComp = (this.hComp) ? this.hComp : 2;
+
+        this.handlers.drag = new OpenLayers.Handler.Drag(
+            this, {move: this.rectDrag, done: this.updateMapToRect},
+            {map: this.ovmap}
+        );
+        this.handlers.click = new OpenLayers.Handler.Click(
+            this, {
+                "click": this.mapDivClick
+            },{
+                "single": true, "double": false,
+                "stopSingle": true, "stopDouble": true,
+                "pixelTolerance": 1,
+                map: this.ovmap
+            }
+        );
+        this.handlers.click.activate();
+        
+        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,
+                                                null, true);
+        this.rectEvents.register("mouseover", this, function(e) {
+            if(!this.handlers.drag.active && !this.map.dragging) {
+                this.handlers.drag.activate();
+            }
+        });
+        this.rectEvents.register("mouseout", this, function(e) {
+            if(!this.handlers.drag.dragging) {
+                this.handlers.drag.deactivate();
+            }
+        });
+
+        if (this.ovmap.getProjection() != this.map.getProjection()) {
+            var sourceUnits = this.map.getProjectionObject().getUnits() ||
+                this.map.units || this.map.baseLayer.units;
+            var targetUnits = this.ovmap.getProjectionObject().getUnits() ||
+                this.ovmap.units || this.ovmap.baseLayer.units;
+            this.resolutionFactor = sourceUnits && targetUnits ?
+                OpenLayers.INCHES_PER_UNIT[sourceUnits] /
+                OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
+        }
+    },
+        
+    /**
+     * Method: updateRectToMap
+     * Updates the extent rectangle position and size to match the map extent
+     */
+    updateRectToMap: function() {
+        // If the projections differ we need to reproject
+        var bounds;
+        if (this.ovmap.getProjection() != this.map.getProjection()) {
+            bounds = this.map.getExtent().transform(
+                this.map.getProjectionObject(), 
+                this.ovmap.getProjectionObject() );
+        } else {
+            bounds = this.map.getExtent();
+        }
+        var pxBounds = this.getRectBoundsFromMapBounds(bounds);
+        if (pxBounds) {
+            this.setRectPxBounds(pxBounds);
+        }
+    },
+    
+    /**
+     * Method: updateMapToRect
+     * Updates the map extent to match the extent rectangle position and size
+     */
+    updateMapToRect: function() {
+        var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
+        if (this.ovmap.getProjection() != this.map.getProjection()) {
+            lonLatBounds = lonLatBounds.transform(
+                this.ovmap.getProjectionObject(),
+                this.map.getProjectionObject() );
+        }
+        this.map.panTo(lonLatBounds.getCenterLonLat());
+    },
+
+    /**
+     * Method: setRectPxBounds
+     * Set extent rectangle pixel bounds.
+     *
+     * Parameters:
+     * pxBounds - {<OpenLayers.Bounds>}
+     */
+    setRectPxBounds: function(pxBounds) {
+        var top = Math.max(pxBounds.top, 0);
+        var left = Math.max(pxBounds.left, 0);
+        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),
+                              this.ovmap.size.h - this.hComp);
+        var right = Math.min(pxBounds.left + pxBounds.getWidth(),
+                             this.ovmap.size.w - this.wComp);
+        var width = Math.max(right - left, 0);
+        var height = Math.max(bottom - top, 0);
+        if(width < this.minRectSize || height < this.minRectSize) {
+            this.extentRectangle.className = this.displayClass +
+                                             this.minRectDisplayClass;
+            var rLeft = left + (width / 2) - (this.minRectSize / 2);
+            var rTop = top + (height / 2) - (this.minRectSize / 2);
+            this.extentRectangle.style.top = Math.round(rTop) + 'px';
+            this.extentRectangle.style.left = Math.round(rLeft) + 'px';
+            this.extentRectangle.style.height = this.minRectSize + 'px';
+            this.extentRectangle.style.width = this.minRectSize + 'px';
+        } else {
+            this.extentRectangle.className = this.displayClass +
+                                             'ExtentRectangle';
+            this.extentRectangle.style.top = Math.round(top) + 'px';
+            this.extentRectangle.style.left = Math.round(left) + 'px';
+            this.extentRectangle.style.height = Math.round(height) + 'px';
+            this.extentRectangle.style.width = Math.round(width) + 'px';
+        }
+        this.rectPxBounds = new OpenLayers.Bounds(
+            Math.round(left), Math.round(bottom),
+            Math.round(right), Math.round(top)
+        );
+    },
+
+    /**
+     * Method: getRectBoundsFromMapBounds
+     * Get the rect bounds from the map bounds.
+     *
+     * Parameters:
+     * lonLatBounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent
+     * translated into pixel bounds for the overview map
+     */
+    getRectBoundsFromMapBounds: function(lonLatBounds) {
+        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left,
+                                                     lonLatBounds.bottom);
+        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right,
+                                                   lonLatBounds.top);
+        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
+        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
+        var bounds = null;
+        if (leftBottomPx && rightTopPx) {
+            bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,
+                                           rightTopPx.x, rightTopPx.y);
+        }
+        return bounds;
+    },
+
+    /**
+     * Method: getMapBoundsFromRectBounds
+     * Get the map bounds from the rect bounds.
+     *
+     * Parameters:
+     * pxBounds - {<OpenLayers.Bounds>}
+     *
+     * Returns:
+     * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds
+     * translated into lon/lat bounds for the overview map
+     */
+    getMapBoundsFromRectBounds: function(pxBounds) {
+        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left,
+                                                pxBounds.bottom);
+        var rightTopPx = new OpenLayers.Pixel(pxBounds.right,
+                                              pxBounds.top);
+        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
+        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
+        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,
+                                     rightTopLonLat.lon, rightTopLonLat.lat);
+    },
+
+    /**
+     * Method: getLonLatFromOverviewPx
+     * Get a map location from a pixel location
+     *
+     * Parameters:
+     * overviewMapPx - {<OpenLayers.Pixel>}
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} Location which is the passed-in overview map
+     * OpenLayers.Pixel, translated into lon/lat by the overview map
+     */
+    getLonLatFromOverviewPx: function(overviewMapPx) {
+        var size = this.ovmap.size;
+        var res  = this.ovmap.getResolution();
+        var center = this.ovmap.getExtent().getCenterLonLat();
+    
+        var delta_x = overviewMapPx.x - (size.w / 2);
+        var delta_y = overviewMapPx.y - (size.h / 2);
+        
+        return new OpenLayers.LonLat(center.lon + delta_x * res ,
+                                     center.lat - delta_y * res); 
+    },
+
+    /**
+     * Method: getOverviewPxFromLonLat
+     * Get a pixel location from a map location
+     *
+     * Parameters:
+     * lonlat - {<OpenLayers.LonLat>}
+     *
+     * Returns:
+     * {<OpenLayers.Pixel>} Location which is the passed-in OpenLayers.LonLat, 
+     * translated into overview map pixels
+     */
+    getOverviewPxFromLonLat: function(lonlat) {
+        var res  = this.ovmap.getResolution();
+        var extent = this.ovmap.getExtent();
+        var px = null;
+        if (extent) {
+            px = new OpenLayers.Pixel(
+                        Math.round(1/res * (lonlat.lon - extent.left)),
+                        Math.round(1/res * (extent.top - lonlat.lat)));
+        } 
+        return px;
+    },
+
+    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
+});
+/* ======================================================================
+    OpenLayers/Format/WFSCapabilities/v1_0_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/WFSCapabilities/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.WFSCapabilities/v1_0_0
+ * Read WMS Capabilities version 1.0.0.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.WFSCapabilities>
+ */
+OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(
+    OpenLayers.Format.WFSCapabilities.v1, {
+    
+    /**
+     * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0
+     * Create a new parser for WFS capabilities version 1.0.0.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.WFSCapabilities.v1.prototype.initialize.apply(
+            this, [options]
+        );
+    },
+    
+    /**
+     * Method: read_cap_Service
+     */
+    read_cap_Service: function(capabilities, node) {
+        var service = {};
+        this.runChildNodes(service, node);
+        capabilities.service = service;
+    },
+
+    /**
+     * Method: read_cap_Fees
+     */
+    read_cap_Fees: function(service, node) {
+        var fees = this.getChildValue(node);
+        if (fees && fees.toLowerCase() != "none") {
+            service.fees = fees;
+        }
+    },
+
+    /**
+     * Method: read_cap_AccessConstraints
+     */
+    read_cap_AccessConstraints: function(service, node) {
+        var constraints = this.getChildValue(node);
+        if (constraints && constraints.toLowerCase() != "none") {
+            service.accessConstraints = constraints;
+        }
+    },
+    
+    /**
+     * Method: read_cap_OnlineResource
+     */
+    read_cap_OnlineResource: function(service, node) {
+        var onlineResource = this.getChildValue(node);
+        if (onlineResource && onlineResource.toLowerCase() != "none") {
+            service.onlineResource = onlineResource;
+        }
+    },
+    
+    /**
+     * Method: read_cap_Keywords
+     */
+    read_cap_Keywords: function(service, node) {
+        var keywords = this.getChildValue(node);
+        if (keywords && keywords.toLowerCase() != "none") {
+            service.keywords = keywords.split(', ');
+        }
+    },
+    
+    /**
+     * Method: read_cap_Capability
+     */
+    read_cap_Capability: function(capabilities, node) {
+        var capability = {};
+        this.runChildNodes(capability, node);
+        capabilities.capability = capability;
+    },
+    
+    /**
+     * Method: read_cap_Request
+     */
+    read_cap_Request: function(obj, node) {
+        var request = {};
+        this.runChildNodes(request, node);
+        obj.request = request;
+    },
+    
+    /**
+     * Method: read_cap_GetFeature
+     */
+    read_cap_GetFeature: function(request, node) {
+        var getfeature = {
+            href: {}, // DCPType
+            formats: [] // ResultFormat
+        };
+        this.runChildNodes(getfeature, node);
+        request.getfeature = getfeature;
+    },
+    
+    /**
+     * Method: read_cap_ResultFormat
+     */
+    read_cap_ResultFormat: function(obj, node) {
+        var children = node.childNodes;
+        var childNode;
+        for(var i=0; i<children.length; i++) {
+            childNode = children[i];
+            if(childNode.nodeType == 1) {
+                obj.formats.push(childNode.nodeName);
+            }
+        }
+    },
+    
+    /**
+     * Method: read_cap_DCPType
+     */
+    read_cap_DCPType: function(obj, node) {
+        this.runChildNodes(obj, node);
+    },
+    
+    /**
+     * Method: read_cap_HTTP
+     */
+    read_cap_HTTP: function(obj, node) {
+        this.runChildNodes(obj.href, node);
+    },
+    
+    /**
+     * Method: read_cap_Get
+     */
+    read_cap_Get: function(obj, node) {
+        obj.get = node.getAttribute("onlineResource");
+    },
+    
+    /**
+     * Method: read_cap_Post
+     */
+    read_cap_Post: function(obj, node) {
+        obj.post = node.getAttribute("onlineResource");
+    },
+    
+    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0" 
+
+});
+/* ======================================================================
+    OpenLayers/Layer/Yahoo.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Layer/SphericalMercator.js
+ * @requires OpenLayers/Layer/EventPane.js
+ * @requires OpenLayers/Layer/FixedZoomLevels.js
+ * @requires OpenLayers/Lang.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.Yahoo
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Layer.EventPane>
+ *  - <OpenLayers.Layer.FixedZoomLevels>
+ */
+OpenLayers.Layer.Yahoo = OpenLayers.Class(
+  OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+    
+    /** 
+     * Constant: MIN_ZOOM_LEVEL
+     * {Integer} 0 
+     */
+    MIN_ZOOM_LEVEL: 0,
+    
+    /** 
+     * Constant: MAX_ZOOM_LEVEL
+     * {Integer} 17
+     */
+    MAX_ZOOM_LEVEL: 17,
+
+    /** 
+     * Constant: RESOLUTIONS
+     * {Array(Float)} Hardcode these resolutions so that they are more closely
+     *                tied with the standard wms projection
+     */
+    RESOLUTIONS: [
+        1.40625, 
+        0.703125, 
+        0.3515625, 
+        0.17578125, 
+        0.087890625, 
+        0.0439453125,
+        0.02197265625, 
+        0.010986328125, 
+        0.0054931640625, 
+        0.00274658203125, 
+        0.001373291015625, 
+        0.0006866455078125, 
+        0.00034332275390625, 
+        0.000171661376953125, 
+        0.0000858306884765625, 
+        0.00004291534423828125,
+        0.00002145767211914062,
+        0.00001072883605957031
+    ],
+
+    /**
+     * APIProperty: type
+     * {YahooMapType}
+     */
+    type: null,
+    
+    /**
+     * APIProperty: wrapDateLine
+     * {Boolean} Allow user to pan forever east/west.  Default is true.  
+     *     Setting this to false only restricts panning if 
+     *     <sphericalMercator> is true. 
+     */
+    wrapDateLine: true,
+
+    /**
+     * APIProperty: sphericalMercator
+     * {Boolean} Should the map act as a mercator-projected map? This will
+     * cause all interactions with the map to be in the actual map projection,
+     * which allows support for vector drawing, overlaying other maps, etc. 
+     */
+    sphericalMercator: false, 
+
+    /** 
+     * Constructor: OpenLayers.Layer.Yahoo
+     * 
+     * Parameters:
+     * name - {String}
+     * options - {Object}
+     */
+    initialize: function(name, options) {
+        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
+        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
+                                                                    arguments);
+        if(this.sphericalMercator) {
+            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+            this.initMercatorParameters();
+        }
+    },
+    
+    /**
+     * Method: loadMapObject
+     */
+    loadMapObject:function() {
+        try { //do not crash! 
+            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+            this.mapObject = new YMap(this.div, this.type, size);
+            this.mapObject.disableKeyControls();
+            this.mapObject.disableDragMap();
+
+            //can we do smooth panning? (moveByXY is not an API function)
+            if ( !this.mapObject.moveByXY || 
+                 (typeof this.mapObject.moveByXY != "function" ) ) {
+
+                this.dragPanMapObject = null;
+            }                
+        } catch(e) {}
+    },
+
+    /**
+     * Method: onMapResize
+     * 
+     */
+    onMapResize: function() {
+        try {
+            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
+            this.mapObject.resizeTo(size);
+        } catch(e) {}     
+    },    
+    
+    
+    /** 
+     * APIMethod: setMap
+     * Overridden from EventPane because we need to remove this yahoo event
+     *     pane which prohibits our drag and drop, and we can only do this 
+     *     once the map has been loaded and centered.
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>}
+     */
+    setMap: function(map) {
+        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
+
+        this.map.events.register("moveend", this, this.fixYahooEventPane);
+    },
+
+    /** 
+     * Method: fixYahooEventPane
+     * The map has been centered, so the mysterious yahoo eventpane has been
+     *     added. we remove it so that it doesnt mess with *our* event pane.
+     */
+    fixYahooEventPane: function() {
+        var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
+        if (yahooEventPane != null) {
+            if (yahooEventPane.parentNode != null) {
+                yahooEventPane.parentNode.removeChild(yahooEventPane);
+            }
+            this.map.events.unregister("moveend", this, 
+                                       this.fixYahooEventPane);
+        }
+    },
+
+    /** 
+     * APIMethod: getWarningHTML
+     * 
+     * Returns: 
+     * {String} String with information on why layer is broken, how to get
+     *          it working.
+     */
+    getWarningHTML:function() {
+        return OpenLayers.i18n(
+            "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
+        );
+    },
+
+  /********************************************************/
+  /*                                                      */
+  /*             Translation Functions                    */
+  /*                                                      */
+  /*    The following functions translate GMaps and OL    */ 
+  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
+  /*                                                      */
+  /********************************************************/
+
+
+  //
+  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
+  //
+  
+    /**
+     * APIMethod: getOLZoomFromMapObjectZoom
+     * 
+     * Parameters:
+     * gZoom - {Integer}
+     * 
+     * Returns:
+     * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
+     *           Returns null if null value is passed in.
+     */
+    getOLZoomFromMapObjectZoom: function(moZoom) {
+        var zoom = null;
+        if (moZoom != null) {
+            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
+            zoom = 18 - zoom;
+        }
+        return zoom;
+    },
+    
+    /**
+     * APIMethod: getMapObjectZoomFromOLZoom
+     * 
+     * Parameters:
+     * olZoom - {Integer}
+     * 
+     * Returns:
+     * {Integer} A MapObject level, translated from the passed in olZoom
+     *           Returns null if null value is passed in
+     */
+    getMapObjectZoomFromOLZoom: function(olZoom) {
+        var zoom = null; 
+        if (olZoom != null) {
+            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
+            zoom = 18 - zoom;
+        }
+        return zoom;
+    },
+
+    /************************************
+     *                                  *
+     *   MapObject Interface Controls   *
+     *                                  *
+     ************************************/
+
+
+  // Get&Set Center, Zoom
+
+    /** 
+     * APIMethod: setMapObjectCenter
+     * Set the mapObject to the specified center and zoom
+     * 
+     * Parameters:
+     * center - {Object} MapObject LonLat format
+     * zoom - {int} MapObject zoom format
+     */
+    setMapObjectCenter: function(center, zoom) {
+        this.mapObject.drawZoomAndCenter(center, zoom); 
+    },
+   
+    /**
+     * APIMethod: getMapObjectCenter
+     * 
+     * Returns: 
+     * {Object} The mapObject's current center in Map Object format
+     */
+    getMapObjectCenter: function() {
+        return this.mapObject.getCenterLatLon();
+    },
+
+    /**
+     * APIMethod: dragPanMapObject
+     * 
+     * Parameters:
+     * dX - {Integer}
+     * dY - {Integer}
+     */
+    dragPanMapObject: function(dX, dY) {
+        this.mapObject.moveByXY({
+            'x': -dX,
+            'y': dY
+        });
+    },
+    
+    /** 
+     * APIMethod: getMapObjectZoom
+     * 
+     * Returns:
+     * {Integer} The mapObject's current zoom, in Map Object format
+     */
+    getMapObjectZoom: function() {
+        return this.mapObject.getZoomLevel();
+    },
+
+
+  // LonLat - Pixel Translation
+  
+    /**
+     * APIMethod: getMapObjectLonLatFromMapObjectPixel
+     * 
+     * Parameters:
+     * moPixel - {Object} MapObject Pixel format
+     * 
+     * Returns:
+     * {Object} MapObject LonLat translated from MapObject Pixel
+     */
+    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
+        return this.mapObject.convertXYLatLon(moPixel);
+    },
+
+    /**
+     * APIMethod: getMapObjectPixelFromMapObjectLonLat
+     * 
+     * Parameters:
+     * moLonLat - {Object} MapObject LonLat format
+     * 
+     * Returns:
+     * {Object} MapObject Pixel transtlated from MapObject LonLat
+     */
+    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
+        return this.mapObject.convertLatLonXY(moLonLat);
+    },
+
+
+    /************************************
+     *                                  *
+     *       MapObject Primitives       *
+     *                                  *
+     ************************************/
+
+
+  // LonLat
+    
+    /**
+     * APIMethod: getLongitudeFromMapObjectLonLat
+     * 
+     * Parameters:
+     * moLonLat - {Object} MapObject LonLat format
+     * 
+     * Returns:
+     * {Float} Longitude of the given MapObject LonLat
+     */
+    getLongitudeFromMapObjectLonLat: function(moLonLat) {
+        return this.sphericalMercator ? 
+            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
+            moLonLat.Lon;
+    },
+
+    /**
+     * APIMethod: getLatitudeFromMapObjectLonLat
+     * 
+     * Parameters:
+     * moLonLat - {Object} MapObject LonLat format
+     * 
+     * Returns:
+     * {Float} Latitude of the given MapObject LonLat
+     */
+    getLatitudeFromMapObjectLonLat: function(moLonLat) {
+        return this.sphericalMercator ? 
+            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
+            moLonLat.Lat;
+    },
+
+    /**
+     * APIMethod: getMapObjectLonLatFromLonLat
+     * 
+     * Parameters:
+     * lon - {Float}
+     * lat - {Float}
+     * 
+     * Returns:
+     * {Object} MapObject LonLat built from lon and lat params
+     */
+    getMapObjectLonLatFromLonLat: function(lon, lat) {
+        var yLatLong;
+        if(this.sphericalMercator) {
+            var lonlat = this.inverseMercator(lon, lat);
+            yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
+        } else {
+            yLatLong = new YGeoPoint(lat, lon);
+        }
+        return yLatLong;
+    },
+
+  // Pixel
+    
+    /**
+     * APIMethod: getXFromMapObjectPixel
+     * 
+     * Parameters:
+     * moPixel - {Object} MapObject Pixel format
+     * 
+     * Returns:
+     * {Integer} X value of the MapObject Pixel
+     */
+    getXFromMapObjectPixel: function(moPixel) {
+        return moPixel.x;
+    },
+
+    /**
+     * APIMethod: getYFromMapObjectPixel
+     * 
+     * Parameters:
+     * moPixel - {Object} MapObject Pixel format
+     * 
+     * Returns:
+     * {Integer} Y value of the MapObject Pixel
+     */
+    getYFromMapObjectPixel: function(moPixel) {
+        return moPixel.y;
+    },
+
+    /**
+     * APIMethod: getMapObjectPixelFromXY
+     * 
+     * Parameters:
+     * x - {Integer}
+     * y - {Integer}
+     * 
+     * Returns:
+     * {Object} MapObject Pixel from x and y parameters
+     */
+    getMapObjectPixelFromXY: function(x, y) {
+        return new YCoordPoint(x, y);
+    },
+    
+  // Size
+  
+    /**
+     * APIMethod: getMapObjectSizeFromOLSize
+     * 
+     * Parameters:
+     * olSize - {<OpenLayers.Size>}
+     * 
+     * Returns:
+     * {Object} MapObject Size from olSize parameter
+     */
+    getMapObjectSizeFromOLSize: function(olSize) {
+        return new YSize(olSize.w, olSize.h);
+    },
+    
+    CLASS_NAME: "OpenLayers.Layer.Yahoo"
+});
+/* ======================================================================
+    OpenLayers/Layer/MapServer.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/Grid.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.MapServer
+ * Instances of OpenLayers.Layer.MapServer are used to display
+ * data from a MapServer CGI instance.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Layer.Grid>
+ */
+OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {
+
+    /**
+     * Constant: DEFAULT_PARAMS
+     * {Object} Hashtable of default parameter key/value pairs 
+     */
+    DEFAULT_PARAMS: {
+        mode: "map",
+        map_imagetype: "png"
+    },
+
+    /**
+     * Constructor: OpenLayers.Layer.MapServer
+     * Create a new MapServer layer object
+     *
+     * Parameters:
+     * name - {String} A name for the layer
+     * url - {String} Base url for the MapServer CGI
+     *       (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)
+     * params - {Object} An object with key/value pairs representing the
+     *          GetMap query string parameters and parameter values.
+     * options - {Ojbect} Hashtable of extra options to tag onto the layer
+     */
+    initialize: function(name, url, params, options) {
+        var newArguments = [];
+        newArguments.push(name, url, params, options);
+        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
+
+        this.params = OpenLayers.Util.applyDefaults(
+            this.params, this.DEFAULT_PARAMS
+        );
+
+        // unless explicitly set in options, if the layer is transparent, 
+        // it will be an overlay
+        if (options == null || options.isBaseLayer == null) {
+            this.isBaseLayer = ((this.params.transparent != "true") && 
+                                (this.params.transparent != true));
+        }
+    },
+
+    /**
+     * Method: clone
+     * Create a clone of this layer
+     *
+     * Returns:
+     * {<OpenLayers.Layer.MapServer>} An exact clone of this layer
+     */
+    clone: function (obj) {
+        if (obj == null) {
+            obj = new OpenLayers.Layer.MapServer(this.name,
+                                           this.url,
+                                           this.params,
+                                           this.getOptions());
+        }
+        //get all additions from superclasses
+        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
+
+        // copy/set any non-init, non-simple values here
+
+        return obj;
+    },
+
+    /**
+     * Method: addTile
+     * Creates a tile, initializes it, and adds it to the layer div. 
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>}
+     * position - {<OpenLayers.Pixel>}
+     * 
+     * Returns:
+     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
+     */
+    addTile:function(bounds,position) {
+        return new OpenLayers.Tile.Image(this, position, bounds, 
+                                         null, this.tileSize);
+    },
+    
+    /**
+     * Method: getURL
+     * Return a query string for this layer
+     *
+     * Parameters:
+     * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox 
+     *                                for the request
+     *
+     * Returns:
+     * {String} A string with the layer's url and parameters and also 
+     *          the passed-in bounds and appropriate tile size specified 
+     *          as parameters.
+     */
+    getURL: function (bounds) {
+        bounds = this.adjustBounds(bounds);
+        // Make a list, so that getFullRequestString uses literal "," 
+        var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top];
+
+        var imageSize = this.getImageSize(); 
+        
+        // make lists, so that literal ','s are used 
+        var url = this.getFullRequestString(
+                     {mapext:   extent,
+                      imgext:   extent,
+                      map_size: [imageSize.w, imageSize.h],
+                      imgx:     imageSize.w / 2,
+                      imgy:     imageSize.h / 2,
+                      imgxy:    [imageSize.w, imageSize.h]
+                      });
+        
+        return url;
+    },
+    
+    /** 
+     * Method: getFullRequestString
+     * combine the layer's url with its params and these newParams. 
+     *   
+     * Parameter:
+     * newParams - {Object} New parameters that should be added to the 
+     *                      request string.
+     * altUrl - {String} (optional) Replace the URL in the full request  
+     *                              string with the provided URL.
+     * 
+     * Returns: 
+     * {String} A string with the layer's url and parameters embedded in it.
+     */
+    getFullRequestString:function(newParams, altUrl) {
+        // use layer's url unless altUrl passed in
+        var url = (altUrl == null) ? this.url : altUrl;
+        
+        // create a new params hashtable with all the layer params and the 
+        // new params together. then convert to string
+        var allParams = OpenLayers.Util.extend({}, this.params);
+        allParams = OpenLayers.Util.extend(allParams, newParams);
+        var paramsString = OpenLayers.Util.getParameterString(allParams);
+        
+        // if url is not a string, it should be an array of strings, 
+        // in which case we will deterministically select one of them in 
+        // order to evenly distribute requests to different urls.
+        if (url instanceof Array) {
+            url = this.selectUrl(paramsString, url);
+        }   
+        
+        // ignore parameters that are already in the url search string
+        var urlParams = OpenLayers.Util.upperCaseObject(
+                            OpenLayers.Util.getParameters(url));
+        for(var key in allParams) {
+            if(key.toUpperCase() in urlParams) {
+                delete allParams[key];
+            }
+        }
+        paramsString = OpenLayers.Util.getParameterString(allParams);
+        
+        // requestString always starts with url
+        var requestString = url;        
+
+        // MapServer needs '+' seperating things like bounds/height/width.
+        //   Since typically this is URL encoded, we use a slight hack: we
+        //  depend on the list-like functionality of getParameterString to
+        //  leave ',' only in the case of list items (since otherwise it is
+        //  encoded) then do a regular expression replace on the , characters
+        //  to '+'
+        //
+        paramsString = paramsString.replace(/,/g, "+");
+        
+        if (paramsString != "") {
+            var lastServerChar = url.charAt(url.length - 1);
+            if ((lastServerChar == "&") || (lastServerChar == "?")) {
+                requestString += paramsString;
+            } else {
+                if (url.indexOf('?') == -1) {
+                    //serverPath has no ? -- add one
+                    requestString += '?' + paramsString;
+                } else {
+                    //serverPath contains ?, so must already have paramsString at the end
+                    requestString += '&' + paramsString;
+                }
+            }
+        }
+        return requestString;
+    },
+
+    CLASS_NAME: "OpenLayers.Layer.MapServer"
+});
+/* ======================================================================
+    OpenLayers/Renderer/VML.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). 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 (num | 0) to truncate 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.
+     *
+     * 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();
+            var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; 
+            for (var i = 0, len = shapes.length; i < len; i++) {
+
+                style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
+                              "position: absolute; display: inline-block;");
+            }                  
+        }
+        
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+    },
+
+    /**
+     * 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}
+     * 
+     * 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.
+     */
+    setExtent: function(extent, resolutionChanged) {
+        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, 
+                                                               arguments);
+        var resolution = this.getResolution();
+    
+        var left = (extent.left/resolution) | 0;
+        var top = (extent.top/resolution - this.size.h) | 0;
+        if (resolutionChanged || !this.offset) {
+            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.coordorigin = org;
+        var roots = [this.root, this.vectorRoot, this.textRoot];
+        var root;
+        for(var i=0, len=roots.length; i<len; ++i) {
+            root = roots[i];
+
+            var size = this.size.w + " " + this.size.h;
+            root.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;
+    },
+
+
+    /**
+     * Method: setSize
+     * Set 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);
+        
+        // setting width and height on all roots to avoid flicker which we
+        // would get with 100% width and height on child roots
+        var roots = [
+            this.rendererRoot,
+            this.root,
+            this.vectorRoot,
+            this.textRoot
+        ];
+        var w = this.size.w + "px";
+        var h = this.size.h + "px";
+        var root;
+        for(var i=0, len=roots.length; i<len; ++i) {
+            root = roots[i];
+            root.style.width = w;
+            root.style.height = h;
+        }
+    },
+
+    /**
+     * 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 fillColor = style.fillColor;
+
+        if (node._geometryClass === "OpenLayers.Geometry.Point") {
+            if (style.externalGraphic) {
+                options.isFilled = true;
+                if (style.graphicTitle) {
+                    node.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 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) | 0) + "px";
+                node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
+                node.style.width = width + "px";
+                node.style.height = height + "px";
+                node.style.flip = "y";
+                
+                // modify fillColor and options for stroke styling below
+                fillColor = "none";
+                options.isStroked = false;
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                var cache = this.importSymbol(style.graphicName);
+                node.path = cache.path;
+                node.coordorigin = cache.left + "," + cache.bottom;
+                var size = cache.size;
+                node.coordsize = size + "," + size;        
+                this.drawCircle(node, geometry, style.pointRadius);
+                node.style.flip = "y";
+            } else {
+                this.drawCircle(node, geometry, style.pointRadius);
+            }
+        }
+
+        // fill 
+        if (options.isFilled) { 
+            node.fillcolor = fillColor; 
+        } else { 
+            node.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.opacity = style.fillOpacity;
+
+            if (node._geometryClass === "OpenLayers.Geometry.Point" &&
+                    style.externalGraphic) {
+
+                // override fillOpacity
+                if (style.graphicOpacity) {
+                    fill.opacity = style.graphicOpacity;
+                }
+                
+                fill.src = style.externalGraphic;
+                fill.type = "frame";
+                
+                if (!(style.graphicWidth && style.graphicHeight)) {
+                  fill.aspect = "atmost";
+                }                
+            }
+            if (fill.parentNode != node) {
+                node.appendChild(fill);
+            }
+        }
+
+        // additional rendering for rotated graphics or symbols
+        var rotation = style.rotation;
+        if ((rotation !== undefined || node._rotation !== undefined)) {
+            node._rotation = rotation;
+            if (style.externalGraphic) {
+                this.graphicRotate(node, xOffset, yOffset, style);
+                // 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.opacity = 0;
+            } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
+                node.style.rotation = rotation || 0;
+            }
+        }
+
+        // stroke 
+        var strokes = node.getElementsByTagName("stroke");
+        var stroke = (strokes.length == 0) ? null : strokes[0];
+        if (!options.isStroked) {
+            node.stroked = false;
+            if (stroke) {
+                stroke.on = false;
+            }
+        } else {
+            if (!stroke) {
+                stroke = this.createNode('olv:stroke', node.id + "_stroke");
+                node.appendChild(stroke);
+            }
+            stroke.on = true;
+            stroke.color = style.strokeColor; 
+            stroke.weight = style.strokeWidth + "px"; 
+            stroke.opacity = style.strokeOpacity;
+            stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
+                (style.strokeLinecap || 'round');
+            if (style.strokeDashstyle) {
+                stroke.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.
+     * 
+     * Parameters:
+     * node    - {DOMElement}
+     * xOffset - {Number} rotation center relative to image, x coordinate
+     * yOffset - {Number} rotation center relative to image, y coordinate
+     * style   - {Object}
+     */
+    graphicRotate: function(node, xOffset, yOffset, style) {
+        var style = style || node._style;
+        var rotation = style.rotation || 0;
+        
+        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, style);
+                }
+            }, 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 rot = rotation * Math.PI / 180;
+        var sintheta = Math.sin(rot);
+        var costheta = Math.cos(rot);
+
+        // 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
+     * Does some node postprocessing to work around browser issues:
+     * - 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.
+     * - In some cases, a node won't become visible after being drawn. Setting
+     *   style.visibility to "visible" works around that.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     */
+    postDraw: function(node) {
+        node.style.visibility = "visible";
+        var fillColor = node._style.fillColor;
+        var strokeColor = node._style.strokeColor;
+        if (fillColor == "none" &&
+                node.fillcolor != fillColor) {
+            node.fillcolor = fillColor;
+        }
+        if (strokeColor == "none" &&
+                node.strokecolor != strokeColor) {
+            node.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) | 0,
+                                      (bbox.bottom/resolution - this.offset.y) | 0,
+                                      (bbox.right/resolution - this.offset.x) | 0,
+                                      (bbox.top/resolution - this.offset.y) | 0);
+            
+            // 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
+     */
+    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";
+        }
+    },
+
+    /**
+     * 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.id = id;
+        }
+        
+        // IE hack to make elements unselectable, to prevent 'blue flash'
+        // while dragging vectors; #1410
+        node.unselectable = 'on';
+        node.onselectstart = OpenLayers.Function.False;
+        
+        return node;    
+    },
+    
+    /**
+     * Method: nodeTypeCompare
+     * Determine whether a node is of a given type
+     *
+     * Parameters:
+     * node - {DOMElement} An VML element
+     * type - {String} Kind of node
+     *
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     */
+    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
+     * 
+     * Parameters:
+     * suffix - {String} suffix to append to the id
+     *
+     * Returns:
+     * {DOMElement}
+     */
+    createRoot: function(suffix) {
+        return this.nodeFactory(this.container.id + suffix, "olv:group");
+    },
+    
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+    
+    /**
+     * Method: drawPoint
+     * Render a point
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the point could not be drawn
+     */
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
+
+    /**
+     * 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) | 0) - radius) + "px";
+            node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - 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.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    drawLineString: function(node, geometry) {
+        return this.drawLine(node, geometry, false);
+    },
+
+    /**
+     * 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) | 0;
+            y = (comp.y/resolution - this.offset.y) | 0;
+            parts[i] = " " + x + "," + y + " 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 j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
+        for (j=0, jj=geometry.components.length; j<jj; j++) {
+            path.push("m");
+            points = geometry.components[j].components;
+            // we only close paths of interior rings with area
+            area = (j === 0);
+            first = null;
+            second = null;
+            for (i=0, ii=points.length; i<ii; i++) {
+                comp = points[i];
+                x = (comp.x / resolution - this.offset.x) | 0;
+                y = (comp.y / resolution - this.offset.y) | 0;
+                pathComp = " " + x + "," + y;
+                path.push(pathComp)
+                if (i==0) {
+                    path.push(" l");
+                }
+                if (!area) {
+                    // IE improperly renders sub-paths that have no area.
+                    // Instead of checking the area of every ring, we confirm
+                    // the ring has at least three distinct points.  This does
+                    // not catch all non-zero area cases, but it greatly improves
+                    // interior ring digitizing and is a minor performance hit
+                    // when rendering rings with many points.
+                    if (!first) {
+                        first = pathComp;
+                    } else if (first != pathComp) {
+                        if (!second) {
+                            second = pathComp;
+                        } else if (second != pathComp) {
+                            // stop looking
+                            area = true;
+                        }
+                    }
+                }
+            }
+            path.push(area ? " 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) | 0) + "px";
+        node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
+        node.style.width = ((geometry.width/resolution) | 0) + "px";
+        node.style.height = ((geometry.height/resolution) | 0) + "px";
+        
+        return node;
+    },
+    
+    /**
+     * Method: drawText
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String}
+     * style -
+     * location - {<OpenLayers.Geometry.Point>}
+     */
+    drawText: function(featureId, style, location) {
+        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
+        var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
+        
+        var resolution = this.getResolution();
+        label.style.left = ((location.x/resolution - this.offset.x) | 0) + "px";
+        label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
+        label.style.flip = "y";
+
+        textbox.innerText = style.label;
+
+        if (style.fontColor) {
+            textbox.style.color = style.fontColor;
+        }
+        if (style.fontOpacity) {
+            textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
+        }
+        if (style.fontFamily) {
+            textbox.style.fontFamily = style.fontFamily;
+        }
+        if (style.fontSize) {
+            textbox.style.fontSize = style.fontSize;
+        }
+        if (style.fontWeight) {
+            textbox.style.fontWeight = style.fontWeight;
+        }
+        if(style.labelSelect === true) {
+            label._featureId = featureId;
+            textbox._featureId = featureId;
+            textbox._geometry = location;
+            textbox._geometryClass = location.CLASS_NAME;
+        }
+        textbox.style.whiteSpace = "nowrap";
+        // fun with IE: IE7 in standards compliant mode does not display any
+        // text with a left inset of 0. So we set this to 1px and subtract one
+        // pixel later when we set label.style.left
+        textbox.inset = "1px,0px,0px,0px";
+
+        if(!label.parentNode) {
+            label.appendChild(textbox);
+            this.textRoot.appendChild(label);
+        }
+
+        var align = style.labelAlign || "cm";
+        if (align.length == 1) {
+            align += "m";
+        }
+        var xshift = textbox.clientWidth *
+            (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
+        var yshift = textbox.clientHeight *
+            (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
+        label.style.left = parseInt(label.style.left)-xshift-1+"px";
+        label.style.top = parseInt(label.style.top)+yshift+"px";
+        
+    },
+
+    /**
+     * 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) | 0;
+            y = (comp.y / resolution - this.offset.y) | 0;
+            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: moveRoot
+     * moves this renderer's root to a different renderer.
+     * 
+     * Parameters:
+     * 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} true if successful, false otherwise
+     */
+    moveRoot: function(renderer) {
+        var layer = this.map.getLayer(renderer.container.id);
+        if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
+            layer = this.map.getLayer(this.container.id);
+        }
+        layer && layer.renderer.clear();
+        OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
+        layer && layer.redraw();
+    },
+    
+    /**
+     * 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');
+        }
+
+        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) {
+            var x = symbol[i];
+            var 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(" ");
+
+        var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
+        if(diff > 0) {
+            symbolExtent.bottom = symbolExtent.bottom - diff;
+            symbolExtent.top = symbolExtent.top + diff;
+        } else {
+            symbolExtent.left = symbolExtent.left + diff;
+            symbolExtent.right = symbolExtent.right - diff;
+        }
+        
+        cache = {
+            path: path,
+            size: symbolExtent.getWidth(), // equals getHeight() now
+            left: symbolExtent.left,
+            bottom: symbolExtent.bottom
+        };
+        this.symbolCache[id] = cache;
+        
+        return cache;
+    },
+    
+    CLASS_NAME: "OpenLayers.Renderer.VML"
+});
+
+/**
+ * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
+ * {Object}
+ */
+OpenLayers.Renderer.VML.LABEL_SHIFT = {
+    "l": 0,
+    "c": .5,
+    "r": 1,
+    "t": 0,
+    "m": .5,
+    "b": 1
+};
+/* ======================================================================
+    OpenLayers/Protocol/WFS/v1_0_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Protocol/WFS/v1.js
+ * @requires OpenLayers/Format/WFST/v1_0_0.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol.WFS.v1_0_0
+ * A WFS v1.0.0 protocol for vector layers.  Create a new instance with the
+ *     <OpenLayers.Protocol.WFS.v1_0_0> constructor.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Protocol.WFS.v1>
+ */
+OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
+    
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: "1.0.0",
+    
+    /**
+     * Constructor: OpenLayers.Protocol.WFS.v1_0_0
+     * A class for giving layers WFS v1.0.0 protocol.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options properties:
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (optional).
+     * featurePrefix - {String} Feature namespace alias (optional - only used
+     *     if featureNS is provided).  Default is 'feature'.
+     * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
+     */
+   
+    CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" 
+});
+/* ======================================================================
+    OpenLayers/Handler/RegularPolygon.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler/Drag.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.RegularPolygon
+ * Handler to draw a regular polygon on the map.  Polygon is displayed on mouse
+ *     down, moves or is modified 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.RegularPolygon> constructor.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Handler>
+ */
+OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {
+    
+    /**
+     * APIProperty: sides
+     * {Integer} Number of sides for the regular polygon.  Needs to be greater
+     *     than 2.  Defaults to 4.
+     */
+    sides: 4,
+
+    /**
+     * APIProperty: radius
+     * {Float} Optional radius in map units of the regular polygon.  If this is
+     *     set to some non-zero value, a polygon with a fixed radius will be
+     *     drawn and dragged with mose movements.  If this property is not
+     *     set, dragging changes the radius of the polygon.  Set to null by
+     *     default.
+     */
+    radius: null,
+    
+    /**
+     * APIProperty: snapAngle
+     * {Float} If set to a non-zero value, the handler will snap the polygon
+     *     rotation to multiples of the snapAngle.  Value is an angle measured
+     *     in degrees counterclockwise from the positive x-axis.  
+     */
+    snapAngle: null,
+    
+    /**
+     * APIProperty: snapToggle
+     * {String} If set, snapToggle is checked on mouse events and will set
+     *     the snap mode to the opposite of what it currently is.  To disallow
+     *     toggling between snap and non-snap mode, set freehandToggle to
+     *     null.  Acceptable toggle values are 'shiftKey', 'ctrlKey', and
+     *     'altKey'. Snap mode is only possible if this.snapAngle is set to a
+     *     non-zero value.
+     */
+    snapToggle: 'shiftKey',
+    
+    /**
+     * Property: layerOptions
+     * {Object} Any optional properties to be set on the sketch layer.
+     */
+    layerOptions: null,
+
+    /**
+     * APIProperty: persist
+     * {Boolean} Leave the feature rendered until clear is called.  Default
+     *     is false.  If set to true, the feature remains rendered until
+     *     clear is called, typically by deactivating the handler or starting
+     *     another drawing.
+     */
+    persist: false,
+
+    /**
+     * APIProperty: irregular
+     * {Boolean} Draw an irregular polygon instead of a regular polygon.
+     *     Default is false.  If true, the initial mouse down will represent
+     *     one corner of the polygon bounds and with each mouse movement, the
+     *     polygon will be stretched so the opposite corner of its bounds
+     *     follows the mouse position.  This property takes precedence over
+     *     the radius property.  If set to true, the radius property will
+     *     be ignored.
+     */
+    irregular: false,
+
+    /**
+     * Property: angle
+     * {Float} The angle from the origin (mouse down) to the current mouse
+     *     position, in radians.  This is measured counterclockwise from the
+     *     positive x-axis.
+     */
+    angle: null,
+
+    /**
+     * Property: fixedRadius
+     * {Boolean} The polygon has a fixed radius.  True if a radius is set before
+     *     drawing begins.  False otherwise.
+     */
+    fixedRadius: false,
+
+    /**
+     * Property: feature
+     * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature
+     */
+    feature: null,
+
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>} The temporary drawing layer
+     */
+    layer: null,
+
+    /**
+     * Property: origin
+     * {<OpenLayers.Geometry.Point>} Location of the first mouse down
+     */
+    origin: null,
+
+    /**
+     * Constructor: OpenLayers.Handler.RegularPolygon
+     * Create a new regular polygon handler.
+     *
+     * Parameters:
+     * 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 object with properties to be set on the handler.
+     *     If the options.sides property is not specified, the number of sides
+     *     will default to 4.
+     *
+     * Named callbacks:
+     * create - Called when a sketch is first created.  Callback called with
+     *     the creation point geometry and sketch feature.
+     * done - Called when the sketch drawing is finished.  The callback will
+     *     recieve a single argument, the sketch geometry.
+     * cancel - Called when the handler is deactivated while drawing.  The
+     *     cancel callback will receive a geometry.
+     */
+    initialize: function(control, callbacks, options) {
+        if(!(options && options.layerOptions && options.layerOptions.styleMap)) {
+            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
+        }
+
+        OpenLayers.Handler.prototype.initialize.apply(this,
+                                                [control, callbacks, options]);
+        this.options = (options) ? options : {};
+    },
+    
+    /**
+     * APIMethod: setOptions
+     * 
+     * Parameters:
+     * newOptions - {Object} 
+     */
+    setOptions: function (newOptions) {
+        OpenLayers.Util.extend(this.options, newOptions);
+        OpenLayers.Util.extend(this, newOptions);
+    },
+    
+    /**
+     * APIMethod: activate
+     * Turn on the handler.
+     *
+     * Return:
+     * {Boolean} The handler was successfully activated
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            // create temporary vector layer for rendering geometry sketch
+            var options = OpenLayers.Util.extend({
+                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: OpenLayers.Function.True
+            }, this.layerOptions);
+            this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
+            this.map.addLayer(this.layer);
+            activated = true;
+        }
+        return activated;
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Turn off the handler.
+     *
+     * Return:
+     * {Boolean} The handler was successfully deactivated
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {
+            // call the cancel callback if mid-drawing
+            if(this.dragging) {
+                this.cancel();
+            }
+            // If a layer's map property is set to null, it means that that
+            // layer isn't added to the map. Since we ourself added the layer
+            // to the map in activate(), we can assume that if this.layer.map
+            // is null it means that the layer has been destroyed (as a result
+            // of map.destroy() for example.
+            if (this.layer.map != null) {
+                this.layer.destroy(false);
+                if (this.feature) {
+                    this.feature.destroy();
+                }
+            }
+            this.layer = null;
+            this.feature = null;
+            deactivated = true;
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: down
+     * Start drawing a new feature
+     *
+     * Parameters:
+     * evt - {Event} The drag start event
+     */
+    down: function(evt) {
+        this.fixedRadius = !!(this.radius);
+        var maploc = this.map.getLonLatFromPixel(evt.xy);
+        this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
+        // create the new polygon
+        if(!this.fixedRadius || this.irregular) {
+            // smallest radius should not be less one pixel in map units
+            // VML doesn't behave well with smaller
+            this.radius = this.map.getResolution();
+        }
+        if(this.persist) {
+            this.clear();
+        }
+        this.feature = new OpenLayers.Feature.Vector();
+        this.createGeometry();
+        this.callback("create", [this.origin, this.feature]);
+        this.layer.addFeatures([this.feature], {silent: true});
+        this.layer.drawFeature(this.feature, this.style);
+    },
+    
+    /**
+     * Method: move
+     * Respond to drag move events
+     *
+     * Parameters:
+     * evt - {Evt} The move event
+     */
+    move: function(evt) {
+        var maploc = this.map.getLonLatFromPixel(evt.xy);
+        var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
+        if(this.irregular) {
+            var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;
+            this.radius = Math.max(this.map.getResolution() / 2, ry);
+        } else if(this.fixedRadius) {
+            this.origin = point;
+        } else {
+            this.calculateAngle(point, evt);
+            this.radius = Math.max(this.map.getResolution() / 2,
+                                   point.distanceTo(this.origin));
+        }
+        this.modifyGeometry();
+        if(this.irregular) {
+            var dx = point.x - this.origin.x;
+            var dy = point.y - this.origin.y;
+            var ratio;
+            if(dy == 0) {
+                ratio = dx / (this.radius * Math.sqrt(2));
+            } else {
+                ratio = dx / dy;
+            }
+            this.feature.geometry.resize(1, this.origin, ratio);
+            this.feature.geometry.move(dx / 2, dy / 2);
+        }
+        this.layer.drawFeature(this.feature, this.style);
+    },
+
+    /**
+     * Method: up
+     * Finish drawing the feature
+     *
+     * Parameters:
+     * evt - {Event} The mouse up event
+     */
+    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]);
+        }
+    },
+
+    /**
+     * Method: out
+     * Finish drawing the feature.
+     *
+     * Parameters:
+     * evt - {Event} The mouse out event
+     */
+    out: function(evt) {
+        this.finalize();
+    },
+
+    /**
+     * Method: createGeometry
+     * Create the new polygon geometry.  This is called at the start of the
+     *     drag and at any point during the drag if the number of sides
+     *     changes.
+     */
+    createGeometry: function() {
+        this.angle = Math.PI * ((1/this.sides) - (1/2));
+        if(this.snapAngle) {
+            this.angle += this.snapAngle * (Math.PI / 180);
+        }
+        this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
+            this.origin, this.radius, this.sides, this.snapAngle
+        );
+    },
+    
+    /**
+     * Method: modifyGeometry
+     * Modify the polygon geometry in place.
+     */
+    modifyGeometry: function() {
+        var angle, point;
+        var ring = this.feature.geometry.components[0];
+        // if the number of sides ever changes, create a new geometry
+        if(ring.components.length != (this.sides + 1)) {
+            this.createGeometry();
+            ring = this.feature.geometry.components[0];
+        }
+        for(var i=0; i<this.sides; ++i) {
+            point = ring.components[i];
+            angle = this.angle + (i * 2 * Math.PI / this.sides);
+            point.x = this.origin.x + (this.radius * Math.cos(angle));
+            point.y = this.origin.y + (this.radius * Math.sin(angle));
+            point.clearBounds();
+        }
+    },
+    
+    /**
+     * Method: calculateAngle
+     * Calculate the angle based on settings.
+     *
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     * evt - {Event}
+     */
+    calculateAngle: function(point, evt) {
+        var alpha = Math.atan2(point.y - this.origin.y,
+                               point.x - this.origin.x);
+        if(this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {
+            var snapAngleRad = (Math.PI / 180) * this.snapAngle;
+            this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;
+        } else {
+            this.angle = alpha;
+        }
+    },
+
+    /**
+     * APIMethod: cancel
+     * Finish the geometry and call the "cancel" callback.
+     */
+    cancel: function() {
+        // the polygon geometry gets cloned in the callback method
+        this.callback("cancel", null);
+        this.finalize();
+    },
+
+    /**
+     * Method: finalize
+     * Finish the geometry and call the "done" callback.
+     */
+    finalize: function() {
+        this.origin = null;
+        this.radius = this.options.radius;
+    },
+
+    /**
+     * APIMethod: clear
+     * Clear any rendered features on the temporary layer.  This is called
+     *     when the handler is deactivated, canceled, or done (unless persist
+     *     is true).
+     */
+    clear: function() {
+        if (this.layer) {
+            this.layer.renderer.clear();
+            this.layer.destroyFeatures();
+        }
+    },
+    
+    /**
+     * Method: callback
+     * Trigger the control's named callback with the given arguments
+     *
+     * Parameters:
+     * name - {String} The key for the callback that is one of the properties
+     *     of the handler's callbacks object.
+     * args - {Array} An array of arguments with which to call the callback
+     *     (defined by the control).
+     */
+    callback: function (name, args) {
+        // override the callback method to always send the polygon geometry
+        if (this.callbacks[name]) {
+            this.callbacks[name].apply(this.control,
+                                       [this.feature.geometry.clone()]);
+        }
+        // since sketch features are added to the temporary layer
+        // they must be cleared here if done or cancel
+        if(!this.persist && (name == "done" || name == "cancel")) {
+            this.clear();
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
+});
+/* ======================================================================
+    OpenLayers/Handler/Feature.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Handler.js
+ */
+
+/**
+ * Class: OpenLayers.Handler.Feature 
+ * Handler to respond to mouse events related to a drawn feature.  Callbacks
+ *     with the following keys will be notified of the following events
+ *     associated with features: click, clickout, over, out, and dblclick.
+ *
+ * This handler stops event propagation for mousedown and mouseup if those
+ *     browser events target features that can be selected.
+ */
+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
+
+    /**
+     * Property: EVENTMAP
+     * {Object} A object mapping the browser events to objects with callback
+     *     keys for in and out.
+     */
+    EVENTMAP: {
+        'click': {'in': 'click', 'out': 'clickout'},
+        'mousemove': {'in': 'over', 'out': 'out'},
+        'dblclick': {'in': 'dblclick', 'out': null},
+        'mousedown': {'in': null, 'out': null},
+        'mouseup': {'in': null, 'out': null}
+    },
+
+    /**
+     * Property: feature
+     * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
+     */
+    feature: null,
+
+    /**
+     * Property: lastFeature
+     * {<OpenLayers.Feature.Vector>} The last feature that was handled.
+     */
+    lastFeature: null,
+
+    /**
+     * Property: down
+     * {<OpenLayers.Pixel>} The location of the last mousedown.
+     */
+    down: null,
+
+    /**
+     * Property: up
+     * {<OpenLayers.Pixel>} The location of the last mouseup.
+     */
+    up: null,
+    
+    /**
+     * Property: clickTolerance
+     * {Number} The number of pixels the mouse can move between mousedown
+     *     and mouseup for the event to still be considered a click.
+     *     Dragging the map should not trigger the click and clickout callbacks
+     *     unless the map is moved by less than this tolerance. Defaults to 4.
+     */
+    clickTolerance: 4,
+
+    /**
+     * Property: geometryTypes
+     * To restrict dragging to a limited set of geometry types, send a list
+     * of strings corresponding to the geometry class names.
+     * 
+     * @type Array(String)
+     */
+    geometryTypes: null,
+
+    /**
+     * Property: stopClick
+     * {Boolean} If stopClick is set to true, handled clicks do not
+     *      propagate to other click listeners. Otherwise, handled clicks
+     *      do propagate. Unhandled clicks always propagate, whatever the
+     *      value of stopClick. Defaults to true.
+     */
+    stopClick: true,
+
+    /**
+     * Property: stopDown
+     * {Boolean} If stopDown is set to true, handled mousedowns do not
+     *      propagate to other mousedown listeners. Otherwise, handled
+     *      mousedowns do propagate. Unhandled mousedowns always propagate,
+     *      whatever the value of stopDown. Defaults to true.
+     */
+    stopDown: true,
+
+    /**
+     * Property: stopUp
+     * {Boolean} If stopUp is set to true, handled mouseups do not
+     *      propagate to other mouseup listeners. Otherwise, handled mouseups
+     *      do propagate. Unhandled mouseups always propagate, whatever the
+     *      value of stopUp. Defaults to false.
+     */
+    stopUp: false,
+    
+    /**
+     * Constructor: OpenLayers.Handler.Feature
+     *
+     * Parameters:
+     * control - {<OpenLayers.Control>} 
+     * 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.
+     * options - {Object} 
+     */
+    initialize: function(control, layer, callbacks, options) {
+        OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
+        this.layer = layer;
+    },
+
+
+    /**
+     * Method: mousedown
+     * Handle mouse down.  Stop propagation if a feature is targeted by this
+     *     event (stops map dragging during feature selection).
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    mousedown: function(evt) {
+        this.down = evt.xy;
+        return this.handle(evt) ? !this.stopDown : true;
+    },
+    
+    /**
+     * Method: mouseup
+     * Handle mouse up.  Stop propagation if a feature is targeted by this
+     *     event.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     */
+    mouseup: function(evt) {
+        this.up = evt.xy;
+        return this.handle(evt) ? !this.stopUp : true;
+    },
+
+    /**
+     * Method: click
+     * Handle click.  Call the "click" callback if click on a feature,
+     *     or the "clickout" callback if click outside any feature.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    click: function(evt) {
+        return this.handle(evt) ? !this.stopClick : true;
+    },
+        
+    /**
+     * Method: mousemove
+     * Handle mouse moves.  Call the "over" callback if moving in to a feature,
+     *     or the "out" callback if moving out of a feature.
+     * 
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    mousemove: function(evt) {
+        if (!this.callbacks['over'] && !this.callbacks['out']) {
+            return true;
+        }     
+        this.handle(evt);
+        return true;
+    },
+    
+    /**
+     * Method: dblclick
+     * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
+     *
+     * Parameters:
+     * evt - {Event} 
+     *
+     * Returns:
+     * {Boolean}
+     */
+    dblclick: function(evt) {
+        return !this.handle(evt);
+    },
+
+    /**
+     * Method: geometryTypeMatches
+     * Return true if the geometry type of the passed feature matches
+     *     one of the geometry types in the geometryTypes array.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Vector.Feature>}
+     *
+     * Returns:
+     * {Boolean}
+     */
+    geometryTypeMatches: function(feature) {
+        return this.geometryTypes == null ||
+            OpenLayers.Util.indexOf(this.geometryTypes,
+                                    feature.geometry.CLASS_NAME) > -1;
+    },
+
+    /**
+     * Method: handle
+     *
+     * Parameters:
+     * evt - {Event}
+     *
+     * Returns:
+     * {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) {
+            // 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
+                    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
+                    this.triggerCallback(type, 'in', [this.feature]);
+                }
+                this.lastFeature = this.feature;
+                handled = true;
+            } else {
+                // not in to a feature
+                if(this.lastFeature && (previouslyIn && inNew || click)) {
+                    // out of last feature for the first time
+                    this.triggerCallback(type, 'out', [this.lastFeature]);
+                }
+                // next time the mouse goes in a feature whose geometry type
+                // doesn't match we don't want to call the 'out' callback
+                // again, so let's set this.feature to null so that
+                // previouslyIn will evaluate to false the next time
+                // we enter handle. Yes, a bit hackish...
+                this.feature = null;
+            }
+        } else {
+            if(this.lastFeature && (previouslyIn || click)) {
+                this.triggerCallback(type, 'out', [this.lastFeature]);
+            }
+        }
+        return handled;
+    },
+    
+    /**
+     * Method: triggerCallback
+     * Call the callback keyed in the event map with the supplied arguments.
+     *     For click and clickout, the <clickTolerance> is checked first.
+     *
+     * Parameters:
+     * type - {String}
+     */
+    triggerCallback: function(type, mode, args) {
+        var key = this.EVENTMAP[type][mode];
+        if(key) {
+            if(type == 'click' && this.up && this.down) {
+                // for click/clickout, only trigger callback if tolerance is met
+                var dpx = Math.sqrt(
+                    Math.pow(this.up.x - this.down.x, 2) +
+                    Math.pow(this.up.y - this.down.y, 2)
+                );
+                if(dpx <= this.clickTolerance) {
+                    this.callback(key, args);
+                }
+            } else {
+                this.callback(key, args);
+            }
+        }
+    },
+
+    /**
+     * Method: activate 
+     * Turn on the handler.  Returns false if the handler was already active.
+     *
+     * Returns:
+     * {Boolean}
+     */
+    activate: function() {
+        var activated = false;
+        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
+            this.moveLayerToTop();
+            this.map.events.on({
+                "removelayer": this.handleMapEvents,
+                "changelayer": this.handleMapEvents,
+                scope: this
+            });
+            activated = true;
+        }
+        return activated;
+    },
+    
+    /**
+     * Method: deactivate 
+     * Turn off the handler.  Returns false if the handler was already active.
+     *
+     * Returns: 
+     * {Boolean}
+     */
+    deactivate: function() {
+        var deactivated = false;
+        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
+            this.moveLayerBack();
+            this.feature = null;
+            this.lastFeature = null;
+            this.down = null;
+            this.up = null;
+            this.map.events.un({
+                "removelayer": this.handleMapEvents,
+                "changelayer": this.handleMapEvents,
+                scope: this
+            });
+            deactivated = true;
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method handleMapEvents
+     * 
+     * Parameters:
+     * evt - {Object}
+     */
+    handleMapEvents: function(evt) {
+        if (!evt.property || evt.property == "order") {
+            this.moveLayerToTop();
+        }
+    },
+    
+    /**
+     * Method: moveLayerToTop
+     * Moves the layer for this handler to the top, so mouse events can reach
+     * it.
+     */
+    moveLayerToTop: function() {
+        var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
+            this.layer.getZIndex()) + 1;
+        this.layer.setZIndex(index);
+        
+    },
+    
+    /**
+     * Method: moveLayerBack
+     * Moves the layer back to the position determined by the map's layers
+     * array.
+     */
+    moveLayerBack: function() {
+        var index = this.layer.getZIndex() - 1;
+        if (index >= this.map.Z_INDEX_BASE['Feature']) {
+            this.layer.setZIndex(index);
+        } else {
+            this.map.setLayerZIndex(this.layer,
+                this.map.getLayerIndex(this.layer));
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Handler.Feature"
+});
+/* ======================================================================
+    OpenLayers/Protocol/WFS/v1_1_0.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Protocol/WFS/v1.js
+ * @requires OpenLayers/Format/WFST/v1_1_0.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol.WFS.v1_1_0
+ * A WFS v1.1.0 protocol for vector layers.  Create a new instance with the
+ *     <OpenLayers.Protocol.WFS.v1_1_0> constructor.
+ *
+ * Differences from the v1.0.0 protocol:
+ *  - uses Filter Encoding 1.1.0 instead of 1.0.0
+ *  - uses GML 3 instead of 2 if no format is provided
+ *  
+ * Inherits from:
+ *  - <OpenLayers.Protocol.WFS.v1>
+ */
+OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
+    
+    /**
+     * Property: version
+     * {String} WFS version number.
+     */
+    version: "1.1.0",
+    
+    /**
+     * Constructor: OpenLayers.Protocol.WFS.v1_1_0
+     * A class for giving layers WFS v1.1.0 protocol.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options properties:
+     * featureType - {String} Local (without prefix) feature typeName (required).
+     * featureNS - {String} Feature namespace (optional).
+     * featurePrefix - {String} Feature namespace alias (optional - only used
+     *     if featureNS is provided).  Default is 'feature'.
+     * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
+     * outputFormat - {String} Optional output format to use for WFS GetFeature
+     *     requests. This can be any format advertized by the WFS's
+     *     GetCapabilities response. If set, an appropriate readFormat also
+     *     has to be provided, unless outputFormat is GML3, GML2 or JSON.
+     * readFormat - {<OpenLayers.Format>} An appropriate format parser if
+     *     outputFormat is none of GML3, GML2 or JSON.
+     */
+    initialize: function(options) {
+        OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);
+        if (this.outputFormat && !this.readFormat) {
+            if (this.outputFormat.toLowerCase() == "gml2") {
+                this.readFormat = new OpenLayers.Format.GML.v2({
+                    featureType: this.featureType,
+                    featureNS: this.featureNS,
+                    geometryName: this.geometryName
+                });
+            } else if (this.outputFormat.toLowerCase() == "json") {
+                this.readFormat = new OpenLayers.Format.GeoJSON();
+            }
+        }
+    },
+   
+    CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0"
+});
+/* ======================================================================
+    OpenLayers/Control/ArgParser.js
+   ====================================================================== */
+
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ */
+
+/**
+ * Class: OpenLayers.Control.ArgParser
+ * 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>
+ */
+OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
+
+    /**
+     * Parameter: center
+     * {<OpenLayers.LonLat>}
+     */
+    center: null,
+    
+    /**
+     * Parameter: zoom
+     * {int}
+     */
+    zoom: null,
+
+    /**
+     * Parameter: layers 
+     * {Array(<OpenLayers.Layer>)}
+     */
+    layers: null,
+    
+    /** 
+     * APIProperty: displayProjection
+     * {<OpenLayers.Projection>} Requires proj4js support. 
+     *     Projection used when reading the coordinates from the URL. This will
+     *
+     *     reproject the map coordinates from the URL into the map's
+     *     projection.
+     *
+     *     If you are using this functionality, be aware that any permalink
+     *     which is added to the map will determine the coordinate type which
+     *     is read from the URL, which means you should not add permalinks with
+     *     different displayProjections to the same map. 
+     */
+    displayProjection: null, 
+
+    /**
+     * Constructor: OpenLayers.Control.ArgParser
+     *
+     * Parameters:
+     * options - {Object}
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: setMap
+     * Set the map property for the control. 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>} 
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        //make sure we dont already have an arg parser attached
+        for(var i=0, len=this.map.controls.length; i<len; i++) {
+            var control = this.map.controls[i];
+            if ( (control != this) &&
+                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
+                
+                // If a second argparser is added to the map, then we 
+                // override the displayProjection to be the one added to the
+                // map. 
+                if (control.displayProjection != this.displayProjection) {
+                    this.displayProjection = control.displayProjection;
+                }    
+                
+                break;
+            }
+        }
+        if (i == this.map.controls.length) {
+
+            var args = OpenLayers.Util.getParameters();
+            // Be careful to set layer first, to not trigger unnecessary layer loads
+            if (args.layers) {
+                this.layers = args.layers;
+    
+                // when we add a new layer, set its visibility 
+                this.map.events.register('addlayer', this, 
+                                         this.configureLayers);
+                this.configureLayers();
+            }
+            if (args.lat && args.lon) {
+                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
+                                                    parseFloat(args.lat));
+                if (args.zoom) {
+                    this.zoom = parseInt(args.zoom);
+                }
+    
+                // when we add a new baselayer to see when we can set the center
+                this.map.events.register('changebaselayer', this, 
+                                         this.setCenter);
+                this.setCenter();
+            }
+        }
+    },
+   
+    /** 
+     * Method: setCenter
+     * As soon as a baseLayer has been loaded, we center and zoom
+     *   ...and remove the handler.
+     */
+    setCenter: function() {
+        
+        if (this.map.baseLayer) {
+            //dont need to listen for this one anymore
+            this.map.events.unregister('changebaselayer', this, 
+                                       this.setCenter);
+            
+            if (this.displayProjection) {
+                this.center.transform(this.displayProjection, 
+                                      this.map.getProjectionObject()); 
+            }      
+
+            this.map.setCenter(this.center, this.zoom);
+        }
+    },
+
+    /** 
+     * Method: configureLayers
+     * As soon as all the layers are loaded, cycle through them and 
+     *   hide or show them. 
+     */
+    configureLayers: function() {
+
+        if (this.layers.length == this.map.layers.length) { 
+            this.map.events.unregister('addlayer', this, this.configureLayers);
+
+            for(var i=0, len=this.layers.length; i<len; i++) {
+                
+                var layer = this.map.layers[i];
+                var c = this.layers.charAt(i);
+                
+                if (c == "B") {
+                    this.map.setBaseLayer(layer);
+                } else if ( (c == "T") || (c == "F") ) {
+                    layer.setVisibility(c == "T");
+                }
+            }
+        }
+    },     
+
+    CLASS_NAME: "OpenLayers.Control.ArgParser"
+});

Modified: branches/fusion-2.2/lib/proj4js-combined.js
===================================================================
--- branches/fusion-2.2/lib/proj4js-combined.js	2011-01-20 19:46:05 UTC (rev 2322)
+++ branches/fusion-2.2/lib/proj4js-combined.js	2011-01-20 19:56:47 UTC (rev 2323)
@@ -487,7 +487,7 @@
           (srsCode.indexOf('PROJCS') >= 0) ||
           (srsCode.indexOf('LOCAL_CS') >= 0)) {
             this.parseWKT(srsCode);
-            this.datum = new Proj4js.datum(this);
+            this.deriveConstants();
             this.loadProjCode(this.projName);
             return;
       }
@@ -759,9 +759,9 @@
         this.unitsPerMeter = parseFloat(wktArray.shift());
         break;
       case 'PARAMETER':
-        var name = wktName;
-        var value = parseFloat(parseFloat(wktArray.shift()));
-        //there amy be many variations on the wktName values, add in case
+        var name = wktName.toLowerCase();
+        var value = parseFloat(wktArray.shift());
+        //there may be many variations on the wktName values, add in case
         //statements as required
         switch (name) {
           case 'false_easting':
@@ -774,10 +774,10 @@
             this.k0 = value;
             break;
           case 'central_meridian':
-            this.long0 = value;
+            this.long0 = value*Proj4js.common.D2R;
             break;
           case 'latitude_of_origin':
-            this.lat0 = value;
+            this.lat0 = value*Proj4js.common.D2R;
             break;
           case 'more_here':
             break;
@@ -1313,7 +1313,7 @@
 
 /* --------------------------------------------------------------
  * Following iterative algorithm was developped by
- * "Institut für Erdmessung", University of Hannover, July 1988.
+ * "Institut f�r Erdmessung", University of Hannover, July 1988.
  * Internet: www.ife.uni-hannover.de
  * Iterative computation of CPHI,SPHI and Height.
  * Iteration of CPHI and SPHI to 10**-12 radian resp.
@@ -3087,6 +3087,8 @@
     		lon = (x == 0. && y == 0.) ? 0. : Math.atan2(x, y);
     		break;
     	}
+        p.x = Proj4js.common.adjust_lon(lon + this.long0);
+        p.y = lat;
     } else {
     	rho = Math.sqrt(x*x + y*y);
     	switch (this.mode) {
@@ -4701,6 +4703,7 @@
         cosphi = Math.cos(phi);
         coslam = Math.cos(lam);
         switch (this.mode) {
+          case this.OBLIQ:
           case this.EQUIT:
             y = (this.mode == this.EQUIT) ? 1. + cosphi * coslam : 1. + this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
             if (y <= Proj4js.common.EPSLN) {

Modified: branches/fusion-2.2/lib/proj4js-compressed.js
===================================================================
--- branches/fusion-2.2/lib/proj4js-compressed.js	2011-01-20 19:46:05 UTC (rev 2322)
+++ branches/fusion-2.2/lib/proj4js-compressed.js	2011-01-20 19:56:47 UTC (rev 2323)
@@ -34,7 +34,7 @@
 Proj4js.extend(extended,parent);}
 Class.prototype=extended;return Class;},bind:function(func,object){var args=Array.prototype.slice.apply(arguments,[2]);return function(){var newArgs=args.concat(Array.prototype.slice.apply(arguments,[0]));return func.apply(object,newArgs);};},scriptName:"proj4js-compressed.js",defsLookupService:'http://spatialreference.org/ref',libPath:null,getScriptLocation:function(){if(this.libPath)return this.libPath;var scriptName=this.scriptName;var scriptNameLen=scriptName.length;var scripts=document.getElementsByTagName('script');for(var i=0;i<scripts.length;i++){var src=scripts[i].getAttribute('src');if(src){var index=src.lastIndexOf(scriptName);if((index>-1)&&(index+scriptNameLen==src.length)){this.libPath=src.slice(0,-scriptNameLen);break;}}}
 return this.libPath||"";},loadScript:function(url,onload,onfail,loadCheck){var script=document.createElement('script');script.defer=false;script.type="text/javascript";script.id=url;script.src=url;script.onload=onload;script.onerror=onfail;script.loadCheck=loadCheck;if(/MSIE/.test(navigator.userAgent)){script.onreadystatechange=this.checkReadyState;}
-document.getElementsByTagName('head')[0].appendChild(script);},checkReadyState:function(){if(this.readyState=='loaded'){if(!this.loadCheck()){this.onerror();}else{this.onload();}}}};Proj4js.Proj=Proj4js.Class({readyToUse:false,title:null,projName:null,units:null,datum:null,x0:0,y0:0,localCS:false,initialize:function(srsCode){this.srsCodeInput=srsCode;if((srsCode.indexOf('GEOGCS')>=0)||(srsCode.indexOf('GEOCCS')>=0)||(srsCode.indexOf('PROJCS')>=0)||(srsCode.indexOf('LOCAL_CS')>=0)){this.parseWKT(srsCode);this.datum=new Proj4js.datum(this);this.loadProjCode(this.projName);return;}
+document.getElementsByTagName('head')[0].appendChild(script);},checkReadyState:function(){if(this.readyState=='loaded'){if(!this.loadCheck()){this.onerror();}else{this.onload();}}}};Proj4js.Proj=Proj4js.Class({readyToUse:false,title:null,projName:null,units:null,datum:null,x0:0,y0:0,localCS:false,initialize:function(srsCode){this.srsCodeInput=srsCode;if((srsCode.indexOf('GEOGCS')>=0)||(srsCode.indexOf('GEOCCS')>=0)||(srsCode.indexOf('PROJCS')>=0)||(srsCode.indexOf('LOCAL_CS')>=0)){this.parseWKT(srsCode);this.deriveConstants();this.loadProjCode(this.projName);return;}
 if(srsCode.indexOf('urn:')==0){var urn=srsCode.split(':');if((urn[1]=='ogc'||urn[1]=='x-ogc')&&(urn[2]=='def')&&(urn[3]=='crs')){srsCode=urn[4]+':'+urn[urn.length-1];}}else if(srsCode.indexOf('http://')==0){var url=srsCode.split('#');if(url[0].match(/epsg.org/)){srsCode='EPSG:'+url[1];}else if(url[0].match(/RIG.xml/)){srsCode='IGNF:'+url[1];}}
 this.srsCode=srsCode.toUpperCase();if(this.srsCode.indexOf("EPSG")==0){this.srsCode=this.srsCode;this.srsAuth='epsg';this.srsProjNumber=this.srsCode.substring(5);}else if(this.srsCode.indexOf("IGNF")==0){this.srsCode=this.srsCode;this.srsAuth='IGNF';this.srsProjNumber=this.srsCode.substring(5);}else if(this.srsCode.indexOf("CRS")==0){this.srsCode=this.srsCode;this.srsAuth='CRS';this.srsProjNumber=this.srsCode.substring(4);}else{this.srsAuth='';this.srsProjNumber=this.srsCode;}
 this.loadProjDefinition();},loadProjDefinition:function(){if(Proj4js.defs[this.srsCode]){this.defsLoaded();return;}
@@ -44,7 +44,7 @@
 switch(wktObject){case'LOCAL_CS':this.projName='identity'
 this.localCS=true;this.srsCode=wktName;break;case'GEOGCS':this.projName='longlat'
 this.geocsCode=wktName;if(!this.srsCode)this.srsCode=wktName;break;case'PROJCS':this.srsCode=wktName;break;case'GEOCCS':break;case'PROJECTION':this.projName=Proj4js.wktProjections[wktName]
-break;case'DATUM':this.datumName=wktName;break;case'LOCAL_DATUM':this.datumCode='none';break;case'SPHEROID':this.ellps=wktName;this.a=parseFloat(wktArray.shift());this.rf=parseFloat(wktArray.shift());break;case'PRIMEM':this.from_greenwich=parseFloat(wktArray.shift());break;case'UNIT':this.units=wktName;this.unitsPerMeter=parseFloat(wktArray.shift());break;case'PARAMETER':var name=wktName;var value=parseFloat(parseFloat(wktArray.shift()));switch(name){case'false_easting':this.x0=value;break;case'false_northing':this.y0=value;break;case'scale_factor':this.k0=value;break;case'central_meridian':this.long0=value;break;case'latitude_of_origin':this.lat0=value;break;case'more_here':break;default:break;}
+break;case'DATUM':this.datumName=wktName;break;case'LOCAL_DATUM':this.datumCode='none';break;case'SPHEROID':this.ellps=wktName;this.a=parseFloat(wktArray.shift());this.rf=parseFloat(wktArray.shift());break;case'PRIMEM':this.from_greenwich=parseFloat(wktArray.shift());break;case'UNIT':this.units=wktName;this.unitsPerMeter=parseFloat(wktArray.shift());break;case'PARAMETER':var name=wktName.toLowerCase();var value=parseFloat(wktArray.shift());switch(name){case'false_easting':this.x0=value;break;case'false_northing':this.y0=value;break;case'scale_factor':this.k0=value;break;case'central_meridian':this.long0=value*Proj4js.common.D2R;break;case'latitude_of_origin':this.lat0=value*Proj4js.common.D2R;break;case'more_here':break;default:break;}
 break;case'TOWGS84':this.datum_params=wktArray;break;case'MORE_HERE':break;default:break;}
 for(var i=0;i<wktArray.length;++i){this.parseWKT(wktArray[i]);}},parseDefs:function(){this.defData=Proj4js.defs[this.srsCode];var paramName,paramVal;if(!this.defData){return;}
 var paramArray=this.defData.split("+");for(var prop=0;prop<paramArray.length;prop++){var property=paramArray[prop].split("=");paramName=property[0].toLowerCase();paramVal=property[1];switch(paramName.replace(/\s/gi,"")){case"":break;case"title":this.title=paramVal;break;case"proj":this.projName=paramVal.replace(/\s/gi,"");break;case"units":this.units=paramVal.replace(/\s/gi,"");break;case"datum":this.datumCode=paramVal.replace(/\s/gi,"");break;case"nadgrids":this.nagrids=paramVal.replace(/\s/gi,"");break;case"ellps":this.ellps=paramVal.replace(/\s/gi,"");break;case"a":this.a=parseFloat(paramVal);break;case"b":this.b=parseFloat(paramVal);break;case"rf":this.rf=parseFloat(paramVal);break;case"lat_0":this.lat0=paramVal*Proj4js.common.D2R;break;case"lat_1":this.lat1=paramVal*Proj4js.common.D2R;break;case"lat_2":this.lat2=paramVal*Proj4js.common.D2R;break;case"lat_ts":this.lat_ts=paramVal*Proj4js.common.D2R;break;case"lon_0":this.long0=paramVal*Proj4js.common.D2R;break;case"alpha
 ":this.alpha=parseFloat(paramVal)*Proj4js.common.D2R;break;case"lonc":this.longc=paramVal*Proj4js.common.D2R;break;case"x_0":this.x0=parseFloat(paramVal);break;case"y_0":this.y0=parseFloat(paramVal);break;case"k_0":this.k0=parseFloat(paramVal);break;case"k":this.k0=parseFloat(paramVal);break;case"r_a":this.R_A=true;break;case"zone":this.zone=parseInt(paramVal);break;case"south":this.utmSouth=true;break;case"towgs84":this.datum_params=paramVal.split(",");break;case"to_meter":this.to_meter=parseFloat(paramVal);break;case"from_greenwich":this.from_greenwich=paramVal*Proj4js.common.D2R;break;case"pm":paramVal=paramVal.replace(/\s/gi,"");this.from_greenwich=Proj4js.PrimeMeridian[paramVal]?Proj4js.PrimeMeridian[paramVal]:parseFloat(paramVal);this.from_greenwich*=Proj4js.common.D2R;break;case"no_defs":break;default:}}
@@ -151,7 +151,8 @@
 if(cosc!=0.||x!=0.)lon=Math.atan2(x*sinc,cosc*rh);break;case this.OBLIQ:if(Math.abs(rh)<=Proj4js.common.EPSLN){lat=this.phi0;}else{lat=Math.asin(cosc*sinph0+y*sinc*cosph0/rh);}
 c=cosc-sinph0*Math.sin(lat);if(c!=0.||x!=0.){lon=Math.atan2(x*sinc*cosph0,c*rh);}
 break;case this.N_POLE:y=-y;case this.S_POLE:if(Math.abs(rh)<=Proj4js.common.EPSLN){lat=this.phi0;}else{lat=Math.asin(this.mode==this.S_POLE?-cosc:cosc);}
-lon=(x==0.&&y==0.)?0.:Math.atan2(x,y);break;}}else{rho=Math.sqrt(x*x+y*y);switch(this.mode){case this.OBLIQ:case this.EQUIT:tp=2.*Math.atan2(rho*this.cosX1,this.akm1);cosphi=Math.cos(tp);sinphi=Math.sin(tp);if(rho==0.0){phi_l=Math.asin(cosphi*this.sinX1);}else{phi_l=Math.asin(cosphi*this.sinX1+(y*sinphi*this.cosX1/rho));}
+lon=(x==0.&&y==0.)?0.:Math.atan2(x,y);break;}
+p.x=Proj4js.common.adjust_lon(lon+this.long0);p.y=lat;}else{rho=Math.sqrt(x*x+y*y);switch(this.mode){case this.OBLIQ:case this.EQUIT:tp=2.*Math.atan2(rho*this.cosX1,this.akm1);cosphi=Math.cos(tp);sinphi=Math.sin(tp);if(rho==0.0){phi_l=Math.asin(cosphi*this.sinX1);}else{phi_l=Math.asin(cosphi*this.sinX1+(y*sinphi*this.cosX1/rho));}
 tp=Math.tan(.5*(Proj4js.common.HALF_PI+phi_l));x*=sinphi;y=rho*this.cosX1*cosphi-y*this.sinX1*sinphi;pi2=Proj4js.common.HALF_PI;halfe=.5*this.e;break;case this.N_POLE:y=-y;case this.S_POLE:tp=-rho/this.akm1;phi_l=Proj4js.common.HALF_PI-2.*Math.atan(tp);pi2=-Proj4js.common.HALF_PI;halfe=-.5*this.e;break;}
 for(i=this.NITER;i--;phi_l=lat){sinphi=this.e*Math.sin(phi_l);lat=2.*Math.atan(tp*Math.pow((1.+sinphi)/(1.-sinphi),halfe))-pi2;if(Math.abs(phi_l-lat)<this.CONV){if(this.mode==this.S_POLE)lat=-lat;lon=(x==0.&&y==0.)?0.:Math.atan2(x,y);p.x=Proj4js.common.adjust_lon(lon+this.long0);p.y=lat;return p;}}}}};Proj4js.Proj.nzmg={iterations:1,init:function(){this.A=new Array();this.A[1]=+0.6399175073;this.A[2]=-0.1358797613;this.A[3]=+0.063294409;this.A[4]=-0.02526853;this.A[5]=+0.0117879;this.A[6]=-0.0055161;this.A[7]=+0.0026906;this.A[8]=-0.001333;this.A[9]=+0.00067;this.A[10]=-0.00034;this.B_re=new Array();this.B_im=new Array();this.B_re[1]=+0.7557853228;this.B_im[1]=0.0;this.B_re[2]=+0.249204646;this.B_im[2]=+0.003371507;this.B_re[3]=-0.001541739;this.B_im[3]=+0.041058560;this.B_re[4]=-0.10162907;this.B_im[4]=+0.01727609;this.B_re[5]=-0.26623489;this.B_im[5]=-0.36249218;this.B_re[6]=-0.6870983;this.B_im[6]=-1.1651967;this.C_re=new Array();this.C_im=new Array();this.C_re[1]=+1.3231
 270439;this.C_im[1]=0.0;this.C_re[2]=-0.577245789;this.C_im[2]=-0.007809598;this.C_re[3]=+0.508307513;this.C_im[3]=-0.112208952;this.C_re[4]=-0.15094762;this.C_im[4]=+0.18200602;this.C_re[5]=+1.01418179;this.C_im[5]=+1.64497696;this.C_re[6]=+1.9660549;this.C_im[6]=+2.5127645;this.D=new Array();this.D[1]=+1.5627014243;this.D[2]=+0.5185406398;this.D[3]=-0.03333098;this.D[4]=-0.1052906;this.D[5]=-0.0368594;this.D[6]=+0.007317;this.D[7]=+0.01220;this.D[8]=+0.00394;this.D[9]=-0.0013;},forward:function(p){var lon=p.x;var lat=p.y;var delta_lat=lat-this.lat0;var delta_lon=lon-this.long0;var d_phi=delta_lat/Proj4js.common.SEC_TO_RAD*1E-5;var d_lambda=delta_lon;var d_phi_n=1;var d_psi=0;for(n=1;n<=10;n++){d_phi_n=d_phi_n*d_phi;d_psi=d_psi+this.A[n]*d_phi_n;}
 var th_re=d_psi;var th_im=d_lambda;var th_n_re=1;var th_n_im=0;var th_n_re1;var th_n_im1;var z_re=0;var z_im=0;for(n=1;n<=6;n++){th_n_re1=th_n_re*th_re-th_n_im*th_im;th_n_im1=th_n_im*th_re+th_n_re*th_im;th_n_re=th_n_re1;th_n_im=th_n_im1;z_re=z_re+this.B_re[n]*th_n_re-this.B_im[n]*th_n_im;z_im=z_im+this.B_im[n]*th_n_re+this.B_re[n]*th_n_im;}
@@ -199,7 +200,7 @@
 var theta=0.0;if(rh1!=0){theta=Math.atan2((con*x),(con*y));}
 if((rh1!=0)||(this.ns>0.0)){con=1.0/this.ns;ts=Math.pow((rh1/(this.a*this.f0)),con);lat=Proj4js.common.phi2z(this.e,ts);if(lat==-9999)return null;}else{lat=-Proj4js.common.HALF_PI;}
 lon=Proj4js.common.adjust_lon(theta/this.ns+this.long0);p.x=lon;p.y=lat;return p;}};Proj4js.Proj.laea={S_POLE:1,N_POLE:2,EQUIT:3,OBLIQ:4,init:function(){var t=Math.abs(this.lat0);if(Math.abs(t-Proj4js.common.HALF_PI)<Proj4js.common.EPSLN){this.mode=this.lat0<0.?this.S_POLE:this.N_POLE;}else if(Math.abs(t)<Proj4js.common.EPSLN){this.mode=this.EQUIT;}else{this.mode=this.OBLIQ;}
-if(this.es>0){var sinphi;this.qp=Proj4js.common.qsfnz(this.e,1.0);this.mmf=.5/(1.-this.es);this.apa=this.authset(this.es);switch(this.mode){case this.N_POLE:case this.S_POLE:this.dd=1.;break;case this.EQUIT:this.rq=Math.sqrt(.5*this.qp);this.dd=1./this.rq;this.xmf=1.;this.ymf=.5*this.qp;break;case this.OBLIQ:this.rq=Math.sqrt(.5*this.qp);sinphi=Math.sin(this.lat0);this.sinb1=Proj4js.common.qsfnz(this.e,sinphi)/this.qp;this.cosb1=Math.sqrt(1.-this.sinb1*this.sinb1);this.dd=Math.cos(this.lat0)/(Math.sqrt(1.-this.es*sinphi*sinphi)*this.rq*this.cosb1);this.ymf=(this.xmf=this.rq)/this.dd;this.xmf*=this.dd;break;}}else{if(this.mode==this.OBLIQ){this.sinph0=Math.sin(this.lat0);this.cosph0=Math.cos(this.lat0);}}},forward:function(p){var x,y;var lam=p.x;var phi=p.y;lam=Proj4js.common.adjust_lon(lam-this.long0);if(this.sphere){var coslam,cosphi,sinphi;sinphi=Math.sin(phi);cosphi=Math.cos(phi);coslam=Math.cos(lam);switch(this.mode){case this.EQUIT:y=(this.mode==this.EQUIT)?1.+cosphi*co
 slam:1.+this.sinph0*sinphi+this.cosph0*cosphi*coslam;if(y<=Proj4js.common.EPSLN){Proj4js.reportError("laea:fwd:y less than eps");return null;}
+if(this.es>0){var sinphi;this.qp=Proj4js.common.qsfnz(this.e,1.0);this.mmf=.5/(1.-this.es);this.apa=this.authset(this.es);switch(this.mode){case this.N_POLE:case this.S_POLE:this.dd=1.;break;case this.EQUIT:this.rq=Math.sqrt(.5*this.qp);this.dd=1./this.rq;this.xmf=1.;this.ymf=.5*this.qp;break;case this.OBLIQ:this.rq=Math.sqrt(.5*this.qp);sinphi=Math.sin(this.lat0);this.sinb1=Proj4js.common.qsfnz(this.e,sinphi)/this.qp;this.cosb1=Math.sqrt(1.-this.sinb1*this.sinb1);this.dd=Math.cos(this.lat0)/(Math.sqrt(1.-this.es*sinphi*sinphi)*this.rq*this.cosb1);this.ymf=(this.xmf=this.rq)/this.dd;this.xmf*=this.dd;break;}}else{if(this.mode==this.OBLIQ){this.sinph0=Math.sin(this.lat0);this.cosph0=Math.cos(this.lat0);}}},forward:function(p){var x,y;var lam=p.x;var phi=p.y;lam=Proj4js.common.adjust_lon(lam-this.long0);if(this.sphere){var coslam,cosphi,sinphi;sinphi=Math.sin(phi);cosphi=Math.cos(phi);coslam=Math.cos(lam);switch(this.mode){case this.OBLIQ:case this.EQUIT:y=(this.mode==this.EQU
 IT)?1.+cosphi*coslam:1.+this.sinph0*sinphi+this.cosph0*cosphi*coslam;if(y<=Proj4js.common.EPSLN){Proj4js.reportError("laea:fwd:y less than eps");return null;}
 y=Math.sqrt(2./y);x=y*cosphi*Math.sin(lam);y*=(this.mode==this.EQUIT)?sinphi:this.cosph0*sinphi-this.sinph0*cosphi*coslam;break;case this.N_POLE:coslam=-coslam;case this.S_POLE:if(Math.abs(phi+this.phi0)<Proj4js.common.EPSLN){Proj4js.reportError("laea:fwd:phi < eps");return null;}
 y=Proj4js.common.FORTPI-phi*.5;y=2.*((this.mode==this.S_POLE)?Math.cos(y):Math.sin(y));x=y*Math.sin(lam);y*=coslam;break;}}else{var coslam,sinlam,sinphi,q,sinb=0.0,cosb=0.0,b=0.0;coslam=Math.cos(lam);sinlam=Math.sin(lam);sinphi=Math.sin(phi);q=Proj4js.common.qsfnz(this.e,sinphi);if(this.mode==this.OBLIQ||this.mode==this.EQUIT){sinb=q/this.qp;cosb=Math.sqrt(1.-sinb*sinb);}
 switch(this.mode){case this.OBLIQ:b=1.+this.sinb1*sinb+this.cosb1*cosb*coslam;break;case this.EQUIT:b=1.+cosb*coslam;break;case this.N_POLE:b=Proj4js.common.HALF_PI+phi;q=this.qp-q;break;case this.S_POLE:b=phi-Proj4js.common.HALF_PI;q=this.qp+q;break;}



More information about the fusion-commits mailing list