[OpenLayers-Commits] r7372 - in sandbox/topp/geoext: build examples lib lib/GoogleGears lib/OpenLayers lib/OpenLayers/Control lib/OpenLayers/Filter lib/OpenLayers/Format lib/OpenLayers/Format/Filter lib/OpenLayers/Format/GML lib/OpenLayers/Format/SLD lib/OpenLayers/Handler lib/OpenLayers/Layer lib/OpenLayers/Protocol lib/OpenLayers/Protocol/SQL lib/OpenLayers/Protocol/WFS lib/OpenLayers/Strategy tests tests/Format tests/Format/Filter tests/Format/GML tests/Format/GML/v2 tests/Protocol tests/Protocol/WFS tests/Strategy

commits at openlayers.org commits at openlayers.org
Tue Jun 17 16:36:54 EDT 2008


Author: tschaub
Date: 2008-06-17 16:36:54 -0400 (Tue, 17 Jun 2008)
New Revision: 7372

Added:
   sandbox/topp/geoext/examples/v2-bbox-wfs-gml.html
   sandbox/topp/geoext/examples/v2-fixed-http-gml.html
   sandbox/topp/geoext/examples/v2-fixed-wfs-gml.html
   sandbox/topp/geoext/examples/v2-wfs-t.html
   sandbox/topp/geoext/examples/vector2-wfs-t.html
   sandbox/topp/geoext/examples/vector2.html
   sandbox/topp/geoext/lib/GoogleGears/
   sandbox/topp/geoext/lib/GoogleGears/gears_init.js
   sandbox/topp/geoext/lib/OpenLayers/Control/DeleteFeature.js
   sandbox/topp/geoext/lib/OpenLayers/Filter/Spatial.js
   sandbox/topp/geoext/lib/OpenLayers/Format/Filter.js
   sandbox/topp/geoext/lib/OpenLayers/Format/Filter/
   sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1.js
   sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1_0_0.js
   sandbox/topp/geoext/lib/OpenLayers/Format/GML/
   sandbox/topp/geoext/lib/OpenLayers/Format/GML/Base.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/
   sandbox/topp/geoext/lib/OpenLayers/Protocol/HTTP.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/SQL.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/SQL/
   sandbox/topp/geoext/lib/OpenLayers/Protocol/SQL/GoogleGears.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/WFS.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/WFS/
   sandbox/topp/geoext/lib/OpenLayers/Protocol/WFS/v1_0_0.js
   sandbox/topp/geoext/lib/OpenLayers/Protocol/WFSV.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/
   sandbox/topp/geoext/lib/OpenLayers/Strategy/Autosave.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/BBOX.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/Composer.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/Fixed.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/GreedySave.js
   sandbox/topp/geoext/lib/OpenLayers/Strategy/Save.js
   sandbox/topp/geoext/tests/Format/Filter.html
   sandbox/topp/geoext/tests/Format/Filter/
   sandbox/topp/geoext/tests/Format/Filter/v1_0_0.html
   sandbox/topp/geoext/tests/Format/GML/
   sandbox/topp/geoext/tests/Format/GML/v2.html
   sandbox/topp/geoext/tests/Format/GML/v2/
   sandbox/topp/geoext/tests/Format/GML/v2/point-coord.xml
   sandbox/topp/geoext/tests/Format/GML/v2/point-coordinates.xml
   sandbox/topp/geoext/tests/Protocol.html
   sandbox/topp/geoext/tests/Protocol/
   sandbox/topp/geoext/tests/Protocol/HTTP.html
   sandbox/topp/geoext/tests/Protocol/WFS/
   sandbox/topp/geoext/tests/Protocol/WFS/v1_0_0.html
   sandbox/topp/geoext/tests/Strategy/
   sandbox/topp/geoext/tests/Strategy/Save.html
Modified:
   sandbox/topp/geoext/build/license.txt
   sandbox/topp/geoext/lib/OpenLayers.js
   sandbox/topp/geoext/lib/OpenLayers/Control/DrawFeature.js
   sandbox/topp/geoext/lib/OpenLayers/Format/GML.js
   sandbox/topp/geoext/lib/OpenLayers/Format/SLD.js
   sandbox/topp/geoext/lib/OpenLayers/Format/SLD/v1.js
   sandbox/topp/geoext/lib/OpenLayers/Format/XML.js
   sandbox/topp/geoext/lib/OpenLayers/Handler/Path.js
   sandbox/topp/geoext/lib/OpenLayers/Handler/Point.js
   sandbox/topp/geoext/lib/OpenLayers/Handler/Polygon.js
   sandbox/topp/geoext/lib/OpenLayers/Layer/Vector.js
   sandbox/topp/geoext/lib/OpenLayers/Layer/WFS.js
   sandbox/topp/geoext/lib/OpenLayers/Request.js
   sandbox/topp/geoext/tests/Format/GML.html
   sandbox/topp/geoext/tests/list-tests.html
   sandbox/topp/geoext/tests/xml_eq.js
Log:
merge r6920:HEAD from behavior sandbox

Modified: sandbox/topp/geoext/build/license.txt
===================================================================
--- sandbox/topp/geoext/build/license.txt	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/build/license.txt	2008-06-17 20:36:54 UTC (rev 7372)
@@ -44,6 +44,36 @@
 **/
 
 /**
+ * Contains portions of GoogleGears <http://gears.google.com/>:
+ *
+ * Copyright 2007, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of Google Inc. nor the names of its contributors may be
+ *     used to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/**
  * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
  * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
  *

Added: sandbox/topp/geoext/examples/v2-bbox-wfs-gml.html
===================================================================
--- sandbox/topp/geoext/examples/v2-bbox-wfs-gml.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/v2-bbox-wfs-gml.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>OpenLayers Vector Behavior Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+        <style type="text/css">
+            #map {
+                width: 512px;
+                height: 256px;
+                border: 1px solid gray;
+            }
+        </style>
+        <script src="../lib/Firebug/firebug.js"></script>
+        <script src="../lib/OpenLayers.js"></script>
+        <script type="text/javascript">
+            var map;
+        
+            function init(){
+                OpenLayers.ProxyHost= "/proxy/?url=";
+                map = new OpenLayers.Map('map');
+                var wms = new OpenLayers.Layer.WMS(
+                    "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
+                    {layers: 'basic'}
+                );
+
+                var layer = new OpenLayers.Layer.Vector("GML", {
+                    strategy: new OpenLayers.Strategy.BBOX(),
+                    protocol: new OpenLayers.Protocol.WFS.v1_0_0({
+                        url:  "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp",
+                        params: {typename: "OWLS", maxfeatures: 10}
+                    }),
+                    format: new OpenLayers.Format.GML()
+                });
+
+                map.addLayers([wms, layer]);
+                map.zoomToExtent(new OpenLayers.Bounds(
+                    -3.92, 44.34, 4.87, 49.55
+                ));
+            }
+        </script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">Vector Behavior Example</h1>
+        <p id="shortdesc">
+            Uses a new strategy, protocol, and format combination.
+        </p>
+        <div id="map"></div>
+        <div id="docs">
+            <p>The vector layer shown uses the Fixed strategy, the HTTP protocol,
+            and the GML format.</p>
+            <p>The BBOX strategy fetches features within a bounding box.  When the map bounds invalidate the data bounds, another request is triggered
+            <p>The WFS protocol gets features through a WFS request.</p>
+            <p>The GML format is used to serialize features.</p>
+        </div>
+    </body>
+</html>

Added: sandbox/topp/geoext/examples/v2-fixed-http-gml.html
===================================================================
--- sandbox/topp/geoext/examples/v2-fixed-http-gml.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/v2-fixed-http-gml.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>OpenLayers Vector Behavior Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+        <style type="text/css">
+            #map {
+                width: 512px;
+                height: 256px;
+                border: 1px solid gray;
+            }
+        </style>
+        <script src="../lib/OpenLayers.js"></script>
+        <script type="text/javascript">
+            var map;
+        
+            function init(){
+                map = new OpenLayers.Map('map');
+                var wms = new OpenLayers.Layer.WMS(
+                    "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
+                    {layers: 'basic'}
+                );
+
+                var layer = new OpenLayers.Layer.Vector("GML", {
+                    strategy: new OpenLayers.Strategy.Fixed(),
+                    protocol: new OpenLayers.Protocol.HTTP({
+                        url: "gml/polygon.xml"
+                    }),
+                    format: new OpenLayers.Format.GML()
+                });
+
+                map.addLayers([wms, layer]);
+                map.zoomToExtent(new OpenLayers.Bounds(
+                    -3.92, 44.34, 4.87, 49.55
+                ));
+            }
+        </script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">Vector Behavior Example</h1>
+        <p id="shortdesc">
+            Uses a new strategy, protocol, and format combination.
+        </p>
+        <div id="map"></div>
+        <div id="docs">
+            <p>The vector layer shown uses the Fixed strategy, the HTTP protocol,
+            and the GML format.</p>
+            <p>The Fixed strategy is a simple strategy that fetches features once
+            and never re-requests new data.
+            <p>The HTTP protocol makes requests using HTTP verbs.  It should be
+            constructed with a url that corresponds to a collection of features
+            (a resource on some server).</p>
+            <p>The GML format is used to serialize features.</p>
+        </div>
+    </body>
+</html>

Added: sandbox/topp/geoext/examples/v2-fixed-wfs-gml.html
===================================================================
--- sandbox/topp/geoext/examples/v2-fixed-wfs-gml.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/v2-fixed-wfs-gml.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>OpenLayers Vector Behavior Layer Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+        <style type="text/css">
+            #map {
+                width: 512px;
+                height: 256px;
+                border: 1px solid gray;
+            }
+        </style>
+        <script src="../lib/Firebug/firebug.js"></script>
+        <script src="../lib/OpenLayers.js"></script>
+        <script type="text/javascript">
+            var map;
+        
+            function init(){
+                OpenLayers.ProxyHost= "/cgi-bin/proxy.cgi?url=";
+                map = new OpenLayers.Map('map');
+                var wms = new OpenLayers.Layer.WMS(
+                    "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
+                    {layers: 'basic'}
+                );
+
+                var layer = new OpenLayers.Layer.Vector("GML", {
+                    strategy: new OpenLayers.Strategy.Fixed(),
+                    protocol: new OpenLayers.Protocol.WFS.v1_0_0({
+                        url:  "http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp",
+                        params: {typename: "OWLS", maxfeatures: 10}
+                    }),
+                    format: new OpenLayers.Format.GML()
+                });
+
+                map.addLayers([wms, layer]);
+                map.zoomToExtent(new OpenLayers.Bounds(
+                    -3.92, 44.34, 4.87, 49.55
+                ));
+            }
+        </script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">Vector Behavior Layer Example</h1>
+        <p id="shortdesc">
+            Uses a new strategy, protocol, and format combination.
+        </p>
+        <div id="map"></div>
+        <div id="docs">
+            <p>The vector layer shown uses the Fixed strategy, the HTTP protocol,
+            and the GML format.</p>
+            <p>The Fixed strategy is a simple strategy that fetches features once
+            and never re-requests new data.
+            <p>The WFS protocol gets features through a WFS request.</p>
+            <p>The GML format is used to serialize features.</p>
+        </div>
+    </body>
+</html>

Added: sandbox/topp/geoext/examples/v2-wfs-t.html
===================================================================
--- sandbox/topp/geoext/examples/v2-wfs-t.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/v2-wfs-t.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,137 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+    <style type="text/css">
+        #map {
+            width: 800px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/Firebug/firebug.js"></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        var map, layer, rlayer, saveStrategy;
+
+        function init(){
+            OpenLayers.ProxyHost= "/cgi-bin/proxy.cgi?url=";
+            map = new OpenLayers.Map('map');
+            layer = new OpenLayers.Layer.WMS( "State",
+                    "http://sigma.openplans.org/geoserver/wms", {layers: 'topp:tasmania_state_boundaries'} );
+            map.addLayer(layer);
+            
+           /*
+            rlayer = new OpenLayers.Layer.Vector("GML", {
+                    strategy: new OpenLayers.Strategy.BBOX(),
+                    protocol: new OpenLayers.Protocol.WFS.v1_0_0({
+                        url:  "http://sigma.openplans.org/geoserver/wfs",
+                        params: {typename: 'topp:tasmania_roads'}
+                    }),
+                    format: new OpenLayers.Format.GML({
+                        featureNS: "http://www.openplans.org/topp",
+                        featurePrefix: "topp",
+                        layerName: "tasmania_roads",
+                        geometryName: "the_geom"
+                    })
+            }); 
+           
+            map.addLayer(rlayer);
+            
+          
+            */
+            saveStrategy = new OpenLayers.Strategy.Save();
+           
+            clayer = new OpenLayers.Layer.Vector("GML", {
+                    strategies: [new OpenLayers.Strategy.BBOX(),
+                               saveStrategy],
+                    protocol: new OpenLayers.Protocol.WFS.v1_0_0({
+                        url:  "http://sigma.openplans.org/geoserver/wfs",
+                        params: {typeName: 'topp:tasmania_cities'}
+                    }),
+                    format: new OpenLayers.Format.GML({
+                        featureNS: "http://www.openplans.org/topp",
+                        featurePrefix: "topp",
+                        layerName: "tasmania_cities",
+                        geometryName: "the_geom"
+                    })
+            }); 
+           
+            map.addLayer(clayer);
+
+            
+
+            
+
+            var p = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'});
+            
+            /*
+            df = new OpenLayers.Control.DrawFeature(rlayer, OpenLayers.Handler.Path, {handlerOptions: {'freehand': false}, 'displayClass': 'olControlDrawFeaturePath'});
+            df.featureAdded = function(feature) {
+              feature.state = OpenLayers.State.INSERT;
+              feature.layer.drawFeature(feature);
+            }
+            */
+
+            dp = new OpenLayers.Control.DrawFeature(clayer, OpenLayers.Handler.Point, {handlerOptions: {'freehand': false}, 'displayClass': 'olControlDrawFeaturePoint'});
+
+            dp.featureAdded = function(feature) {
+              var oldgeom = feature.geometry;
+ 	          feature.layer.renderer.eraseGeometry(oldgeom);
+   	          feature.geometry = new OpenLayers.Geometry.MultiPoint(oldgeom);
+   	          feature.state = OpenLayers.State.INSERT;   	                  
+              feature.layer.drawFeature(feature);
+            }
+           
+            p.addControls([ new OpenLayers.Control.Navigation(), /* df, */ dp ]);
+
+            map.addControl(p);
+            
+            map.zoomToExtent(new OpenLayers.Bounds(145.51045,-44.0,149.0,-40.5));
+        }
+        
+        
+        function deleteCity(){
+            clayer.features[clayer.features.length-1].state = OpenLayers.State.DELETE;
+            OpenLayers.Console.log(clayer.protocol.commit());
+        }
+        
+                
+        function editCity(){
+            var feature = clayer.features[clayer.features.length-1];
+            feature.attributes.CITY_NAME = feature.attributes.CITY_NAME + "x" 
+            feature.state = OpenLayers.State.UPDATE;
+            OpenLayers.Console.log(clayer.protocol.commit());
+        }
+        
+    </script>
+  </head>
+  <body onload="init()">
+
+  <h1 id="title">WFS Example</h1>
+
+  <div id="tags">
+  </div>
+  <p id="shortdesc">
+      Shows the use of the WFS layer type.
+  </p>
+
+  <a href="#" onclick="OpenLayers.Console.log(rlayer.protocol.commit());return false">Save Roads</a><br />
+  <a href="#" onclick="saveStrategy.save();return false">Save Cities</a><br />
+    
+  <button onclick="deleteCity()">Delete a city</button><br />
+  <button onclick="editCity()">Change a city</button><br />
+  <div id="map"></div>
+
+  <div id="docs">
+      This is an example of using a WFS layer type. Note that it requires a 
+    working GeoServer install, which the OpenLayers project does not maintain;
+    however, if you're interested, you should be able to point this against
+    a default GeoServer setup without too much trouble.
+   </div>
+
+
+
+  </body>
+</html>
+
+

Added: sandbox/topp/geoext/examples/vector2-wfs-t.html
===================================================================
--- sandbox/topp/geoext/examples/vector2-wfs-t.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/vector2-wfs-t.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,127 @@
+<html>
+  <head>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="style.css" type="text/css" />
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAbs-AXzXTiUE-iwvdTFv0TRTJUVlep-V_kcHWKMoAW7P0KYzoHRSbTZ4mBasJApBqjm2Vnrs7dKOYJg'></script>
+    <script src="../lib/Firebug/firebug.js"></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <style>
+        .olControlModifyFeatureItemActive { 
+            background-image: url(../theme/default/img/move_feature_on.png);
+            background-repeat: no-repeat;
+            background-position: 0px 1px;
+        }
+        .olControlModifyFeatureItemInactive { 
+            background-image: url(../theme/default/img/move_feature_off.png);
+            background-repeat: no-repeat;
+            background-position: 0px 1px;
+        }
+    </style>
+    <script type="text/javascript">
+        var map, wfs;
+
+        function init() {
+            OpenLayers.ProxyHost= "/cgi-bin/proxy.cgi?url=";
+            map = new OpenLayers.Map('map', {
+                projection: new OpenLayers.Projection("EPSG:900913"),
+                displayProjection: new OpenLayers.Projection("EPSG:4326"),
+                units: "m",
+                maxResolution: 156543.0339,
+                maxExtent: new OpenLayers.Bounds(
+                    -11593508, 5509847, -11505759, 5557774
+                ),
+                controls: [
+                    new OpenLayers.Control.PanZoom()
+                ]
+            });
+            var gphy = new OpenLayers.Layer.Google(
+                "Google Physical",
+                {type: G_PHYSICAL_MAP, sphericalMercator: true}
+            );
+
+            wfs = new OpenLayers.Layer.Vector("Editable Features", {
+                strategies: [new OpenLayers.Strategy.BBOX(),
+                             new OpenLayers.Strategy.Save()//,
+                             //new OpenLayers.Strategy.Autosave()
+                             ],
+                protocol: new OpenLayers.Protocol.WFS.v1_0_0({
+                    url: "http://sigma.openplans.org/geoserver/wfs",
+                    params: {typeName: 'opengeo:restricted'}
+                }),
+                format: new OpenLayers.Format.GML({
+                    featureNS: "http://open-geo.com",
+                    featurePrefix: "opengeo",
+                    layerName: "restricted",
+                    geometryName: "the_geom"
+                })
+            }); 
+           
+            map.addLayers([gphy, wfs]);
+
+            var panel = new OpenLayers.Control.Panel(
+                {'displayClass': 'olControlEditingToolbar'}
+            );
+            
+            var navigate = new OpenLayers.Control.Navigation();
+            
+            var draw = new OpenLayers.Control.DrawFeature(
+                wfs, OpenLayers.Handler.Polygon,
+                {
+                    displayClass: "olControlDrawFeaturePolygon",
+                    handlerOptions: {multi: true},
+                    featureAdded: function(feature) {
+                        feature.state = OpenLayers.State.INSERT;   	                  
+                    }
+                }
+            );
+            
+            var edit = new OpenLayers.Control.ModifyFeature(wfs, {
+                displayClass: "olControlModifyFeature",
+                onModificationEnd: function(feature) {
+                    feature.state = OpenLayers.State.UPDATE
+                }
+            });
+           
+            var del = new OpenLayers.Control.DeleteFeature(wfs, {
+                displayClass: "olControlModifyFeature",
+            });
+           
+            var save = new OpenLayers.Control.Button({
+                trigger: function() {
+                    if(edit.feature) {
+                        edit.selectControl.unselectAll();
+                    }
+                    wfs.strategies[1].save();
+                },
+                displayClass: "olControlSaveFeatures"
+            });
+
+            panel.addControls([navigate, save, del, edit, draw]);
+            panel.defaultControl = navigate;
+            map.addControl(panel);
+            map.zoomToMaxExtent();
+        }
+        
+    </script>
+    </head>
+    <body onload="init()">
+    
+    <h1 id="title">WFS Transaction Example</h1>
+    
+    <div id="tags">
+    </div>
+    <p id="shortdesc">
+        Shows the use of the WFS Transactions.
+    </p>
+    
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+    </div>
+
+
+
+</body>
+</html>
+
+

Added: sandbox/topp/geoext/examples/vector2.html
===================================================================
--- sandbox/topp/geoext/examples/vector2.html	                        (rev 0)
+++ sandbox/topp/geoext/examples/vector2.html	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,62 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>OpenLayers Vector Behavior Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+        <style type="text/css">
+            #map {
+                width: 512px;
+                height: 256px;
+                border: 1px solid gray;
+            }
+        </style>
+        <script src="../lib/OpenLayers.js"></script>
+        <script type="text/javascript">
+            var map, layer;
+        
+            function init(){
+                map = new OpenLayers.Map('map');
+                var wms = new OpenLayers.Layer.WMS(
+                    "OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0",
+                    {layers: 'basic'}
+                );
+
+                var layer = new OpenLayers.Layer.Vector("GML", {
+                    strategy: new OpenLayers.Strategy.BBOX(),
+                    protocol: new OpenLayers.Protocol.HTTP({
+                        url: "gml/polygon.xml"
+                    }),
+                    format: new OpenLayers.Format.GML()
+                });
+
+                map.addLayers([wms, layer]);
+                map.zoomToExtent(new OpenLayers.Bounds(
+                    -3.92, 44.34, 4.87, 49.55
+                ));
+            }
+        </script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">Vector Behavior Example</h1>
+        <p id="shortdesc">
+            Uses a new strategy, protocol, and format combination.
+        </p>
+        <div id="map"></div>
+        <div id="docs">
+            <p>The vector layer shown uses the BBOX strategy, the HTTP protocol,
+            and the GML format.  (This is a little dumb since the GML is straight
+            from a file, but it is just to demonstrate the parts).</p>
+            <p>The BBOX strategy is a simple strategy that fetches data based on
+            some ratio applied to the map bounds.  When this bounds is invalidated
+            (the map viewport is no longer contained by the data bounds), a new
+            batch of data is requested with a new bounds.  When this data comes in,
+            the old data is destroyed and the new data is added.  This is a simple
+            strategy, but it is meant to be extended into a more complicated one.</p>
+            <p>The HTTP protocol makes requests using HTTP verbs.  It assumes a layer
+            has a url that corresponds to a collection of features (a resource on
+            some server).  It also assumes that each feature will have a url representing
+            a resource for updating or deleting itself.  The HTTP protocol also
+            knows how to deal with bbox parameters given to it from a strategy.</p>
+            <p>The GML format is used to serialize features.</p>
+        </div>
+    </body>
+</html>

Added: sandbox/topp/geoext/lib/GoogleGears/gears_init.js
===================================================================
--- sandbox/topp/geoext/lib/GoogleGears/gears_init.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/GoogleGears/gears_init.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,86 @@
+// Copyright 2007, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//  1. Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//  3. Neither the name of Google Inc. nor the names of its contributors may be
+//     used to endorse or promote products derived from this software without
+//     specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Sets up google.gears.*, which is *the only* supported way to access Gears.
+//
+// Circumvent this file at your own risk!
+//
+// In the future, Gears may automatically define google.gears.* without this
+// file. Gears may use these objects to transparently fix bugs and compatibility
+// issues. Applications that use the code below will continue to work seamlessly
+// when that happens.
+
+(function() {
+  // We are already defined. Hooray!
+  if (window.google && google.gears) {
+    return;
+  }
+
+  var factory = null;
+
+  // Firefox
+  if (typeof GearsFactory != 'undefined') {
+    factory = new GearsFactory();
+  } else {
+    // IE
+    try {
+      factory = new ActiveXObject('Gears.Factory');
+      // privateSetGlobalObject is only required and supported on WinCE.
+      if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
+        factory.privateSetGlobalObject(this);
+      }
+    } catch (e) {
+      // Safari
+      if ((typeof navigator.mimeTypes != 'undefined')
+           && navigator.mimeTypes["application/x-googlegears"]) {
+        factory = document.createElement("object");
+        factory.style.display = "none";
+        factory.width = 0;
+        factory.height = 0;
+        factory.type = "application/x-googlegears";
+        document.documentElement.appendChild(factory);
+      }
+    }
+  }
+
+  // *Do not* define any objects if Gears is not installed. This mimics the
+  // behavior of Gears defining the objects in the future.
+  if (!factory) {
+    return;
+  }
+
+  // Now set up the objects, being careful not to overwrite anything.
+  //
+  // Note: In Internet Explorer for Windows Mobile, you can't add properties to
+  // the window object. However, global objects are automatically added as
+  // properties of the window object in all browsers.
+  if (!window.google) {
+    google = {};
+  }
+
+  if (!google.gears) {
+    google.gears = {factory: factory};
+  }
+})();

Added: sandbox/topp/geoext/lib/OpenLayers/Control/DeleteFeature.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Control/DeleteFeature.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Control/DeleteFeature.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,92 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/**
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Handler/Feature.js
+ */
+
+/**
+ * Class: OpenLayers.Control.DeleteFeature
+ * Deletes vector features from a given layer on click. 
+ *
+ * Inherits from:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
+    
+    /**
+     * APIProperty: geometryTypes
+     * {Array(String)} To restrict selecting to a limited set of geometry types,
+     *     send a list of strings corresponding to the geometry class names.
+     */
+    geometryTypes: null,
+
+    /**
+     * Property: layer
+     * {<OpenLayers.Layer.Vector>}
+     */
+    layer: null,
+    
+    /**
+     * APIProperty: callbacks
+     * {Object} The functions that are sent to the handler for callback
+     */
+    callbacks: null,
+    
+    /**
+     * Property: handler
+     * {<OpenLayers.Handler.Feature>}
+     */
+    handler: null,
+
+    /**
+     * Constructor: <OpenLayers.Control.SelectFeature>
+     *
+     * Parameters:
+     * layer - {<OpenLayers.Layer.Vector>} 
+     * options - {Object} 
+     */
+    initialize: function(layer, options) {
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+        this.layer = layer;
+        this.callbacks = OpenLayers.Util.extend({
+                                                  click: this.clickFeature
+                                                }, this.callbacks);
+        var handlerOptions = { geometryTypes: this.geometryTypes};
+        this.handler = new OpenLayers.Handler.Feature(this, layer,
+                                                      this.callbacks,
+                                                      handlerOptions);
+    },
+
+    /**
+     * Method: clickFeature
+     * Called on click in a feature
+     * Only responds if this.hover is false.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     */
+    clickFeature: function(feature) {
+        feature.state = OpenLayers.State.DELETE;
+        this.layer.events.triggerEvent("afterfeaturemodified", 
+                                       {feature: feature});
+    },
+
+    /** 
+     * Method: setMap
+     * Set the map property for the control. 
+     * 
+     * Parameters:
+     * map - {<OpenLayers.Map>} 
+     */
+    setMap: function(map) {
+        this.handler.setMap(map);
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+    },
+
+    CLASS_NAME: "OpenLayers.Control.DeleteFeature"
+});

Modified: sandbox/topp/geoext/lib/OpenLayers/Control/DrawFeature.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Control/DrawFeature.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Control/DrawFeature.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -64,6 +64,8 @@
         var feature = new OpenLayers.Feature.Vector(geometry);
         this.layer.addFeatures([feature]);
         this.featureAdded(feature);
+        this.layer.events.triggerEvent("afterfeaturemodified", 
+                                       {feature: feature});
     },
 
     CLASS_NAME: "OpenLayers.Control.DrawFeature"

Added: sandbox/topp/geoext/lib/OpenLayers/Filter/Spatial.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Filter/Spatial.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Filter/Spatial.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,78 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+  * full text of the license. */
+
+/**
+ * @requires OpenLayers/Filter.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: type of spatial filter. This is one of
+     * - OpenLayers.Filter.Spatial.BBOX                 = "BBOX";
+     * - OpenLayers.Filter.Spatial.INTERSECTS             = "INTERSECTS";
+     * - OpenLayers.Filter.Spatial.DWITHIN                = "DWITHIN";
+     */
+    type: null,
+    
+    /**
+     * APIProperty: property
+     * {String}
+     * name of the context property to compare
+     */
+    property: null,
+    
+    /**
+     * APIProperty: value
+     * {<OpenLayers.Geometry>}
+     * The geometry to pass into the spatial filter
+     */
+    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]);
+    },
+
+    CLASS_NAME: "OpenLayers.Filter.Spatial"
+});
+
+
+OpenLayers.Filter.Spatial.BBOX                 = "BBOX";
+OpenLayers.Filter.Spatial.INTERSECTS             = "INTERSECTS";
+OpenLayers.Filter.Spatial.DWITHIN                = "DWITHIN";

Added: sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,585 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/Filter.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.
+     */
+    namespaces: {
+        ogc: "http://www.opengis.net/ogc",
+        xlink: "http://www.w3.org/1999/xlink",
+        xsi: "http://www.w3.org/2001/XMLSchema-instance"
+    },
+    
+    /**
+     * Property: defaultPrefix
+     */
+    defaultPrefix: "ogc",
+
+    /**
+     * 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:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Method: read
+     *
+     * Parameters:
+     * data - {DOMElement} A Filter document element.
+     *
+     * Returns:
+     * {<OpenLayers.Filter>} A filter object.
+     */
+    read: function(data) {
+        var obj = {}
+        var filter = this.readers.ogc["Filter"].apply(this, [data, obj]);
+        return obj.filter;
+    },
+    
+    /**
+     * 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": {
+            "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);
+            },
+            "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);
+            },
+            "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);
+            },
+            "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);
+            },
+            "Literal": function(node, obj) {
+                obj.value = this.getChildValue(node);
+            },
+            "PropertyName": function(node, filter) {
+                filter.property = this.getChildValue(node);
+            },
+            "LowerBoundary": function(node, filter) {
+                filter.lowerBoundary = this.readOgcExpression(node);
+            },
+            "UpperBoundary": function(node, filter) {
+                filter.upperBoundary = this.readOgcExpression(node);
+            }
+            
+        }
+    },
+    
+    /**
+     * 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) {
+            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]);
+    },
+    
+    /**
+     * 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(node, "FeatureId", filter.fids[i]);
+                    }
+                } else {
+                    this.writeNode(node, this.getFilterType(filter), filter);
+                }
+                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(
+                        node, this.getFilterType(childFilter), childFilter
+                    );
+                }
+                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(
+                        node, this.getFilterType(childFilter), childFilter
+                    );
+                }
+                return node;
+            },
+            "Not": function(filter) {
+                var node = this.createElementNSPlus("ogc:Not");
+                var childFilter = filter.filters[0];
+                this.writeNode(
+                    node, this.getFilterType(childFilter), childFilter
+                );
+                return node;
+            },
+            "PropertyIsEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);
+                return node;
+            },
+            "PropertyIsNotEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);
+                return node;
+            },
+            "PropertyIsLessThan": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);                
+                return node;
+            },
+            "PropertyIsGreaterThan": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);
+                return node;
+            },
+            "PropertyIsLessThanOrEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);
+                return node;
+            },
+            "PropertyIsGreaterThanOrEqualTo": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "Literal", filter.value);
+                return node;
+            },
+            "PropertyIsBetween": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsBetween");
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                this.writeNode(node, "LowerBoundary", filter);
+                this.writeNode(node, "UpperBoundary", filter);
+                return node;
+            },
+            "PropertyIsLike": function(filter) {
+                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
+                    attributes: {
+                        wildCard: "*", singleChar: ".", escape: "!"
+                    }
+                });
+                // no ogc:expression handling for now
+                this.writeNode(node, "PropertyName", filter);
+                // convert regex string to ogc string
+                this.writeNode(node, "Literal", filter.regex2value());
+                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(node, "Literal", filter.lowerBoundary);
+                return node;
+            },
+            "UpperBoundary": function(filter) {
+                // no ogc:expression handling for now
+                var node = this.createElementNSPlus("ogc:UpperBoundary");
+                this.writeNode(node, "Literal", filter.upperBoundary);
+                return node;
+            },
+            "BBOX": function(filter) {
+                var node = this.createElementNSPlus("ogc:BBOX");
+                this.writeNode(node, "PropertyName", filter);
+                var gml = new OpenLayers.Format.GML();
+                node.appendChild(gml.buildGeometryNode(filter.value)); 
+                return node;
+            },
+            "DWITHIN": function(filter) {
+                var node = this.createElementNSPlus("ogc:DWithin");
+                this.writeNode(node, "PropertyName", filter);
+                var gml = new OpenLayers.Format.GML();
+                node.appendChild(gml.buildGeometryNode(filter.value));
+                this.writeNode(node, "Distance", filter);
+                return node;
+            },
+           "INTERSECTS": function(filter) {
+               var node = this.createElementNSPlus("ogc:Intersects");
+               this.writeNode(node, "PropertyName", filter);
+               var gml = new OpenLayers.Format.GML();
+               node.appendChild(gml.buildGeometryNode(filter.value));
+               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;
+    },
+    
+    /**
+     * Property: filterMap
+     * {Object} Contains a member for each filter type.  Values are node names
+     *     for corresponding OGC Filter child elements.
+     */
+    filterMap: {
+        "&&": "And",
+        "||": "Or",
+        "!": "Not",
+        "==": "PropertyIsEqualTo",
+        "!=": "PropertyIsNotEqualTo",
+        "<": "PropertyIsLessThan",
+        ">": "PropertyIsGreaterThan",
+        "<=": "PropertyIsLessThanOrEqualTo",
+        ">=": "PropertyIsGreaterThanOrEqualTo",
+        "..": "PropertyIsBetween",
+        "~": "PropertyIsLike",
+        "BBOX": "BBOX",
+        "DWITHIN": "DWITHIN",
+        "INTERSECTS": "INTERSECTS"
+    },
+    
+
+    /**
+     * Methods below this point are of general use for versioned XML parsers.
+     * These are candidates for an abstract class.
+     */
+    
+    /**
+     * Method: getNamespacePrefix
+     * Get the namespace prefix for a given uri from the <namespaces> object.
+     *
+     * Returns:
+     * {String} A namespace prefix or null if none found.
+     */
+    getNamespacePrefix: function(uri) {
+        var prefix = null;
+        if(uri == null) {
+            prefix = this.namespaces[this.defaultPrefix];
+        } else {
+            var gotPrefix = false;
+            for(prefix in this.namespaces) {
+                if(this.namespaces[prefix] == uri) {
+                    gotPrefix = true;
+                    break;
+                }
+            }
+            if(!gotPrefix) {
+                prefix = null;
+            }
+        }
+        return prefix;
+    },
+
+
+    /**
+     * Method: readChildNodes
+     */
+    readChildNodes: function(node, obj) {
+        var children = node.childNodes;
+        var child, group, reader, prefix, local;
+        for(var i=0; i<children.length; ++i) {
+            child = children[i];
+            if(child.nodeType == 1) {
+                prefix = this.getNamespacePrefix(child.namespaceURI);
+                local = child.nodeName.split(":").pop();
+                group = this.readers[prefix];
+                if(group) {
+                    reader = group[local];
+                    if(reader) {
+                        reader.apply(this, [child, 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:
+     * parent - {DOMElement} Result will be appended to this node.
+     * 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.
+     * obj - {Object} Structure containing data for the writer.
+     *
+     * Returns:
+     * {DOMElement} The child node.
+     */
+    writeNode: function(parent, name, obj) {
+        var prefix, local;
+        var split = name.indexOf(":");
+        if(split > 0) {
+            prefix = name.substring(0, split);
+            local = name.substring(split + 1);
+        } else {
+            prefix = this.getNamespacePrefix(parent.namespaceURI);
+            local = name;
+        }
+        var child = this.writers[prefix][local].apply(this, [obj]);
+        parent.appendChild(child);
+        return child;
+    },
+    
+    /**
+     * 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.
+     *
+     * Returns:
+     * {Element} An element node.
+     */
+    createElementNSPlus: function(name, options) {
+        options = options || {};
+        var loc = name.indexOf(":");
+        // 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) {
+            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);
+        }
+        if(options.value) {
+            node.appendChild(this.createTextNode(options.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, loc, alias, uri;
+        for(var name in obj) {
+            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);
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Format.Filter.v1" 
+
+});

Added: sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1_0_0.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1_0_0.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/Filter/v1_0_0.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,48 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/Filter/v1.js
+ */
+
+/**
+ * Class: OpenLayers.Format.Filter.v1_0_0
+ * Write ogc:Filter version 1.0.0.
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Format.Filter.v1>
+ */
+OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
+    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.Filter.v1.prototype.initialize.apply(
+            this, [options]
+        );
+    },
+
+    CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" 
+
+});
\ No newline at end of file

Added: sandbox/topp/geoext/lib/OpenLayers/Format/Filter.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/Filter.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/Filter.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,115 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/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, {
+    
+    /**
+     * 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:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
+    initialize: function(options) {
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * APIMethod: write
+     * Write an ogc:Filter given a filter object.
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter>} An filter.
+     * options - {Object} Optional configuration object.
+     *
+     * Returns:
+     * {Elment} An ogc:Filter element node.
+     */
+    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]);
+    },
+    
+    /**
+     * APIMethod: read
+     * Read and Filter doc and return an object representing the Filter.
+     *
+     * Parameters:
+     * data - {String | DOMElement} Data to read.
+     *
+     * Returns:
+     * {<OpenLayers.Filter>} A filter object.
+     */
+    read: function(data) {
+        if(typeof data == "string") {
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
+        }
+        var root = data.nodeType == 9 ? data.documentElement : data;
+        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;
+    },
+
+    CLASS_NAME: "OpenLayers.Format.Filter" 
+});

Added: sandbox/topp/geoext/lib/OpenLayers/Format/GML/Base.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/GML/Base.js	                        (rev 0)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/GML/Base.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -0,0 +1,655 @@
+/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
+ * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/GML.js
+ */
+
+/**
+ * Class: OpenLayers.Format.GML.Base
+ * Superclass for GML parsers.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format.XML>
+ */
+OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
+    
+    /**
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.
+     */
+    namespaces: {
+        gml: "http://www.opengis.net/gml",
+        xlink: "http://www.w3.org/1999/xlink",
+        xsi: "http://www.w3.org/2001/XMLSchema-instance"
+    },
+    
+    /**
+     * Property: defaultPrefix
+     */
+    defaultPrefix: "gml",
+
+    /**
+     * Property: schemaLocation
+     * {String} Schema location for a particular minor version.
+     */
+    schemaLocation: null,
+    
+    /**
+     * APIProperty: typeName
+     * {String} The feature typeName.
+     */
+    typeName: null,
+    
+    /**
+     * APIProperty: featureNS
+     * {String} The feature namespace.  Must be set in the options at
+     *     construction.
+     */
+    featureNS: null,
+
+    /**
+     * APIProperty: geometry
+     * {String} Name of geometry element.  Defaults to "geometry".
+     */
+    geometryName: "geometry",
+
+    /**
+     * APIProperty: extractAttributes
+     * {Boolean} Extract attributes from GML.  Default is true.
+     */
+    extractAttributes: true,
+
+    /**
+     * 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: regExes
+     * Compiled regular expressions for manipulating strings.
+     */
+    regExes: {
+        trimSpace: (/^\s*|\s*$/g),
+        removeSpace: (/\s*/g),
+        splitSpace: (/\s+/),
+        trimComma: (/\s*,\s*/g)
+    },
+
+    /**
+     * Constructor: OpenLayers.Format.GML.Base
+     * Instances of this class are not created directly.  Use the
+     *     <OpenLayers.Format.GML> constructor instead.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     *
+     * Valid options properties:
+     * typeName - {String} Feature typeName (required).
+     * featureNS - {String} Feature namespace (required).
+     * geometryName - {String} Geometry element name.
+     */
+    initialize: function(options) {
+        this.namespaces = OpenLayers.Util.extend(
+            {feature: options.featureNS},
+            OpenLayers.Format.GML.Base.prototype.namespaces
+        );
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
+    },
+    
+    /**
+     * Method: read
+     *
+     * Parameters:
+     * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
+     *     element, or an element containing either of the above at any level.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Feature.Vector>)} An array of features.
+     */
+    read: function(data) {
+        var features = [];
+        this.readSelf(data, {features: features});
+        if(features.length == 0) {
+            // look for gml:featureMember elements
+            var elements = this.getElementsByTagNameNS(
+                data, this.namespaces.gml, "featureMember"
+            );
+            if(elements.length) {
+                for(var i=0; i<elements.length; ++i) {
+                    this.readSelf(elements[i], {features: features});
+                }
+            } else {
+                // look for gml:featureMembers elements
+                var elements = this.getElementsByTagNameNS(
+                    data, this.namespaces.gml, "featureMembers"
+                );
+                if(elements.length) {
+                    // there can be only one
+                    this.readSelf(elements[0], {features: features});
+                }
+            }
+        }
+        return features;
+    },
+    
+    /**
+     * 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": {
+            "featureMember": function(node, obj) {
+                this.readChildNodes(node, feature);
+            },
+            "featureMembers": function(node, obj) {
+                this.readChildNodes(node, feature);                
+            },
+            "Point": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.components.push(obj.points[0]);
+            },
+            // GML3
+            "pos": function(node, obj) {
+                var str = this.getChildValue(node).replace(
+                    this.regExes.trimSpace, ""
+                );
+                var coords = str.split(this.regExes.splitSpace);
+                var point;
+                if(this.xy) {
+                    point = new OpenLayers.Geometry.Point(
+                        coords[0], coords[1], coords[2]
+                    );
+                } else {
+                    point = new OpenLayers.Geometry.Point(
+                        coords[1], coords[0], coords[2]
+                    );
+                }
+                obj.points = [point];
+            },
+            "coordinates": function(node, obj) {
+                var str = this.concatChildValues(node).replace(
+                    this.regExes.trimSpace, ""
+                );
+                str = str.replace(this.regExes.trimComma, ",");
+                var pointList = str.split(this.regExes.splitSpace);
+                var coords;
+                var numPoints = pointList.length;
+                var points = new Array(numPoints);
+                for(var i=0; i<numPoints; ++i) {
+                    coords = pointList[i].split(",");
+                    if (this.xy) {
+                        points[i] = new OpenLayers.Geometry.Point(
+                            coords[0], coords[1], coords[2]
+                        );
+                    } else {
+                        points[i] = new OpenLayers.Geometry.Point(
+                            coords[1], coords[0], coords[2]
+                        );
+                    }
+                }
+                obj.points = points;
+            },
+            "coord": function(node, obj) {
+                var coord = {};
+                this.readChildNodes(node, coord);
+                obj.points = [new OpenLayers.Geometry.Point(
+                    coord.x, coord.y, coord.z
+                )];
+            },
+            "X": function(node, coord) {
+                coord.x = this.getChildValue(node);
+            },
+            "Y": function(node, coord) {
+                coord.y = this.getChildValue(node);
+            },
+            "Z": function(node, coord) {
+                coord.z = this.getChildValue(node);
+            },
+            "MultiPoint": function(node, container) {
+                var obj = {components: []};
+                this.readChildNodes(node, obj);
+                container.components = [
+                    new OpenLayers.Geometry.MultiPoint(obj.components)
+                ];
+            },
+            "LineString": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.components.push(
+                    new OpenLayers.Geometry.LineString(obj.points)
+                );
+            },
+            // GML3
+            "posList": function(node, obj) {
+                var str = this.concatChildValues(node).replace(
+                    this.regExes.trimSpace, ""
+                );
+                var coords = str.split(this.regExes.splitSpace);
+                var dim = parseInt(node.getAttribute("dimension"));
+                var j, x, y, z;
+                var points = [];
+                var numPoints = coords.length / dim;
+                var points = new Array(numPoints);
+                for(var i=0; i<coords.length; i += dim) {
+                    x = coords[i];
+                    y = coords[i+1];
+                    z = (dim == 2) ? null : coords[i+2];
+                    if (this.xy) {
+                        points[i/dim] = new OpenLayers.Geometry.Point(x, y, z);
+                    } else {
+                        points[i/dim] = new OpenLayers.Geometry.Point(y, x, z);
+                    }
+                }
+                obj.points = points;
+            },
+            "MultiLineString": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.components = [
+                    new OpenLayers.Geometry.MultiLineString(obj.components)
+                ];
+            },
+            "Polygon": function(node, container) {
+                var obj = {outer: null, inner: []};
+                this.readChildNodes(node, obj);
+                obj.inner.unshift(obj.outer);
+                container.components.push(
+                    new OpenLayers.Geometry.Polygon(obj.inner)
+                );
+            },
+            "outerBoundaryIs": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.outer = obj.ring;
+            },
+            "innerBoundaryIs": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.inner.push(obj.ring);
+            },
+            "LinearRing": function(node, obj) {
+                var container = {};
+                this.readChildNodes(node, container);
+                obj.ring = new OpenLayers.Geometry.LinearRing(
+                    container.points
+                );
+            },
+            "MultiPolygon": function(node, container) {
+                var obj = {};
+                this.readChildNodes(node, obj);
+                container.components = [
+                    new OpenLayers.Geometry.MultiPolygon(obj.components)
+                ];
+            }
+        },
+        "feature": {
+            "*": function(node, obj) {
+                // The node can either be named like the typeName, or it
+                // can be a child of the feature:typeName.  Children can be
+                // geometry or attributes.
+                var name;
+                var local = node.nodeName.split(":").pop();
+                if(local == this.typeName) {
+                    name = "_typeName";
+                } else {
+                    // Assume attribute elements have one child node and that the child
+                    // is a text node.  Otherwise assume it is a geometry node.
+                    if(node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+                        name = "_attribute";
+                    } else {
+                        name = "_geometry";
+                    }
+                }
+                this.readers.feature[name].apply(this, [node, obj]);
+            },
+            "_typeName": function(node, obj) {
+                var feature = new OpenLayers.Feature.Vector();
+                var fid = node.getAttribute("fid");
+                if(fid) {
+                    feature.fid = fid;
+                }
+                var container = {components: []};
+                this.readChildNodes(node, container);
+                feature.geometry = container.components[0];
+                obj.features.push(feature);
+            },
+            "_geometry": function(node, feature) {
+                this.readChildNodes(node, feature);
+            },
+            "_attribute": function(node, feature) {
+                var local = node.nodeName.split(":").pop();
+                var value = this.getChildValue(node);
+                feature.attributes[local] = value;
+            }
+        }
+    },
+    
+    /**
+     * Method: write
+     *
+     * Parameters:
+     * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
+     *     An array of features or a single feature.
+     *
+     * Returns:
+     * {DOMElement} Given an array of features, a gml:featureMembers element
+     *     will be returned.  Given a single feature, a gml:featureMember
+     *     element will be returned.
+     */
+    write: function(features) {
+        var name;
+        if(features instanceof Array) {
+            name = "featureMembers";
+        } else {
+            name = "featureMember";
+        }
+        return this.writers.gml[name].apply(this, [features]);
+    },
+    
+    /**
+     * 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": {
+            "featureMember": function(feature) {
+                var node = this.createElementNSPlus("gml:featureMember");
+                this.writeNode(node, "feature:_typeName", feature);
+                return node;
+            },
+            "featureMembers": function(features) {
+                var node = this.createElementNSPlus("gml:featureMembers");
+                for(var i=0; i<features.length; ++i) {
+                    this.writeNode(node, "feature:_typeName", features[0]);
+                }
+                return node;
+            },
+            "Point": function(geometry) {
+                var node = this.createElementNSPlus("gml:Point");
+                this.writeNode(node, "coordinates", [geometry]);
+                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;
+                    }
+                }
+                return this.createElementNSPlus("gml:coordinates", {
+                    value: (numPoints == 1) ? parts[0] : parts.join(" ")
+                });
+            },
+            "MultiPoint": function(geometry) {
+                var node = this.createElementNSPlus("gml:MultiPoint");
+                for(var i=0; i<geometry.components.length; ++i) {
+                    this.writeNode(node, "Point", geometry.components[i]);
+                }
+                return node;
+            },
+            "LineString": function(geometry) {
+                var node = this.createElementNSPlus("gml:LineString");
+                this.writeNode(node, "coordinates", geometry.components);
+                return node;
+            },
+            "MultiLineString": function(geometry) {
+                var node = this.createElementNSPlus("gml:MultiLineString");
+                for(var i=0; i<geometry.components.length; ++i) {
+                    this.writeNode(node, "LineString", geometry.components[i]);
+                }
+                return node;
+            },
+            "Polygon": function(geometry) {
+                var node = this.createElementNSPlus("gml:Polygon");
+                this.writeNode(node, "outerBoundaryIs", geometry.components[0]);
+                for(var i=1; i<geometry.components.length; ++i) {
+                    this.writeNode(
+                        node, "innerBoundaryIs", geometry.components[i]
+                    );
+                }
+                return node;
+            },
+            "outerBoundaryIs": function(ring) {
+                var node = this.createElementNSPlus("gml:outerBoundaryIs");
+                this.writeNode(node, "LinearRing", ring);
+                return node;
+            },
+            "innerBoundaryIs": function(ring) {
+                var node = this.createElementNSPlus("gml:innerBoundaryIs");
+                this.writeNode(node, "LinearRing", ring);
+                return node;
+            },
+            "LinearRing": function(ring) {
+                var node = this.createElementNSPlus("gml:LinearRing");
+                this.writeNode(node, "coordinates", ring.components);
+                return node;
+            },
+            "MultiPolygon": function(geometry) {
+                var node = this.createElementNSPlus("gml:MultiPolygon");
+                for(var i=0; i<geometry.components.length; ++i) {
+                    this.writeNode(
+                        node, "Polygon", geometry.components[i]
+                    );
+                }
+                return node;
+            }
+        },
+        "feature": {
+            "_typeName": function(feature) {
+                var node = this.createElementNSPlus("feature:" + this.typeName);
+                if(feature.geometry) {
+                    this.writeNode(node, "feature:_geometry", feature.geometry);
+                }
+                for(var name in feature.attributes) {
+                    var value = feature.attributes[name];
+                    if(value != null) {
+                        this.writeNode(
+                            node, "feature:_attribute",
+                            {name: name, value: value}
+                        )
+                    }
+                }
+            },
+            "_geometry": function(geometry) {
+                var node = this.createElementNSPlus(
+                    "feature:" + this.geometryName
+                );
+                var type = this.geometryTypes[geometry.CLASS_NAME];
+                this.writeNode(node, "gml:" + type, geometry);
+                return node;
+            },
+            "_attribute": function(obj) {
+                return this.createElementNSPlus("feature:" + obj.name, {
+                    value: obj.value
+                });
+            }
+        }
+    },
+    
+    /**
+     * Property: geometryTypes
+     * {Object} Maps OpenLayers geometry class names to GML element names.
+     */
+    geometryTypes: {
+        "OpenLayers.Geometry.Point": "Point",
+        "OpenLayers.Geometry.MultiPoint": "MultiPoint",
+        "OpenLayers.Geometry.LineString": "LineString",
+        "OpenLayers.Geometry.MultiLineString": "MultiLineString",
+        "OpenLayers.Geometry.Polygon": "Polygon",
+        "OpenLayers.Geometry.MultiPolygon": "MultiPolygon"
+    },
+
+    /**
+     * Methods below this point are of general use for versioned XML parsers.
+     * These are candidates for an abstract class.
+     */
+    
+    /**
+     * Method: getNamespacePrefix
+     * Get the namespace prefix for a given uri from the <namespaces> object.
+     *
+     * Returns:
+     * {String} A namespace prefix or null if none found.
+     */
+    getNamespacePrefix: function(uri) {
+        var prefix = null;
+        if(uri == null) {
+            prefix = this.namespaces[this.defaultPrefix];
+        } else {
+            var gotPrefix = false;
+            for(prefix in this.namespaces) {
+                if(this.namespaces[prefix] == uri) {
+                    gotPrefix = true;
+                    break;
+                }
+            }
+            if(!gotPrefix) {
+                prefix = null;
+            }
+        }
+        return prefix;
+    },
+    
+    /**
+     * Method: readSelf
+     */
+    readSelf: function(node, obj) {
+        var prefix = this.getNamespacePrefix(node.namespaceURI);
+        var local = node.nodeName.split(":").pop();
+        var group = this.readers[prefix];
+        if(group) {
+            var reader = group[local] || group["*"];
+            if(reader) {
+                reader.apply(this, [node, obj]);
+            }
+        }
+    },
+
+    /**
+     * Method: readChildNodes
+     */
+    readChildNodes: function(node, obj) {
+        var children = node.childNodes;
+        var child;
+        for(var i=0; i<children.length; ++i) {
+            child = children[i];
+            if(child.nodeType == 1) {
+                this.readSelf(child, 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:
+     * parent - {DOMElement} Result will be appended to this node.
+     * 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.
+     * obj - {Object} Structure containing data for the writer.
+     *
+     * Returns:
+     * {DOMElement} The child node.
+     */
+    writeNode: function(parent, name, obj) {
+        var prefix, local;
+        var split = name.indexOf(":");
+        if(split > 0) {
+            prefix = name.substring(0, split);
+            local = name.substring(split + 1);
+        } else {
+            prefix = this.getNamespacePrefix(parent.namespaceURI);
+            local = name;
+        }
+        var writer = this.writers[prefix][local] || this.writers[prefix]["*"];
+        var child = writer.apply(this, [obj]);
+        parent.appendChild(child);
+        return child;
+    },
+    
+    /**
+     * 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.
+     *
+     * Returns:
+     * {Element} An element node.
+     */
+    createElementNSPlus: function(name, options) {
+        options = options || {};
+        var loc = name.indexOf(":");
+        // 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) {
+            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);
+        }
+        if(options.value) {
+            node.appendChild(this.createTextNode(options.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, loc, alias, uri;
+        for(var name in obj) {
+            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);
+        }
+    },
+
+    CLASS_NAME: "OpenLayers.Format.GML.v1" 
+
+});

Modified: sandbox/topp/geoext/lib/OpenLayers/Format/GML.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/GML.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/GML.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -136,7 +136,7 @@
      * node - {DOMElement} A GML feature node. 
      */
     parseFeature: function(node) {
-        // only accept on geometry per feature - look for highest "order"
+        // only accept one geometry per feature - look for highest "order"
         var order = ["MultiPolygon", "Polygon",
                      "MultiLineString", "LineString",
                      "MultiPoint", "Point", "Envelope"];
@@ -565,22 +565,29 @@
      * Generate a GML document string given a list of features. 
      * 
      * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
+     * features - {DOMElement} A DOM node or {Array(<OpenLayers.Feature.Vector>)} List of features to
      *     serialize into a string.
      *
      * Returns:
      * {String} A string representing the GML document.
      */
     write: function(features) {
-        if(!(features instanceof Array)) {
-            features = [features];
+        var node;
+        if(typeof(features.nodeType) != 'number'){
+            if(!(features instanceof Array)) {
+                features = [features];
+            }
+            var gml = this.createElementNS("http://www.opengis.net/wfs",
+                                           "wfs:" + this.collectionName);
+            for(var i=0; i<features.length; i++) {
+                gml.appendChild(this.createFeatureXML(features[i]));
+            }
+            
+            node = gml;
+        } else {
+            node = features;
         }
-        var gml = this.createElementNS("http://www.opengis.net/wfs",
-                                       "wfs:" + this.collectionName);
-        for(var i=0; i<features.length; i++) {
-            gml.appendChild(this.createFeatureXML(features[i]));
-        }
-        return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
+        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
     },
 
     /** 
@@ -593,21 +600,23 @@
      * Returns:
      * {DOMElement} A node reprensting the feature in GML.
      */
-    createFeatureXML: function(feature) {
+    createFeatureXML: function(feature, notContained) {
         var geometry = feature.geometry;
         var geometryNode = this.buildGeometryNode(geometry);
+        // TODO: deal with this properly
+        geometryNode.setAttribute("srsName", feature.layer.projection.getCode());
+        
         var geomContainer = this.createElementNS(this.featureNS,
                                                  this.featurePrefix + ":" +
                                                  this.geometryName);
         geomContainer.appendChild(geometryNode);
-        var featureNode = this.createElementNS(this.gmlns,
-                                               "gml:" + this.featureName);
-        var featureContainer = this.createElementNS(this.featureNS,
+
+        var featureNode = this.createElementNS(this.featureNS,
                                                     this.featurePrefix + ":" +
                                                     this.layerName);
         var fid = feature.fid || feature.id;
-        featureContainer.setAttribute("fid", fid);
-        featureContainer.appendChild(geomContainer);
+        featureNode.setAttribute("fid", fid);
+        featureNode.appendChild(geomContainer);
         for(var attr in feature.attributes) {
             var attrText = this.createTextNode(feature.attributes[attr]); 
             var nodename = attr.substring(attr.lastIndexOf(":") + 1);
@@ -615,9 +624,16 @@
                                                      this.featurePrefix + ":" +
                                                      nodename);
             attrContainer.appendChild(attrText);
-            featureContainer.appendChild(attrContainer);
-        }    
-        featureNode.appendChild(featureContainer);
+            featureNode.appendChild(attrContainer);
+        }   
+         
+        if(!notContained){
+        	var featureContainer = this.createElementNS(this.gmlns,
+                                               "gml:" + this.featureName);
+            featureContainer.appendChild(featureNode);
+            return featureContainer;
+        }
+
         return featureNode;
     },
     
@@ -797,6 +813,23 @@
                 gml.appendChild(polyMember);
             }
             return gml;
+        },
+
+        /**
+         * Method: buildGeometry.rectangle
+         * Given an OpenLayers rectangle geometry, create a GML box.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.Rectangle>} A rectangle
+         *     geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML box node.
+         */
+        rectangle: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:Box");
+            gml.appendChild(this.buildCoordinatesNode(geometry));
+            return gml;
         }
     },
 
@@ -818,11 +851,18 @@
         coordinatesNode.setAttribute("decimal", ".");
         coordinatesNode.setAttribute("cs", ",");
         coordinatesNode.setAttribute("ts", " ");
-        
-        var points = (geometry.components) ? geometry.components : [geometry];
+       
         var parts = [];
-        for(var i=0; i<points.length; i++) {
-            parts.push(points[i].x + "," + points[i].y);
+        if (geometry instanceof OpenLayers.Geometry.Rectangle) {
+            parts.push(geometry.x + "," + geometry.y);
+            parts.push((geometry.x + geometry.width) + "," + 
+                (geometry.y + geometry.height));
+        } else { 
+            var points = (geometry.components) ? geometry.components : 
+                [geometry];
+            for(var i=0; i<points.length; i++) {
+                parts.push(points[i].x + "," + points[i].y);
+            }
         }
 
         var txtNode = this.createTextNode(parts.join(" "));

Modified: sandbox/topp/geoext/lib/OpenLayers/Format/SLD/v1.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/SLD/v1.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/SLD/v1.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -65,6 +65,13 @@
      *     this instance.
      */
     initialize: function(options) {
+        // extend with ogc:Filter readers and writers
+        this.readers["ogc"] = OpenLayers.Format.Filter.v1.prototype.readers["ogc"];
+        this.writers["ogc"] = OpenLayers.Format.Filter.v1.prototype.writers["ogc"];
+        // extend with custom filter methods that may get changed
+        this.readOgcExpression = OpenLayers.Format.Filter.v1.prototype.readOgcExpression;
+        this.getFilterType = OpenLayers.Format.Filter.v1.prototype.getFilterType;
+        this.filterMap = OpenLayers.Format.Filter.v1.prototype.filterMap;
         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
     },
     
@@ -222,7 +229,7 @@
                     symbolizer.graphicOpacity = graphic.opacity;
                 }
                 if(graphic.size != undefined) {
-                    symbolizer.pointRadius = graphic.size / 2;
+                    symbolizer.pointRadius = graphic.size;
                 }
                 if(graphic.href != undefined) {
                     symbolizer.externalGraphic = graphic.href;
@@ -272,149 +279,10 @@
             "Format": function(node, graphic) {
                 graphic.graphicFormat = this.getChildValue(node);
             }
-        },
-        "ogc": {
-            "Filter": function(node, rule) {
-                // 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 rule (sld:Rule).
-                var obj = {
-                    fids: [],
-                    filters: []
-                };
-                this.readChildNodes(node, obj);
-                if(obj.fids.length > 0) {
-                    rule.filter = new OpenLayers.Filter.FeatureId({
-                        fids: obj.fids
-                    });
-                } else if(obj.filters.length > 0) {
-                    rule.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);
-            },
-            "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);
-            },
-            "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);
-            },
-            "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);
-            },
-            "Literal": function(node, obj) {
-                obj.value = this.getChildValue(node);
-            },
-            "PropertyName": function(node, filter) {
-                filter.property = this.getChildValue(node);
-            },
-            "LowerBoundary": function(node, filter) {
-                filter.lowerBoundary = this.readOgcExpression(node);
-            },
-            "UpperBoundary": function(node, filter) {
-                filter.upperBoundary = this.readOgcExpression(node);
-            }
         }
     },
     
     /**
-     * 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) {
-            value = this.getChildValue(node);
-        }
-        return value;
-    },
-    
-    /**
      * Property: cssMap
      * {Object} Object mapping supported css property names to OpenLayers
      *     symbolizer property names.
@@ -839,7 +707,7 @@
                     this.writeNode(node, "Opacity", symbolizer.graphicOpacity);
                 }
                 if(symbolizer.pointRadius != undefined) {
-                    this.writeNode(node, "Size", symbolizer.pointRadius * 2);
+                    this.writeNode(node, "Size", symbolizer.pointRadius);
                 }
                 if(symbolizer.rotation != undefined) {
                     this.writeNode(node, "Rotation", symbolizer.rotation);
@@ -896,174 +764,8 @@
                     value: format
                 });
             }
-        },
-        "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(node, "FeatureId", filter.fids[i]);
-                    }
-                } else {
-                    this.writeNode(node, this.getFilterType(filter), filter);
-                }
-                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(
-                        node, this.getFilterType(childFilter), childFilter
-                    );
-                }
-                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(
-                        node, this.getFilterType(childFilter), childFilter
-                    );
-                }
-                return node;
-            },
-            "Not": function(filter) {
-                var node = this.createElementNSPlus("ogc:Not");
-                var childFilter = filter.filters[0];
-                this.writeNode(
-                    node, this.getFilterType(childFilter), childFilter
-                );
-                return node;
-            },
-            "PropertyIsEqualTo": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);
-                return node;
-            },
-            "PropertyIsNotEqualTo": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);
-                return node;
-            },
-            "PropertyIsLessThan": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);                
-                return node;
-            },
-            "PropertyIsGreaterThan": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);
-                return node;
-            },
-            "PropertyIsLessThanOrEqualTo": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);
-                return node;
-            },
-            "PropertyIsGreaterThanOrEqualTo": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "Literal", filter.value);
-                return node;
-            },
-            "PropertyIsBetween": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsBetween");
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                this.writeNode(node, "LowerBoundary", filter);
-                this.writeNode(node, "UpperBoundary", filter);
-                return node;
-            },
-            "PropertyIsLike": function(filter) {
-                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
-                    attributes: {
-                        wildCard: "*", singleChar: ".", escape: "!"
-                    }
-                });
-                // no ogc:expression handling for now
-                this.writeNode(node, "PropertyName", filter);
-                // convert regex string to ogc string
-                this.writeNode(node, "Literal", filter.regex2value());
-                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(node, "Literal", filter.lowerBoundary);
-                return node;
-            },
-            "UpperBoundary": function(filter) {
-                // no ogc:expression handling for now
-                var node = this.createElementNSPlus("ogc:UpperBoundary");
-                this.writeNode(node, "Literal", filter.upperBoundary);
-                return node;
-            }
         }
     },
-    
-    /**
-     * Method: getFilterType
-     */
-    getFilterType: function(filter) {
-        var filterType = this.filterMap[filter.type];
-        if(!filterType) {
-            throw "SLD writing not supported for rule type: " + filter.type;
-        }
-        return filterType;
-    },
-    
-    /**
-     * Property: filterMap
-     * {Object} Contains a member for each filter type.  Values are node names
-     *     for corresponding OGC Filter child elements.
-     */
-    filterMap: {
-        "&&": "And",
-        "||": "Or",
-        "!": "Not",
-        "==": "PropertyIsEqualTo",
-        "!=": "PropertyIsNotEqualTo",
-        "<": "PropertyIsLessThan",
-        ">": "PropertyIsGreaterThan",
-        "<=": "PropertyIsLessThanOrEqualTo",
-        ">=": "PropertyIsGreaterThanOrEqualTo",
-        "..": "PropertyIsBetween",
-        "~": "PropertyIsLike"
-    },
-    
 
     /**
      * Methods below this point are of general use for versioned XML parsers.

Modified: sandbox/topp/geoext/lib/OpenLayers/Format/SLD.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/SLD.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/SLD.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -9,6 +9,7 @@
  * @requires OpenLayers/Filter/FeatureId.js
  * @requires OpenLayers/Filter/Logical.js
  * @requires OpenLayers/Filter/Comparison.js
+ * @requires OpenLayers/Filter/Spatial.js
  */
 
 /**

Modified: sandbox/topp/geoext/lib/OpenLayers/Format/XML.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Format/XML.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Format/XML.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -20,6 +20,19 @@
 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
     
     /**
+     * Property: namespaces
+     * {Object} Mapping of namespace aliases to namespace URIs.
+     */
+    namespaces: {
+    },
+    
+    /**
+     * Property: defaultNamespace
+     * {String} The default namespace alias for creating element nodes.
+     */
+    defaultNamespace: null,
+
+    /**
      * 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.
@@ -384,6 +397,94 @@
         }
     },
 
+    /**
+     * Method: getNamespacePrefix
+     * Get the namespace prefix for a given uri from the <namespaces> object.
+     *
+     * Returns:
+     * {String} A namespace prefix or null if none found.
+     */
+    getNamespacePrefix: function(uri) {
+        var prefix = null;
+        if(uri == null) {
+            prefix = this.namespaces[this.defaultPrefix];
+        } else {
+            var gotPrefix = false;
+            for(prefix in this.namespaces) {
+                if(this.namespaces[prefix] == uri) {
+                    gotPrefix = true;
+                    break;
+                }
+            }
+            if(!gotPrefix) {
+                prefix = null;
+            }
+        }
+        return prefix;
+    },
+
+    /**
+     * 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.
+     *
+     * Returns:
+     * {Element} An element node.
+     */
+    createElementNSPlus: function(name, options) {
+        options = options || {};
+        var loc = name.indexOf(":");
+        // 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) {
+            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);
+        }
+        if(options.value) {
+            node.appendChild(this.createTextNode(options.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, loc, alias, uri;
+        for(var name in obj) {
+            if(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);
+            }
+        }
+    },
+
     CLASS_NAME: "OpenLayers.Format.XML" 
 
 });     

Modified: sandbox/topp/geoext/lib/OpenLayers/Handler/Path.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Handler/Path.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Handler/Path.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -138,7 +138,11 @@
      * {<OpenLayers.Geometry.LineString>}
      */
     geometryClone: function() {
-        return this.line.geometry.clone();
+        var geometry = this.line.geometry.clone();
+        if(this.multi) {
+            geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
+        }
+        return geometry;
     },
 
     /**

Modified: sandbox/topp/geoext/lib/OpenLayers/Handler/Point.js
===================================================================
--- sandbox/topp/geoext/lib/OpenLayers/Handler/Point.js	2008-06-17 18:32:48 UTC (rev 7371)
+++ sandbox/topp/geoext/lib/OpenLayers/Handler/Point.js	2008-06-17 20:36:54 UTC (rev 7372)
@@ -33,6 +33,13 @@
     layer: null,
     
     /**
+     * Property: multi
+     * {Boolean} Cast features to multi-part geometries before passing to the
+     *     lay