[Commits] r876 - in sandbox/elemoine/playground/geoext: examples lib lib/GeoExt/data lib/GeoExt/widgets lib/GeoExt/widgets/form lib/GeoExt/widgets/tree tests tests/lib/GeoExt/data tests/lib/GeoExt/widgets tests/lib/GeoExt/widgets/form

commits at geoext.org commits at geoext.org
Thu May 28 05:49:49 CEST 2009


Author: elemoine
Date: 2009-05-28 05:49:48 +0200 (Thu, 28 May 2009)
New Revision: 876

Added:
   sandbox/elemoine/playground/geoext/examples/legendpanel.html
   sandbox/elemoine/playground/geoext/examples/legendpanel.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendImage.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendPanel.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendWMS.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form.js
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/LegendPanel.html
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form.html
Modified:
   sandbox/elemoine/playground/geoext/examples/popup.js
   sandbox/elemoine/playground/geoext/examples/tree.html
   sandbox/elemoine/playground/geoext/lib/GeoExt.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/data/LayerStore.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/MapPanel.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/Popup.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form/SearchAction.js
   sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/tree/LayerNode.js
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/data/LayerStore.html
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/MapPanel.html
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/Popup.html
   sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form/SearchAction.html
   sandbox/elemoine/playground/geoext/tests/list-tests.html
Log:
svn merge -r831:HEAD http://svn.geoext.org/core/trunk/geoext .


Copied: sandbox/elemoine/playground/geoext/examples/legendpanel.html (from rev 875, core/trunk/geoext/examples/legendpanel.html)
===================================================================
--- sandbox/elemoine/playground/geoext/examples/legendpanel.html	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/examples/legendpanel.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,26 @@
+<html>
+    <head>
+        <script type="text/javascript" src="http://dev.geoext.org/trunk/ext/adapter/ext/ext-base.js"></script>
+        <script type="text/javascript" src="http://dev.geoext.org/trunk/ext/ext-all.js"></script>
+        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/ext-all.css" />
+        <link rel="stylesheet" type="text/css" href="http://extjs.com/deploy/dev/examples/shared/examples.css"></link>
+        <script src="http://openlayers.org/api/2.8-rc2/OpenLayers.js"></script>
+        <script type="text/javascript" src="../lib/GeoExt.js"></script>
+
+        <script type="text/javascript" src="legendpanel.js"></script>
+
+        <style type="text/css">
+        .mylabel {
+            font-weight: bold;
+            color: red;
+        }
+        </style>
+    </head>
+    <body>
+        <h1>GeoExt.LegendPanel</h1>
+        <p>This example shows the how to create a LegendPanel that autopopulates with legends from a map
+        that has already been created.</p>
+        <p>The js is not minified so it is readable. See <a href="legendpanel.js">legendpanel.js</a>.</p>
+        <div id="view"></div>
+    </body>
+</html>
\ No newline at end of file

Copied: sandbox/elemoine/playground/geoext/examples/legendpanel.js (from rev 875, core/trunk/geoext/examples/legendpanel.js)
===================================================================
--- sandbox/elemoine/playground/geoext/examples/legendpanel.js	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/examples/legendpanel.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,91 @@
+
+var mapPanel;
+
+Ext.onReady(function() {
+    var map = new OpenLayers.Map({allOverlays: true});
+    map.addLayers([
+        new OpenLayers.Layer.WMS(
+            "Tasmania",
+            "http://publicus.opengeo.org/geoserver/wms?",
+            {layers: 'topp:tasmania_state_boundaries', format: 'image/png', transparent: true},
+            {singleTile: true}),
+        new OpenLayers.Layer.WMS(
+            "Cities and Roads",
+            "http://publicus.opengeo.org/geoserver/wms?",
+            {layers: 'topp:tasmania_cities,topp:tasmania_roads', format: 'image/png', transparent: true},
+            {singleTile: true}),
+        new OpenLayers.Layer.Vector('Polygons', {styleMap: new OpenLayers.StyleMap({
+                "default": new OpenLayers.Style({
+                    pointRadius: 8,
+                    fillColor: "#00ffee",
+                    strokeColor: "#000000",
+                    strokeWidth: 2
+                }) }) })
+    ]);
+    map.addControl(new OpenLayers.Control.LayerSwitcher());
+
+    var addLayer = function() {
+        var wmslayer = new OpenLayers.Layer.WMS("Bodies of Water",
+            "http://publicus.opengeo.org/geoserver/wms?",
+            {layers: 'topp:tasmania_water_bodies', format: 'image/png', transparent: true},
+            {singleTile: true});
+        mapPanel.map.addLayer(wmslayer);
+    };
+
+    var removeLayer = function() {
+        mapPanel.map.removeLayer(mapPanel.map.layers[1]);
+    };
+
+    var moveLayer = function(idx) {
+        mapPanel.map.setLayerIndex(mapPanel.map.layers[0], idx);
+    };
+
+    var toggleVisibility = function() {
+        mapPanel.map.layers[1].setVisibility(!mapPanel.map.layers[1].getVisibility());
+    };
+
+    var updateHideInLegend = function() {
+        mapPanel.layers.getAt(1).set("hideInLegend", true);
+    };
+
+    var updateLegendUrl = function() {
+        mapPanel.layers.getAt(0).set("legendURL", "http://www.geoext.org/trac/geoext/chrome/site/img/GeoExt.png");
+    };
+
+    var mapPanel = new GeoExt.MapPanel({
+        region: 'center',
+        height: 400,
+        width: 600,
+        map: map,
+        center: new OpenLayers.LonLat(146.4, -41.6),
+        zoom: 7
+    });
+
+    var legendPanel = new GeoExt.LegendPanel({
+        labelCls: 'mylabel',
+        bodyStyle: 'padding:5px',
+        width: 350,
+        autoScroll: true,
+        region: 'west'
+    });
+
+    new Ext.Panel({
+        title: "GeoExt LegendPanel Demo",
+        layout: 'border',
+        renderTo: 'view',
+        height: 400,
+        width: 800,
+        tbar: new Ext.Toolbar({
+            items: [
+                {text: 'add', handler: addLayer},
+                {text: 'remove', handler: removeLayer},
+                {text: 'movetotop', handler: function() { moveLayer(10); } },
+                {text: 'moveup', handler: function() { moveLayer(1); } },
+                {text: 'togglevis', handler: toggleVisibility},
+                {text: 'hide', handler: updateHideInLegend},
+                {text: 'legendurl', handler: updateLegendUrl}
+            ]
+        }),
+        items: [legendPanel, mapPanel]
+    });
+});

Modified: sandbox/elemoine/playground/geoext/examples/popup.js
===================================================================
--- sandbox/elemoine/playground/geoext/examples/popup.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/examples/popup.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -36,7 +36,7 @@
                 }
             }
         });
-        popup.addToMapPanel(mapPanel);
+        mapPanel.add(popup);
     }
 
     // create popup on "featureselected"

Modified: sandbox/elemoine/playground/geoext/examples/tree.html
===================================================================
--- sandbox/elemoine/playground/geoext/examples/tree.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/examples/tree.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -5,8 +5,8 @@
         <script type="text/javascript" src="http://extjs.cachefly.net/builds/ext-cdn-771.js"></script>
         <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-2.2.1/resources/css/ext-all.css" />
         <link rel="stylesheet" type="text/css" href="http://extjs.com/deploy/dev/examples/shared/examples.css"></link>
+        <script src="http://openlayers.org/api/2.8-rc2/OpenLayers.js"></script>
         <script type="text/javascript" src="../lib/GeoExt.js"></script>
-        <script src="http://openlayers.org/api/2.8-rc2/OpenLayers.js"></script>
 
         <script type="text/javascript" src="tree.js"></script>
 
@@ -16,7 +16,7 @@
             <h1>GeoExt.tree Components</h1>
             <p>This example shows how to work with layer tree.  The basic
             component for building layer trees is the LayerNode, and there are
-            differnt types of containers for automatically adding a map's
+            different types of containers for automatically adding a map's
             layers to the tree. The tree configuration of this example is pure
             JSON and can be edited live by clicking on the "Show/Edit Tree Config"
             button below the layers panel.<p>
@@ -24,4 +24,4 @@
             <a href="tree.js">tree.js</a>.</p>
         </div>
     </body>
-</html>
\ No newline at end of file
+</html>

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt/data/LayerStore.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/data/LayerStore.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/data/LayerStore.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -190,15 +190,15 @@
      */
     onChangeLayer: function(evt) {
         var layer = evt.layer;
-        if(evt.property === "order") {
-            if(!this._adding && !this._removing) {
-                var layerIndex = this.map.getLayerIndex(layer);
-                var recordIndex = this.findBy(function(rec, id) {
-                    return rec.get("layer") === layer;
-                });
-                if(recordIndex > -1) {
+        var recordIndex = this.findBy(function(rec, id) {
+            return rec.get("layer") === layer;
+        });
+        if(recordIndex > -1) {
+            var record = this.getAt(recordIndex);
+            if(evt.property === "order") {
+                if(!this._adding && !this._removing) {
+                    var layerIndex = this.map.getLayerIndex(layer);
                     if(layerIndex !== recordIndex) {
-                        var record = this.getAt(recordIndex);
                         this._removing = true;
                         this.remove(record);
                         delete this._removing;
@@ -207,6 +207,8 @@
                         delete this._adding;
                     }
                 }
+            } else {
+                this.fireEvent("update", this, record, Ext.data.Record.EDIT);
             }
         }
     },

Copied: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendImage.js (from rev 875, core/trunk/geoext/lib/GeoExt/widgets/LegendImage.js)
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendImage.js	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendImage.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,80 @@
+/* Copyright (C) 2008-2009 The Open Source Geospatial Foundation
+ * Published under the BSD license.
+ * See http://geoext.org/svn/geoext/core/trunk/license.txt for the full text
+ * of the license.
+ * 
+ * pending approval */
+
+/** api: (define)
+ *  module = GeoExt
+ *  class = LegendImage
+ */
+
+Ext.namespace('GeoExt');
+
+/** api: constructor
+ *  .. class:: LegendImage(config)
+ *
+ *  Show a legend image in a BoxComponent and make sure load errors are dealt
+ *  with.
+ */
+GeoExt.LegendImage = Ext.extend(Ext.BoxComponent, {
+
+    /** api: config[url]
+     *  ``String``  The url of the image to load
+     */
+    url: null,
+
+    /** api: config[imgCls]
+     *  ``String``  Optional css class to apply to img tag
+     */
+    imgCls: null,
+
+    /** private: method[initComponent]
+     *  Initializes the legend image component. 
+     */
+    initComponent: function() {
+        GeoExt.LegendImage.superclass.initComponent.call(this);
+        this.autoEl = {tag: 'img',
+            'class': (this.imgCls ? this.imgCls : ''), src: this.url};
+    },
+
+    /** api: method[setUrl]
+     *  :param url: ``String`` The new url of the image.
+     *  
+     *  Sets the url of the image.
+     */
+    setUrl: function(url) {
+        var el = this.getEl();
+        if (el) {
+            el.dom.src = url;
+        }
+    },
+
+    /** private: method[onRender]
+     *  Private method called when the legend image component is being
+     *  rendered.
+     */
+    onRender: function(ct, position) {
+        GeoExt.LegendImage.superclass.onRender.call(this, ct, position);
+        this.getEl().on('error', this.onImageLoadError, this);
+    },
+
+    /** private: method[onDestroy]
+     *  Private method called during the destroy sequence.
+     */
+    onDestroy: function() {
+        this.getEl().un('error', this.onImageLoadError, this);
+        GeoExt.LegendImage.superclass.onDestroy.apply(this, arguments);
+    },
+    
+    /** private: method[onImageLoadError]
+     *  Private method called if the legend image fails loading.
+     */
+    onImageLoadError: function() {
+        this.getEl().dom.src = Ext.BLANK_IMAGE_URL;
+    }
+
+});
+
+Ext.reg('gx_legendimage', GeoExt.LegendImage);

Copied: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendPanel.js (from rev 875, core/trunk/geoext/lib/GeoExt/widgets/LegendPanel.js)
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendPanel.js	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendPanel.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,281 @@
+/* Copyright (C) 2008-2009 The Open Source Geospatial Foundation
+ * Published under the BSD license.
+ * See http://geoext.org/svn/geoext/core/trunk/license.txt for the full text
+ * of the license.
+ *
+ * pending approval */
+
+/** api: (define)
+ *  module = GeoExt
+ *  class = LegendPanel
+ */
+
+Ext.namespace('GeoExt');
+
+/** api: constructor
+ *  .. class:: LegendPanel(config)
+ *
+ *  A panel showing legends of all layers in a layer store.
+ *  Depending on the layer type, a legend renderer will be chosen.
+ */
+GeoExt.LegendPanel = Ext.extend(Ext.Panel, {
+
+    /** api: config[dynamic]
+     *  ``Boolean``
+     *  If false the LegendPanel will not listen to the add, remove and change 
+     *  events of the LayerStore. So it will load with the initial state of
+     *  the LayerStore and not change anymore. 
+     */
+    dynamic: true,
+    
+    /** api: config[showTitle]
+     *  ``Boolean``
+     *  Whether or not to show the title of a layer. This can be a global
+     *  setting for the whole panel, or it can be overridden on the LayerStore 
+     *  record using the hideInLegend property.
+     */
+    showTitle: true,
+
+    /** api: config[labelCls]
+     *  ``String``
+     *  Optional css class to use for the layer title labels.
+     */
+    labelCls: null,
+
+    /** api: config[bodyStyle]
+     *  ``String``
+     *  Optional style to apply to the body of the legend panels.
+     */
+    bodyStyle: '',
+
+    /** api: config[layerStore]
+     *  ``GeoExt.data.LayerStore``
+     *  The layer store containing layers to be displayed in the legend 
+     *  container. If not provided it will be taken from the MapPanel.
+     */
+    layerStore: null,
+
+    /** private: method[initComponent]
+     *  Initializes the legend panel.
+     */
+    initComponent: function() {
+        GeoExt.LegendPanel.superclass.initComponent.call(this);
+    },
+    
+    /** private: method[onRender]
+     *  Private method called when the legend panel is being rendered.
+     */
+    onRender: function() {
+        GeoExt.LegendPanel.superclass.onRender.apply(this, arguments);
+        if(!this.layerStore) {
+            this.layerStore = GeoExt.MapPanel.guess().layers;
+        }
+        this.layerStore.each(function(record) {
+                this.addLegend(record);
+            }, this);
+        if (this.dynamic) {
+            this.layerStore.on({
+                "add": this.onStoreAdd,
+                "remove": this.onStoreRemove,
+                "update": this.onStoreUpdate,
+                scope: this
+            });
+        }
+        this.doLayout();
+    },
+
+    /** private: method[recordIndexToPanelIndex]
+     *  Private method to get the panel index for a layer represented by a
+     *  record.
+     *
+     *  :param index ``Integer`` The index of the record in the store.
+     *
+     *  :return: ``Integer`` The index of the sub panel in this panel.
+     */
+    recordIndexToPanelIndex: function(index) {
+        var store = this.layerStore;
+        var count = store.getCount();
+        var panelIndex = -1;
+        for(var i=count-1; i>=0; --i) {
+            var layer = store.getAt(i).get("layer");
+            var legendGenerator = GeoExt[
+                "Legend" + layer.CLASS_NAME.split(".").pop()
+            ];
+            if(layer.displayInLayerSwitcher && legendGenerator &&
+                (store.getAt(i).get("hideInLegend") !== true)) {
+                    ++panelIndex;
+                    if(index === i) {
+                        break;
+                    }
+            }
+        }
+        return panelIndex;
+    },
+
+    /** private: method[onStoreUpdate]
+     *  Update a layer within the legend panel. Gets called when the store
+     *  fires the update event. This usually means the visibility of the layer
+     *  has changed.
+     *
+     *  :param store: ``Ext.data.Store`` The store in which the record was
+     *      changed.
+     *  :param record: ``Ext.data.Record`` The record object corresponding
+     *      to the updated layer.
+     *  :param operation: ``String`` The type of operation.
+     */
+    onStoreUpdate: function(store, record, operation) {
+        var layer = record.get('layer');
+        var legend = this.getComponent(layer.id);
+        if (legend) {
+            legend.setVisible(layer.getVisibility() && 
+                layer.displayInLayerSwitcher && !record.get('hideInLegend'));
+            if (record.get('legendURL')) {
+                var items = legend.findByType('gx_legendimage');
+                for (var i=0, len=items.length; i<len; i++) {
+                    items[i].setUrl(record.get('legendURL'));
+                }
+            }
+        }
+    },
+
+    /** private: method[onStoreAdd]
+     *  Private method called when a layer is added to the store.
+     *
+     *  :param store: ``Ext.data.Store`` The store to which the record(s) was 
+     *      added.
+     *  :param record: ``Ext.data.Record`` The record object(s) corresponding
+     *      to the added layers.
+     *  :param index: ``Integer`` The index of the inserted record.
+     */
+    onStoreAdd: function(store, records, index) {
+        var panelIndex = this.recordIndexToPanelIndex(index);
+        for (var i=0, len=records.length; i<len; i++) {
+            this.addLegend(records[i], panelIndex);
+        }
+        this.doLayout();
+    },
+
+    /** private: method[onStoreRemove]
+     *  Private method called when a layer is removed from the store.
+     *
+     *  :param store: ``Ext.data.Store`` The store from which the record(s) was
+     *      removed.
+     *  :param record: ``Ext.data.Record`` The record object(s) corresponding
+     *      to the removed layers.
+     *  :param index: ``Integer`` The index of the removed record.
+     */
+    onStoreRemove: function(store, record, index) {
+        this.removeLegend(record);
+    },
+
+    /** private: method[removeLegend]
+     *  Remove the legend of a layer.
+     *  :param record: ``Ext.data.Record`` The record object from the layer 
+     *      store to remove.
+     */
+    removeLegend: function(record) {
+        var legend = this.getComponent(record.get('layer').id);
+        if (legend) {
+            this.remove(legend, true);
+            this.doLayout();
+        }
+    },
+
+    /** private: method[createLegendSubpanel]
+     *  Create a legend sub panel for the layer.
+     *
+     *  :param record: ``Ext.data.Record`` The record object from the layer
+     *      store.
+     *
+     *  :return: ``Ext.Panel`` The created panel per layer
+     */
+    createLegendSubpanel: function(record) {
+        var layer = record.get('layer');
+        var mainPanel = this.createMainPanel(record);
+        if (mainPanel !== null) {
+            // the default legend can be overridden by specifying a
+            // legendURL property
+            var legend;
+            if (record.get('legendURL')) {
+                legend = new GeoExt.LegendImage({url: record.get('legendURL')});
+                mainPanel.add(legend);
+            } else {
+                var legendGenerator = GeoExt[
+                    "Legend" + layer.CLASS_NAME.split(".").pop()
+                ];
+                if (legendGenerator) {
+                    legend = new legendGenerator({layer: layer});
+                    mainPanel.add(legend);
+                }
+            }
+        }
+        return mainPanel;
+    },
+
+    /** private: method[addLegend]
+     *  Add a legend for the layer.
+     *
+     *  :param record: ``Ext.data.Record`` The record object from the layer 
+     *      store.
+     *  :param index: ``Integer`` The position at which to add the legend.
+     */
+    addLegend: function(record, index) {
+        index = index || 0;
+        var layer = record.get('layer');
+        var legendSubpanel = this.createLegendSubpanel(record);
+        if (legendSubpanel !== null) {
+           legendSubpanel.setVisible(layer.getVisibility());
+           this.insert(index, legendSubpanel);
+        }
+    },
+
+    /** private: method[createMainPanel]
+     *  Creates the main panel with a title for the layer.
+     *
+     *  :param record: ``Ext.data.Record`` The record object from the layer
+     *      store.
+     *
+     *  :return: ``Ext.Panel`` The created main panel with a label.
+     */
+    createMainPanel: function(record) {
+        var layer = record.get('layer');
+        var panel = null;
+        var legendGenerator = GeoExt[
+            "Legend" + layer.CLASS_NAME.split(".").pop()
+        ];
+        if (layer.displayInLayerSwitcher && !record.get('hideInLegend') &&
+            legendGenerator) {
+            var panelConfig = {
+                id: layer.id,
+                border: false,
+                bodyBorder: false,
+                bodyStyle: this.bodyStyle,
+                items: [
+                    new Ext.form.Label({
+                        text: (this.showTitle && !record.get('hideTitle')) ? 
+                            layer.name : '',
+                        cls: 'x-form-item x-form-item-label' +
+                            (this.labelCls ? ' ' + this.labelCls : '')
+                    })
+                ]
+            };
+            panel = new Ext.Panel(panelConfig);
+        }
+        return panel;
+    },
+
+    /** private: method[onDestroy]
+     *  Private method called during the destroy sequence.
+     */
+    onDestroy: function() {
+        if(this.layerStore) {
+            this.layerStore.un("add", this.onStoreAdd, this);
+            this.layerStore.un("remove", this.onStoreRemove, this);
+            this.layerStore.un("update", this.onStoreUpdate, this);
+        }
+        GeoExt.LegendPanel.superclass.onDestroy.apply(this, arguments);
+    }
+    
+});
+
+Ext.reg('gx_legendpanel', GeoExt.LegendPanel);
\ No newline at end of file

Copied: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendWMS.js (from rev 875, core/trunk/geoext/lib/GeoExt/widgets/LegendWMS.js)
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendWMS.js	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/LegendWMS.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,87 @@
+/* Copyright (C) 2008-2009 The Open Source Geospatial Foundation
+ * Published under the BSD license.
+ * See http://geoext.org/svn/geoext/core/trunk/license.txt for the full text
+ * of the license.
+ *
+ * pending approval */
+
+/**
+ * @include GeoExt/widgets/LegendImage.js
+ */
+
+/** api: (define)
+ *  module = GeoExt
+ *  class = LegendWMS
+ */
+Ext.namespace('GeoExt');
+
+/** api: constructor
+ *  .. class:: LegendWMS(config)
+ *
+ *  Show a legend image for a WMS layer.
+ */
+GeoExt.LegendWMS = Ext.extend(Ext.Panel, {
+
+    /** api: config[imageFormat]
+     *  ``String``  
+     *  The image format to request the legend image in.
+     *  Defaults to image/png.
+     */
+    imageFormat: "image/gif",
+
+    /** api: config[layer]
+     *  ``OpenLayers.Layer.WMS``
+     *  The WMS layer to request the legend for.
+     */
+    layer: null,
+
+    /** api: config[bodyBorder]
+     *  ``Boolean``
+     *  Show a border around the legend image or not. Default is false.
+     */
+    bodyBorder: false,
+
+    /** private: method[initComponent]
+     *  Initializes the WMS legend. For group layers it will create multiple
+     *  image box components.
+     */
+    initComponent: function() {
+        GeoExt.LegendWMS.superclass.initComponent.call(this);
+        this.createLegend();
+    },
+
+    /** private: method[getLegendUrl]
+     *  :param layer: ``OpenLayers.Layer.WMS`` The OpenLayers WMS layer object
+     *  :param layerName: ``String`` The name of the layer 
+     *      (used in the LAYERS parameter)
+     *  :return: ``String`` The url of the SLD WMS GetLegendGraphic request.
+     *
+     *  Get the url for the SLD WMS GetLegendGraphic request.
+     */
+    getLegendUrl: function(layerName) {
+        return this.layer.getFullRequestString({
+            REQUEST: "GetLegendGraphic",
+            WIDTH: null,
+            HEIGHT: null,
+            EXCEPTIONS: "application/vnd.ogc.se_xml",
+            LAYER: layerName,
+            LAYERS: null,
+            SRS: null,
+            FORMAT: this.imageFormat
+        });
+    },
+
+    /** private: method[createLegend]
+     *  Add one BoxComponent per sublayer to this panel.
+     */
+    createLegend: function() {
+        var layers = this.layer.params.LAYERS.split(",");
+        for (var i = 0, len = layers.length; i < len; i++){
+            var layerName = layers[i];
+            var legend = new GeoExt.LegendImage({url:
+                this.getLegendUrl(layerName)});
+            this.add(legend);
+        }
+    }
+
+});
\ No newline at end of file

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/MapPanel.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/MapPanel.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/MapPanel.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -170,14 +170,46 @@
         this.updateMapSize();
     },
     
-    /** private: method[onDestroy]
+    /** private: method[onBeforeAdd]
+     *  Private method called before a component is added to the panel.
+     */
+    onBeforeAdd: function(item) {
+        if(typeof item.addToMapPanel === "function") {
+            item.addToMapPanel(this);
+        }
+        GeoExt.MapPanel.superclass.onBeforeAdd.apply(this, arguments);
+    },
+    
+    /** private: method[remove]
+     *  Private method called when a component is removed from the panel.
+     */
+    remove: function(item, autoDestroy) {
+        if(typeof item.removeFromMapPanel === "function") {
+            item.removeFromMapPanel(this);
+        }
+        GeoExt.MapPanel.superclass.remove.apply(this, arguments);
+    },
+
+    /** private: method[beforeDestroy]
      *  Private method called during the destroy sequence.
      */
-    onDestroy: function() {
+    beforeDestroy: function() {
         if(this.ownerCt) {
             this.ownerCt.un("move", this.updateMapSize, this);
         }
-        GeoExt.MapPanel.superclass.onDestroy.apply(this, arguments);
+        /**
+         * If this container was passed a map instance, it is the
+         * responsibility of the creator to destroy it.
+         */
+        if(!this.initialConfig.map ||
+           !(this.initialConfig.map instanceof OpenLayers.Map)) {
+            // we created the map, we destroy it
+            if(this.map && this.map.destroy) {
+                this.map.destroy();
+            }
+        }
+        delete this.map;
+        GeoExt.MapPanel.superclass.beforeDestroy.apply(this, arguments);
     }
     
 });

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/Popup.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/Popup.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/Popup.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -146,34 +146,36 @@
         GeoExt.Popup.superclass.initTools.call(this);
     },
 
-    /** api: method[addToMapPanel]
+    /** private: method[addToMapPanel]
      *  :param mapPanel: :class:`MapPanel` The panel to which this popup should
      *      be added.
      *  
      *  Adds this popup to a :class:`MapPanel`.  Assumes that the
      *  MapPanel's map is already initialized and that the
-     *  Popup's feature is on the map.
+     *  Popup's feature is on the map.  This method is called by the MapPanel's
+     *  add method.
      */
     addToMapPanel: function(mapPanel) {
         this.mapPanel = mapPanel;
         this.map = this.mapPanel.map;
-
-        mapPanel.add(this);
-        mapPanel.doLayout();
-
-        this.position();
-
-        /* Anchoring */
-        if(this.anchored) {
-            this.anchorPopup();
-        }
-
-        this.show();
-
-        /* Panning */
-        if(this.panIn) {
-            this.panIntoView();
-        }
+        
+        mapPanel.on({
+            "add": {
+                fn: function() {
+                    mapPanel.doLayout();
+                    this.position();
+                    if(this.anchored) {
+                        this.anchorPopup();
+                    }
+                    this.show();
+                    if(this.panIn) {
+                        this.panIntoView();
+                    }
+                },
+                single: true,
+                scope: this
+            }
+        });
     },
 
     /** api: method[setSize]
@@ -249,7 +251,6 @@
      *  MapPanel and adds it to the page body.
      */
     unanchorPopup: function() {
-        this.unbindFromMapPanel();
 
         //make the window draggable
         this.draggable = true;
@@ -284,19 +285,25 @@
         }
     },
 
-    /** private: method[unbindFromMapPanel]
+    /** private: method[removeFromMapPanel]
      *  Utility method for unbinding events that call for popup repositioning.
+     *  Called from the panel during panel.remove(popup).
      */
-    unbindFromMapPanel: function() {
-        //stop position with feature
-        this.map.events.un({
-            "move" : this.position,
-            scope : this
-        });
+    removeFromMapPanel: function() {
+        if(this.map && this.map.events) {
+            //stop position with feature
+            this.map.events.un({
+                "move" : this.position,
+                scope : this
+            });
+        }
 
-        this.un("resize", this.position);
-        this.un("collapse", this.position);
-        this.un("expand", this.position);
+        this.un("resize", this.position, this);
+        this.un("collapse", this.position, this);
+        this.un("expand", this.position, this);
+
+        delete this.mapPanel;
+        delete this.map;
     },
 
     /** private: method[panIntoView]
@@ -352,7 +359,7 @@
      *  Cleanup events before destroying the popup.
      */
     beforeDestroy: function() {
-        this.unbindFromMapPanel();
+        this.removeFromMapPanel();
         GeoExt.Popup.superclass.beforeDestroy.call(this);
     }
 });

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form/SearchAction.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form/SearchAction.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form/SearchAction.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -11,6 +11,10 @@
  *  base_link = `Ext.form.Action <http://extjs.com/deploy/dev/docs/?class=Ext.form.Action>`_
  */
 
+/**
+ * @include GeoExt/widgets/form.js
+ */
+
 Ext.namespace("GeoExt.form");
  
 /** api: example
@@ -47,68 +51,6 @@
  *      });
  */
 
-/**
- * Function: GeoExt.form.filterFromForm
- * Create an {OpenLayers.Filter} object from a {Ext.form.BasicForm}
- *     instance or a {Ext.form.FormPanel}.
- *
- * Parameters:
- * form - {Ext.form.BasicForm|Ext.form.FormPanel}
- * logicalOp - {String} Either {OpenLayers.Filter.Logical.AND}
- *     or {OpenLayers.Filter.Logical.OR}, set to
- *     {OpenLayers.Filter.Logical.AND} if null or
- *     undefined.
- *
- * Returns:
- * {OpenLayers.Filter}
- */
-GeoExt.form.filterFromForm = function(form, logicalOp) {
-    if(form instanceof Ext.form.FormPanel) {
-        form = form.getForm();
-    }
-    var filters = [], values = form.getValues(false);
-    for(var prop in values) {
-        var s = prop.split("__");
-
-        var value = values[prop], type;
-
-        if(s.length > 1 && 
-           (type = GeoExt.form.filterFromForm.FILTER_MAP[s[1]]) !== undefined) {
-            prop = s[0];
-        } else {
-            type = OpenLayers.Filter.Comparison.EQUAL_TO;
-        }
-
-        filters.push(
-            new OpenLayers.Filter.Comparison({
-                type: type,
-                value: value,
-                property: prop
-            })
-        );
-    }
-
-    return new OpenLayers.Filter.Logical({
-        type: logicalOp || OpenLayers.Filter.Logical.AND,
-        filters: filters
-    });
-};
-
-/**
- * Constant: GeoExt.form.filterFromForm.FILTER_MAP
- * An object mapping operator strings as found in field names to
- *     {OpenLayers.Filter.Comparison} types.
- */
-GeoExt.form.filterFromForm.FILTER_MAP = {
-    "eq": OpenLayers.Filter.Comparison.EQUAL_TO,
-    "ne": OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
-    "lt": OpenLayers.Filter.Comparison.LESS_THAN,
-    "le": OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
-    "gt": OpenLayers.Filter.Comparison.GREATER_THAN,
-    "ge": OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
-    "like": OpenLayers.Filter.Comparison.LIKE
-};
-
 /** api: constructor
  *  .. class:: SearchAction(form, options)
  *
@@ -165,7 +107,7 @@
      */
     run: function() {
         var o = this.options;
-        var f = GeoExt.form.filterFromForm(this.form);
+        var f = GeoExt.form.toFilter(this.form);
         if(o.clientValidation === false || this.form.isValid()){
             this.response = o.protocol.read(
                 Ext.applyIf({

Copied: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form.js (from rev 875, core/trunk/geoext/lib/GeoExt/widgets/form.js)
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form.js	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/form.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,70 @@
+/* Copyright (C) 2008-2009 The Open Source Geospatial Foundation
+ * Published under the BSD license.
+ * See http://geoext.org/svn/geoext/core/trunk/license.txt for the full text
+ * of the license.
+ * 
+ * pending approval */
+
+Ext.namespace("GeoExt.form");
+
+/**
+ * Function: GeoExt.form.toFilter
+ * Create an {OpenLayers.Filter} object from a {Ext.form.BasicForm}
+ *     or a {Ext.form.FormPanel} instance.
+ *
+ * Parameters:
+ * form - {Ext.form.BasicForm|Ext.form.FormPanel}
+ * logicalOp - {String} Either {OpenLayers.Filter.Logical.AND}
+ *     or {OpenLayers.Filter.Logical.OR}, set to
+ *     {OpenLayers.Filter.Logical.AND} if null or
+ *     undefined.
+ *
+ * Returns:
+ * {OpenLayers.Filter}
+ */
+GeoExt.form.toFilter = function(form, logicalOp) {
+    if(form instanceof Ext.form.FormPanel) {
+        form = form.getForm();
+    }
+    var filters = [], values = form.getValues(false);
+    for(var prop in values) {
+        var s = prop.split("__");
+
+        var value = values[prop], type;
+
+        if(s.length > 1 && 
+           (type = GeoExt.form.toFilter.FILTER_MAP[s[1]]) !== undefined) {
+            prop = s[0];
+        } else {
+            type = OpenLayers.Filter.Comparison.EQUAL_TO;
+        }
+
+        filters.push(
+            new OpenLayers.Filter.Comparison({
+                type: type,
+                value: value,
+                property: prop
+            })
+        );
+    }
+
+    return new OpenLayers.Filter.Logical({
+        type: logicalOp || OpenLayers.Filter.Logical.AND,
+        filters: filters
+    });
+};
+
+/**
+ * Constant: GeoExt.form.omForm.FILTER_MAP
+ * An object mapping operator strings as found in field names to
+ *     {OpenLayers.Filter.Comparison} types.
+ */
+GeoExt.form.toFilter.FILTER_MAP = {
+    "eq": OpenLayers.Filter.Comparison.EQUAL_TO,
+    "ne": OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+    "lt": OpenLayers.Filter.Comparison.LESS_THAN,
+    "le": OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
+    "gt": OpenLayers.Filter.Comparison.GREATER_THAN,
+    "ge": OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+    "like": OpenLayers.Filter.Comparison.LIKE
+};

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/tree/LayerNode.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/tree/LayerNode.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt/widgets/tree/LayerNode.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -36,7 +36,7 @@
     render: function(bulkRender) {
         GeoExt.tree.LayerNodeUI.superclass.render.call(this, bulkRender);
         var a = this.node.attributes;
-        if (a.radioGroup && this.radio !== undefined) {
+        if (a.radioGroup && this.radio === null) {
             this.radio = Ext.DomHelper.insertAfter(this.checkbox,
                 ['<input type="radio" class="x-tree-node-radio" name="',
                 a.radioGroup, '_radio"></input>'].join(""));
@@ -83,64 +83,69 @@
     }
 });
 
+/** api: (define)
+ *  module = GeoExt.tree
+ *  class = LayerNode
+ *  base_link = `Ext.tree.TreeNode <http://extjs.com/deploy/dev/docs/?class=Ext.tree.TreeNode>`_
+ */
 
-/**
- * Class: GeoExt.tree.LayerNode
+/** api: constructor
+ *  .. class:: LayerNode(config)
  * 
- * A subclass of {<GeoExt.tree.TristateCheckboxNode>} that is connected to an
- * {OpenLayers.Layer} by setting the node's layer property. Checking or
- * unchecking the checkbox of this node will directly affect the layer and
- * vice versa. The default iconCls for this node's icon is "layer-icon",
- * unless it has children.
+ *      A subclass of ``Ext.tree.TreeNode`` that is connected to an
+ *      ``OpenLayers.Layer`` by setting the node's layer property. Checking or
+ *      unchecking the checkbox of this node will directly affect the layer and
+ *      vice versa. The default iconCls for this node's icon is "layer-icon",
+ *      unless it has children.
  * 
- * Setting the node's layer property to a layer name instead of an object
- * will also work. As soon as a layer is found, it will be stored as layer
- * property in the attributes hash.
+ *      Setting the node's layer property to a layer name instead of an object
+ *      will also work. As soon as a layer is found, it will be stored as layer
+ *      property in the attributes hash.
  * 
- * The node's text property defaults to the layer name.
+ *      The node's text property defaults to the layer name.
  * 
- * If the node has a radioGroup attribute configured, the node will be
- * rendered with a radio button. This works like the checkbox with the
- * checked attribute, but radioGroup is a string that identifies the options
- * group. Clicking the radio button will fire a radioChange event.
+ *      If the node has a radioGroup attribute configured, the node will be
+ *      rendered with a radio button. This works like the checkbox with the
+ *      checked attribute, but radioGroup is a string that identifies the options
+ *      group. Clicking the radio button will fire a radioChange event.
  * 
- * To use this node type in a JSON config, set nodeType to "gx_layer".
- * 
- * Inherits from:
- * - Ext.tree.TreeNode
+ *      To use this node type in a JSON config, set nodeType to "gx_layer".
  */
 GeoExt.tree.LayerNode = Ext.extend(Ext.tree.TreeNode, {
     
-    /**
-     * APIProperty: layer
-     * {OpenLayers.Layer|String} The layer that this layer node will
-     *     be bound to, or the name of the layer (has to match the layer's
-     *     name property). If a layer name is provided, <layerStore> also has
-     *     to be provided.
+    /** api: config[layer]
+     *  ``OpenLayers.Layer or String``
+     *  The layer that this layer node will
+     *  be bound to, or the name of the layer (has to match the layer's
+     *  name property). If a layer name is provided, ``layerStore`` also has
+     *  to be provided.
      */
+
+    /** api: property[layer]
+     *  ``OpenLayers.Layer``
+     *  The layer this node is bound to.
+     */
     layer: null,
     
-    /**
-     * APIProperty: layerStore
-     * {<GeoExt.data.LayerStore|"auto"} The layer store containing the layer
-     *     that this node represents. If set to "auto", the node will query
-     *     the ComponentManager for a <GeoExt.MapPanel>, take the first one it
-     *     finds and take its layer store. This property is only required
-     *     if <layer> is provided as a string.
+    /** api: config[layerStore]
+     *  :class:`GeoExt.data.LayerStore` ``or "auto"``
+     *  The layer store containing the layer that this node represents.  If set
+     *  to "auto", the node will query the ComponentManager for a
+     *  :class:`GeoExt.MapPanel`, take the first one it finds and take its layer
+     *  store. This property is only required if ``layer`` is provided as a
+     *  string.
      */
     layerStore: null,
     
-    /**
-     * APIProperty: childNodeType
-     * {Ext.tree.Node|String} node class or nodeType of childnodes for this
-     *     node. A node type provided here needs to have an add method, with
-     *     a scope argument. This method will be run by this node in the
-     *     context of this node, to create child nodes.
+    /** api: config[childNodeType]
+     *  ``Ext.tree.Node or String``
+     *  Node class or nodeType of childnodes for this node. A node type provided
+     *  here needs to have an add method, with a scope argument. This method
+     *  will be run by this node in the context of this node, to create child nodes.
      */
     childNodeType: null,
     
-    /**
-     * Property: visibilityChanging
+    /** private: property[visibilityChanging]
      * {Boolean} private property indicating layer visibility being changed
      *     by this node in order to prevent visibilitychanged events bouncing
      *     back and forth

Modified: sandbox/elemoine/playground/geoext/lib/GeoExt.js
===================================================================
--- sandbox/elemoine/playground/geoext/lib/GeoExt.js	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/lib/GeoExt.js	2009-05-28 03:49:48 UTC (rev 876)
@@ -71,13 +71,17 @@
             "GeoExt/data/ProtocolProxy.js",
             "GeoExt/widgets/MapPanel.js",
             "GeoExt/widgets/Popup.js",
+            "GeoExt/widgets/form.js",
             "GeoExt/widgets/form/SearchAction.js",
             "GeoExt/widgets/form/BasicForm.js",
             "GeoExt/widgets/form/FormPanel.js",
             "GeoExt/widgets/tree/LayerNode.js",
             "GeoExt/widgets/tree/LayerContainer.js",
             "GeoExt/widgets/tree/BaseLayerContainer.js",
-            "GeoExt/widgets/tree/OverlayLayerContainer.js"
+            "GeoExt/widgets/tree/OverlayLayerContainer.js",
+            "GeoExt/widgets/LegendImage.js",
+            "GeoExt/widgets/LegendWMS.js",
+            "GeoExt/widgets/LegendPanel.js"
         );
 
         var agent = navigator.userAgent;

Modified: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/data/LayerStore.html
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/data/LayerStore.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/data/LayerStore.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -285,7 +285,92 @@
 
         }
         
+        function test_events(t) {
+            t.plan(21);
+            
+            var map = new OpenLayers.Map({div: "mappanel", allOverlays: true});
+            var a = new OpenLayers.Layer("a");
+            var b = new OpenLayers.Layer("b");
+            var c = new OpenLayers.Layer("c");
+            var d = new OpenLayers.Layer("d");
 
+            var store = new GeoExt.data.LayerStore({
+                map: map
+            });
+            
+            store.add(store.reader.readRecords([a, b, c]).records);
+            
+            var events = [];
+            function logEvent(type, args) {
+                events.push({type: type, args: args});
+            }
+            store.on({
+                add: function() {logEvent("add", arguments)},
+                remove: function() {logEvent("remove", arguments)},
+                update: function() {logEvent("update", arguments)}
+            });
+            
+            var batch, event, record;
+            
+            // confirm that we get an "add" on map.addLayer
+            batch = "map.addLayer";
+            map.addLayer(d);
+            t.eq(events.length, 1, "[" + batch + "] one event");
+            event = events[0];
+            t.eq(event.type, "add", "[" + batch + "] 'add' event");
+            t.eq(event.args[1].length, 1, "[" + batch + "] correct number of records");
+            record = event.args[1][0];
+            t.ok(record.get("layer") === d, "[" + batch + "] correct layer on record");
+            
+            // confirm that we get a "remove" on map.removeLayer
+            events = [];
+            batch = "map.removeLayer";
+            map.removeLayer(d);
+            t.eq(events.length, 1, "[" + batch + "] one event");
+            event = events[0];
+            t.eq(event.type, "remove", "[" + batch + "] 'remove' event");
+            t.ok(event.args[1].get("layer") === d, "[" + batch + "] correct layer on record");
+
+            // confirm that we get an "add" and "remove" on map.setLayerIndex
+            events = [];
+            batch = "map.setLayerIndex";
+            map.setLayerIndex(b, 0);
+            t.eq(events.length, 2, "[" + batch + "] two events");
+            event = events[0];
+            t.eq(event.type, "remove", "[" + batch + "] 'remove' event first");
+            t.ok(event.args[1].get("layer") === b, "[" + batch + "] correct layer on record");
+            event = events[1];
+            t.eq(event.type, "add", "[" + batch + "] 'add' event second");
+            t.eq(event.args[1].length, 1, "[" + batch + "] correct number of records");
+            record = event.args[1][0];
+            t.ok(record.get("layer") === b, "[" + batch + "] correct layer on record");
+
+            // confirm that we get an "update" on layer.setName
+            events = [];
+            batch = "layer.setName";
+            c.setName("c layer");
+            t.eq(events.length, 1, "[" + batch + "] one event");
+            event = events[0];
+            t.eq(event.type, "update", "[" + batch + "] 'update' event");
+            t.ok(event.args[1].get("layer") === c, "[" + batch + "] correct layer on record");
+            t.eq(event.args[2], Ext.data.Record.EDIT, "[" + batch + "] correct operation");
+            
+            // confirm that we get an "update" on layer.setVisibility
+            events = [];
+            batch = "layer.setVisibility";
+            a.setVisibility(false);
+            t.eq(events.length, 1, "[" + batch + "] one event");
+            event = events[0];
+            t.eq(event.type, "update", "[" + batch + "] 'update' event");
+            t.ok(event.args[1].get("layer") === a, "[" + batch + "] correct layer on record");
+            t.eq(event.args[2], Ext.data.Record.EDIT, "[" + batch + "] correct operation");
+
+
+            map.destroy();
+            
+        }
+        
+
     </script>
   </head>  
   <body>

Copied: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/LegendPanel.html (from rev 875, core/trunk/geoext/tests/lib/GeoExt/widgets/LegendPanel.html)
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/LegendPanel.html	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/LegendPanel.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html debug="true">
+  <head>
+    <script type="text/javascript" src="../../../../../openlayers/lib/OpenLayers.js"></script>
+    <script type="text/javascript" src="../../../../../ext/adapter/ext/ext-base.js"></script>
+    <script type="text/javascript" src="../../../../../ext/ext-all.js"></script>
+    <script type="text/javascript" src="../../../../lib/GeoExt.js"></script>
+
+    <script type="text/javascript">
+
+        function createMap() {
+            var map = new OpenLayers.Map({allOverlays: true});
+            var layer = new OpenLayers.Layer.WMS("test", '/ows', {layers: 'a'});
+            map.addLayer(layer);
+            return map;
+        }
+
+        function loadMapPanel() {
+            var map = createMap();
+
+            mapPanel = new GeoExt.MapPanel({
+                // panel options
+                id: "map-panel",
+                title: "GeoExt MapPanel",
+                renderTo: "mappanel",
+                height: 400,
+                width: 600,
+                // map panel-specific options
+                map: map,
+                center: new OpenLayers.LonLat(5, 45),
+                zoom: 4
+            });
+
+            return mapPanel;
+        }
+
+        function test_legendurl(t) {
+            t.plan(1);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                renderTo: 'legendpanel'});
+            lp.render();
+
+            var newUrl = "http://www.geoext.org//trac/geoext/chrome/site/img/GeoExt.png";
+            mapPanel.layers.getAt(0).set("legendURL", newUrl);
+
+            var item = lp.getComponent(mapPanel.map.layers[0].id);
+            var url = item.items.items[1].items.items[0].getEl().dom.src;
+            t.eq(url, newUrl, "Update the image with the provided legendURL");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+        function test_togglevisibility(t) {
+            t.plan(2);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                renderTo: 'legendpanel'});
+            lp.render();
+
+            mapPanel.map.layers[0].setVisibility(false);
+            var id = mapPanel.layers.getAt(0).get('layer').id;
+            t.eq(lp.getComponent(id).hidden, true, "Layer has been hidden in legend");
+
+            mapPanel.map.layers[0].setVisibility(true);
+            t.eq(lp.getComponent(id).hidden, false, "Layer has been made visible again in legend");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+        function test_hide(t) {
+            t.plan(1);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                renderTo: 'legendpanel'});
+            lp.render();
+
+            mapPanel.layers.getAt(0).set("hideInLegend", true);
+            var id = mapPanel.layers.getAt(0).get('layer').id;
+            t.eq(lp.getComponent(id).hidden, true, "Layer has been hidden in legend");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+        function test_dynamic(t) {
+            t.plan(1);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                dynamic: false,
+                renderTo: 'legendpanel'});
+            lp.render();
+
+            var layer;
+            layer = new OpenLayers.Layer.WMS("test2", '/ows', {layers: 'b', format: 'image/png', transparent: 'TRUE'});
+            mapPanel.map.addLayer(layer);
+
+            t.eq(lp.items.length, 1, "If dynamic is false, do not add or remove layers from legend");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+        function test_wms(t) {
+            t.plan(1);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                renderTo: 'legendpanel'});
+            lp.render();
+
+            var item = lp.getComponent(mapPanel.map.layers[0].id);
+            var url = item.items.items[1].items.items[0].url;
+            var expectedUrl = "/ows?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_xml&FORMAT=image%2Fgif&LAYER=a";
+            t.eq(url, expectedUrl, "GetLegendGraphic url is generated correctly");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+        function test_addremove(t) {
+            t.plan(4);
+            var mapPanel = loadMapPanel();
+            var lp  = new GeoExt.LegendPanel({
+                renderTo: 'legendpanel'});
+            lp.render();
+            t.eq(lp.items.length, 1, "Same number of layers in legend panel and in map");
+
+            var item = lp.getComponent(mapPanel.map.layers[0].id);
+
+            var layer;
+            layer = new OpenLayers.Layer.WMS("test2", '/ows', {layers: 'b', format: 'image/png', transparent: 'TRUE'});
+            mapPanel.map.addLayer(layer);
+
+            t.eq(lp.items.length, 2, "New WMS layer has been added");
+
+            layer = new OpenLayers.Layer.WMS("test3", '/ows', {layers: 'c'}, {visibility: false});
+            mapPanel.map.addLayer(layer);
+
+            t.eq(lp.items.length, 3, "A non visible WMS layer will be added but will be invisible");
+
+            mapPanel.map.removeLayer(mapPanel.map.layers[0]);
+            t.eq(lp.items.length, 2, "Removing the WMS layer really removes the legend from the panel");
+
+            lp.destroy();
+            mapPanel.destroy();
+        }
+
+    </script>
+  <body>
+    <div id="legendpanel"></div>
+    <div id="mappanel"></div>
+  </body>
+</html>

Modified: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/MapPanel.html
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/MapPanel.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/MapPanel.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -15,10 +15,12 @@
             return map;
         }
 
-        function loadMapPanel() {
+        function test_mappanel(t) {
+            t.plan(3)
+
             var map = createMap();
-
-            mapPanel = new GeoExt.MapPanel({
+            
+            var mapPanel = new GeoExt.MapPanel({
                 // panel options
                 id: "map-panel",
                 title: "GeoExt MapPanel",
@@ -30,17 +32,14 @@
                 center: new OpenLayers.LonLat(5, 45),
                 zoom: 4
             });
-
-            return mapPanel;
-        }
-
-        function test_mappanel(t) {
-            t.plan(3)
-            
-            loadMapPanel();
             t.eq(mapPanel.map.getCenter().toString(), "lon=5,lat=45", "Map center set correctly");
             t.eq(mapPanel.map.getZoom(), 4, "Zoom set correctly");
             t.eq(GeoExt.MapPanel.guess().id, mapPanel.id, "MapPanel guessed correctly");
+            
+            // since we created the map, we destroy it
+            map.destroy();
+            
+            mapPanel.destroy();
         }
         
         function test_allOverlays(t) {
@@ -52,11 +51,15 @@
                 map: map
             });
             t.eq(panel.map.allOverlays, false, "allOverlays is not set if map is provided to panel");
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
             
             panel = new GeoExt.MapPanel({
             });
             t.eq(panel.map.allOverlays, true, "allOverlays set to true if no map is provided to panel");
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
             
             panel = new GeoExt.MapPanel({
@@ -65,6 +68,8 @@
                 }
             });
             t.eq(panel.map.allOverlays, true, "allOverlays set to true if map config is provided to panel");
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
             
         }
@@ -85,6 +90,8 @@
             });            
             t.eq(log.extent.toArray(), [1, 2, 3, 4], "map extent set with array");
             delete log.extent;
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
             
             map = createMap();
@@ -97,6 +104,8 @@
                 extent: "1, 2, 3, 4"
             });
             t.eq(log.extent.toArray(), [1, 2, 3, 4], "map extent set with string");
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
             
             map = createMap();
@@ -109,6 +118,8 @@
                 extent: new OpenLayers.Bounds(1, 2, 3, 4)
             });
             t.eq(log.extent.toArray(), [1, 2, 3, 4], "map extent set with Bounds");
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();            
             
         }
@@ -129,6 +140,8 @@
             });            
             t.eq(log.center.toString(), "lon=1,lat=2", "map center set with array");
             delete log.center;
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
 
             map = createMap();
@@ -142,6 +155,8 @@
             });            
             t.eq(log.center.toString(), "lon=1,lat=2", "map center set with string");
             delete log.center;
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
 
             map = createMap();
@@ -155,9 +170,48 @@
             });            
             t.eq(log.center.toString(), "lon=1,lat=2", "map center set with LonLat");
             delete log.center;
+            // since we created the map, we destroy it
+            map.destroy();
             panel.destroy();
 
         }
+        
+        function test_destroy(t) {
+            
+            /**
+             * If the panel is passed an instance of OpenLayers.Map, we don't
+             * touch it in the destroy sequence, we only remove our reference
+             * to it.  If the panel is passed a map config object, the panel
+             * creates the OpenLayers.Map instance, and the panel destroys the
+             * map in its destroy sequence.
+             */
+            
+            t.plan(3);
+            
+            var panel = new GeoExt.MapPanel({
+                renderTo: "mappanel",
+                layers: [
+                    new OpenLayers.Layer("test")
+                ],
+                zoom: 1
+            });
+            
+            t.ok(panel.map instanceof OpenLayers.Map, "panel creates a map");
+            
+            var called = false;
+            panel.map.destroy = function() {
+                called = true;
+                OpenLayers.Map.prototype.destroy.apply(panel.map, arguments);
+            }
+            try {
+                panel.destroy();
+                t.ok(called, "panel.destroy calls map.destroy");
+            } catch(err) {
+                t.fail("panel.destroy causes problems: " + err);
+            }
+            t.ok(!panel.map, "panel has no reference to a map");
+            
+        }
 
 
     </script>

Modified: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/Popup.html
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/Popup.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/Popup.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -9,26 +9,13 @@
 
     <script type="text/javascript">
 
-        function makeFeature() {
-           return new OpenLayers.Feature.Vector(
-              new OpenLayers.Geometry.Point(100,50),
-              {
-                  name : "My Feature"
-              }
-           )
-        }
-       
-        function createMap() {
+        function setupContext() {        
+
             var map = new OpenLayers.Map();
             var layer = new OpenLayers.Layer("test", {isBaseLayer: true});
             map.addLayer(layer);
-            return map;
-        }
 
-        function loadMapPanel() {
-            var map = createMap();
-
-            mapPanel = new GeoExt.MapPanel({
+            var mapPanel = new GeoExt.MapPanel({
                 // panel options
                 id: "map-panel",
                 title: "GeoExt MapPanel",
@@ -41,35 +28,34 @@
                 zoom: 4
             });
 
-            return mapPanel;
-        }
+            var feature = new OpenLayers.Feature.Vector(
+                new OpenLayers.Geometry.Point(100,50),
+                {name: "My Feature"}
+            );
 
-        function setupContext() {        
-            var mapPanel = loadMapPanel();
-            var feature = makeFeature();
-            var map = mapPanel.map;
-
             return {
-               feature : feature,
-               map : map,
-               mapPanel : mapPanel
+                feature: feature,
+                map: map,
+                mapPanel: mapPanel
             };      
         }
+        
+        function tearDown(context) {
+            context.feature.destroy();
+            context.map.destroy();
+            context.mapPanel.destroy();
+        }
 
-        function popup(feature){
-          pop = new GeoExt.Popup({
-            title: 'My Popup',
-            feature: feature,
-            width:200,
-            html: bogusMarkup,
-            collapsible: true
-          });
-
-          return pop;
+        function popup(feature) {
+            return new GeoExt.Popup({
+                title: 'My Popup',
+                feature: feature,
+                width: 200,
+                html: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.",
+                collapsible: true
+            });
         }
 
-        var bogusMarkup = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit."
-
         function test_addtomappanel(t) {
             t.plan(1);
 
@@ -77,11 +63,11 @@
 
             var pop = popup(context.feature);
 
-            pop.addToMapPanel(context.mapPanel);
+            context.mapPanel.add(pop);
 
             t.ok(context.mapPanel.el.child("div." + pop.popupCls),"Map panel contains popup");
-
-            context.mapPanel.destroy();
+            
+            tearDown(context);
         }
 
         function test_anchorPopup(t) {
@@ -89,9 +75,9 @@
 
             var context = setupContext();
 
-            var pop = popup(context.feature, context.mapPanel);
+            var pop = popup(context.feature);
 
-            pop.addToMapPanel(context.mapPanel);
+            context.mapPanel.add(pop);
 
             pop.on({
                 'move' : function(c,x,y){
@@ -110,6 +96,8 @@
 
             action = "popup expand"
             pop.expand();
+            
+            tearDown(context);
         }
 
 
@@ -120,7 +108,7 @@
 
             var pop = popup(context.feature, context.mapPanel);
 
-            pop.addToMapPanel(context.mapPanel);
+            context.mapPanel.add(pop);
         
             pop.collapse();
 
@@ -152,34 +140,32 @@
             
             action = "popup collapse";
             pop.collapse();
+            
+            tearDown(context);
 
         }
 
-        function test_Popup_beforeDestroy(t){
+        function test_Popup_destroy(t){
 
-            t.plan(0);
+            t.plan(1);
 
             var context = setupContext();
-            var pop = popup(context.feature, context.mapPanel);
-            pop.addToMapPanel(context.mapPanel);
-
+            var pop = popup(context.feature);
+            context.mapPanel.add(pop);
+            
+            var called = false;
             pop.on({
-                'move' : function(c,x,y){
-                    t.ok(false,"Move event fired improperly on " + action); //should happen twice, on call to position()
-                },
-                scope : this
+                move: function() {
+                    called = true;
+                }
             });
+            pop.destroy();
 
-            pop.beforeDestroy();
-
-            var action = "map move";
             context.map.events.triggerEvent("move");
-
-            action = "popup expand"
-            pop.expand();
             
-            action = "popup collapse";
-            pop.collapse();
+            t.ok(!called, "pop is not moved after it is destroyed");
+            
+            tearDown(context);
         }
 
     </script>

Modified: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form/SearchAction.html
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form/SearchAction.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form/SearchAction.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -8,139 +8,6 @@
 
     <script type="text/javascript">
 
-    function test_filterFromForm(t) {
-        t.plan(27);
-
-        /*
-         * Set up
-         */
-
-        var form, filter, fields = [];
-
-        fields.push(new Ext.form.TextField({
-            name: "foo0",
-            value: "bar0"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo1__eq",
-            value: "bar1"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo2__ne",
-            value: "bar2"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo3__lt",
-            value: "bar3"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo4__le",
-            value: "bar4"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo5__gt",
-            value: "bar5"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo6__ge",
-            value: "bar6"
-        }));
-
-        fields.push(new Ext.form.TextField({
-            name: "foo7__like",
-            value: "bar7"
-        }));
-
-        form = new Ext.form.FormPanel({
-            renderTo: "form",
-            items: fields
-        });
-
-        /*
-         * Test
-         */
-
-        // 26 tests
-        filter = GeoExt.form.filterFromForm(form);
-
-        t.ok(filter instanceof OpenLayers.Filter.Logical,
-             "GeoExt.form.filterFormForm returns a logical filter");
-
-        t.eq(filter.type, OpenLayers.Filter.Logical.AND,
-             ["GeoExt.form.filterFormForm returns a logical AND filter if",
-              "logicalOp is undefined"].join(" "));
-
-        t.eq(filter.filters[0].type, OpenLayers.Filter.Comparison.EQUAL_TO,
-             "GeoExt.form.filterFormForm creates correct filter type");
-        t.eq(filter.filters[0].property, "foo0",
-             "GeoExt.form.filterFormForm creates correct filter prop");
-        t.eq(filter.filters[0].value, "bar0",
-             "GeoExt.form.filterFormForm creates correct filter value");
-
-        t.eq(filter.filters[1].type, OpenLayers.Filter.Comparison.EQUAL_TO,
-             "GeoExt.form.filterFormForm creates correct filter type (__eq)");
-        t.eq(filter.filters[1].property, "foo1",
-             "GeoExt.form.filterFormForm creates correct filter prop (__eq)");
-        t.eq(filter.filters[1].value, "bar1",
-             "GeoExt.form.filterFormForm creates correct filter value (__eq)");
-
-        t.eq(filter.filters[2].type, OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
-             "GeoExt.form.filterFormForm creates correct filter type (__ne)");
-        t.eq(filter.filters[2].property, "foo2",
-             "GeoExt.form.filterFormForm creates correct filter prop (__ne)");
-        t.eq(filter.filters[2].value, "bar2",
-             "GeoExt.form.filterFormForm creates correct filter value (__ne)");
-
-        t.eq(filter.filters[3].type, OpenLayers.Filter.Comparison.LESS_THAN,
-             "GeoExt.form.filterFormForm creates correct filter type (__lt)");
-        t.eq(filter.filters[3].property, "foo3",
-             "GeoExt.form.filterFormForm creates correct filter prop (__lt)");
-        t.eq(filter.filters[3].value, "bar3",
-             "GeoExt.form.filterFormForm creates correct filter value (__lt)");
-
-        t.eq(filter.filters[4].type, OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
-             "GeoExt.form.filterFormForm creates correct filter type (__le)");
-        t.eq(filter.filters[4].property, "foo4",
-             "GeoExt.form.filterFormForm creates correct filter prop (__le)");
-        t.eq(filter.filters[4].value, "bar4",
-             "GeoExt.form.filterFormForm creates correct filter value (__le)");
-
-        t.eq(filter.filters[5].type, OpenLayers.Filter.Comparison.GREATER_THAN,
-             "GeoExt.form.filterFormForm creates correct filter type (__gt)");
-        t.eq(filter.filters[5].property, "foo5",
-             "GeoExt.form.filterFormForm creates correct filter prop (__gt)");
-        t.eq(filter.filters[5].value, "bar5",
-             "GeoExt.form.filterFormForm creates correct filter value (__gt)");
-
-        t.eq(filter.filters[6].type, OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
-             "GeoExt.form.filterFormForm creates correct filter type (__ge)");
-        t.eq(filter.filters[6].property, "foo6",
-             "GeoExt.form.filterFormForm creates correct filter prop (__ge)");
-        t.eq(filter.filters[6].value, "bar6",
-             "GeoExt.form.filterFormForm creates correct filter value (__ge)");
-
-        t.eq(filter.filters[7].type, OpenLayers.Filter.Comparison.LIKE,
-             "GeoExt.form.filterFormForm creates correct filter type (__like)");
-        t.eq(filter.filters[7].property, "foo7",
-             "GeoExt.form.filterFormForm creates correct filter prop (__like)");
-        t.eq(filter.filters[7].value, "bar7",
-             "GeoExt.form.filterFormForm creates correct filter value (__like)");
-
-        // 1 test
-        filter = GeoExt.form.filterFromForm(form,
-                                            OpenLayers.Filter.Logical.OR);
-
-        t.eq(filter.type, OpenLayers.Filter.Logical.OR,
-             ["GeoExt.form.filterFormForm returns a logical OR filter if",
-              "logicalOp is OpenLayers.Filter.Logical.OR"].join(" "));
-    }
-
     function test_constructor(t) {
         t.plan(1);
 

Copied: sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form.html (from rev 875, core/trunk/geoext/tests/lib/GeoExt/widgets/form.html)
===================================================================
--- sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form.html	                        (rev 0)
+++ sandbox/elemoine/playground/geoext/tests/lib/GeoExt/widgets/form.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html debug="true">
+  <head>
+    <script type="text/javascript" src="../../../../../openlayers/lib/OpenLayers.js"></script>
+    <script type="text/javascript" src="../../../../../ext/adapter/ext/ext-base.js"></script>
+    <script type="text/javascript" src="../../../../../ext/ext-all-debug.js"></script>
+    <script type="text/javascript" src="../../../../lib/GeoExt.js"></script>
+
+    <script type="text/javascript">
+
+    function test_toFilter(t) {
+        t.plan(27);
+
+        /*
+         * Set up
+         */
+
+        var form, filter, fields = [];
+
+        fields.push(new Ext.form.TextField({
+            name: "foo0",
+            value: "bar0"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo1__eq",
+            value: "bar1"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo2__ne",
+            value: "bar2"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo3__lt",
+            value: "bar3"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo4__le",
+            value: "bar4"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo5__gt",
+            value: "bar5"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo6__ge",
+            value: "bar6"
+        }));
+
+        fields.push(new Ext.form.TextField({
+            name: "foo7__like",
+            value: "bar7"
+        }));
+
+        form = new Ext.form.FormPanel({
+            renderTo: "form",
+            items: fields
+        });
+
+        /*
+         * Test
+         */
+
+        // 26 tests
+        filter = GeoExt.form.toFilter(form);
+
+        t.ok(filter instanceof OpenLayers.Filter.Logical,
+             "GeoExt.form.filterFormForm returns a logical filter");
+
+        t.eq(filter.type, OpenLayers.Filter.Logical.AND,
+             ["GeoExt.form.filterFormForm returns a logical AND filter if",
+              "logicalOp is undefined"].join(" "));
+
+        t.eq(filter.filters[0].type, OpenLayers.Filter.Comparison.EQUAL_TO,
+             "GeoExt.form.filterFormForm creates correct filter type");
+        t.eq(filter.filters[0].property, "foo0",
+             "GeoExt.form.filterFormForm creates correct filter prop");
+        t.eq(filter.filters[0].value, "bar0",
+             "GeoExt.form.filterFormForm creates correct filter value");
+
+        t.eq(filter.filters[1].type, OpenLayers.Filter.Comparison.EQUAL_TO,
+             "GeoExt.form.filterFormForm creates correct filter type (__eq)");
+        t.eq(filter.filters[1].property, "foo1",
+             "GeoExt.form.filterFormForm creates correct filter prop (__eq)");
+        t.eq(filter.filters[1].value, "bar1",
+             "GeoExt.form.filterFormForm creates correct filter value (__eq)");
+
+        t.eq(filter.filters[2].type, OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+             "GeoExt.form.filterFormForm creates correct filter type (__ne)");
+        t.eq(filter.filters[2].property, "foo2",
+             "GeoExt.form.filterFormForm creates correct filter prop (__ne)");
+        t.eq(filter.filters[2].value, "bar2",
+             "GeoExt.form.filterFormForm creates correct filter value (__ne)");
+
+        t.eq(filter.filters[3].type, OpenLayers.Filter.Comparison.LESS_THAN,
+             "GeoExt.form.filterFormForm creates correct filter type (__lt)");
+        t.eq(filter.filters[3].property, "foo3",
+             "GeoExt.form.filterFormForm creates correct filter prop (__lt)");
+        t.eq(filter.filters[3].value, "bar3",
+             "GeoExt.form.filterFormForm creates correct filter value (__lt)");
+
+        t.eq(filter.filters[4].type, OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
+             "GeoExt.form.filterFormForm creates correct filter type (__le)");
+        t.eq(filter.filters[4].property, "foo4",
+             "GeoExt.form.filterFormForm creates correct filter prop (__le)");
+        t.eq(filter.filters[4].value, "bar4",
+             "GeoExt.form.filterFormForm creates correct filter value (__le)");
+
+        t.eq(filter.filters[5].type, OpenLayers.Filter.Comparison.GREATER_THAN,
+             "GeoExt.form.filterFormForm creates correct filter type (__gt)");
+        t.eq(filter.filters[5].property, "foo5",
+             "GeoExt.form.filterFormForm creates correct filter prop (__gt)");
+        t.eq(filter.filters[5].value, "bar5",
+             "GeoExt.form.filterFormForm creates correct filter value (__gt)");
+
+        t.eq(filter.filters[6].type, OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+             "GeoExt.form.filterFormForm creates correct filter type (__ge)");
+        t.eq(filter.filters[6].property, "foo6",
+             "GeoExt.form.filterFormForm creates correct filter prop (__ge)");
+        t.eq(filter.filters[6].value, "bar6",
+             "GeoExt.form.filterFormForm creates correct filter value (__ge)");
+
+        t.eq(filter.filters[7].type, OpenLayers.Filter.Comparison.LIKE,
+             "GeoExt.form.filterFormForm creates correct filter type (__like)");
+        t.eq(filter.filters[7].property, "foo7",
+             "GeoExt.form.filterFormForm creates correct filter prop (__like)");
+        t.eq(filter.filters[7].value, "bar7",
+             "GeoExt.form.filterFormForm creates correct filter value (__like)");
+
+        // 1 test
+        filter = GeoExt.form.toFilter(form,
+                                      OpenLayers.Filter.Logical.OR);
+
+        t.eq(filter.type, OpenLayers.Filter.Logical.OR,
+             ["GeoExt.form.filterFormForm returns a logical OR filter if",
+              "logicalOp is OpenLayers.Filter.Logical.OR"].join(" "));
+    }
+    </script>
+  <body>
+    <div id="form"></div>
+  </body>
+</html>

Modified: sandbox/elemoine/playground/geoext/tests/list-tests.html
===================================================================
--- sandbox/elemoine/playground/geoext/tests/list-tests.html	2009-05-27 22:24:26 UTC (rev 875)
+++ sandbox/elemoine/playground/geoext/tests/list-tests.html	2009-05-28 03:49:48 UTC (rev 876)
@@ -11,9 +11,11 @@
   <li>lib/GeoExt/widgets/Action.html</li>
   <li>lib/GeoExt/widgets/MapPanel.html</li>
   <li>lib/GeoExt/widgets/Popup.html</li>
+  <li>lib/GeoExt/widgets/form.html</li>
   <li>lib/GeoExt/widgets/form/SearchAction.html</li>
   <li>lib/GeoExt/widgets/form/BasicForm.html</li>
   <li>lib/GeoExt/widgets/form/FormPanel.html</li>
   <li>lib/GeoExt/widgets/tree/LayerNode.html</li>
   <li>lib/GeoExt/widgets/tree/LayerContainer.html</li>
+  <li>lib/GeoExt/widgets/LegendPanel.html</li>
 </ul>



More information about the Commits mailing list