Changeset 1258

Show
Ignore:
Timestamp:
10/30/08 10:27:54 (2 months ago)
Author:
elemoine
Message:

big refactoring of the feature editing panel
- setUp/tearDown implemented
- i18n
- only one vector layer used
- use Strategy.BBOX internally

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • sandbox/camptocamp/MapFishUnhcr/client/examples/editing/editing-panel.html

    r1227 r1258  
    9292                    ) 
    9393                }), 
    94                 strategy: new OpenLayers.Strategy.BBOX({ 
    95                     tileSize: new OpenLayers.Size(256, 256) 
    96                 }), 
    9794                featuretypes: { 
    9895                    geometry: { 
     
    111108                        mapfish.SERVER_BASE_URL + 'lines' 
    112109                    ) 
    113                 }), 
    114                 strategy: new OpenLayers.Strategy.BBOX({ 
    115                     tileSize: new OpenLayers.Size(256, 256) 
    116110                }), 
    117111                featuretypes: { 
     
    132126                        mapfish.SERVER_BASE_URL + 'points' 
    133127                    ) 
    134                 }), 
    135                 strategy: new OpenLayers.Strategy.BBOX({ 
    136                     tileSize: new OpenLayers.Size(256, 256) 
    137128                }), 
    138129                featuretypes: { 
  • sandbox/camptocamp/MapFishUnhcr/client/mfbase/mapfish/lang/en.js

    r1135 r1258  
    2323    'mf.error': 'Error', 
    2424    'mf.warning': 'Warning', 
    25     'mf.information': 'Information' 
     25    'mf.information': 'Information', 
     26 
     27    'mf.editing.comboNoneName': 'None', 
     28    'mf.editing.import': 'Import', 
     29    'mf.editing.importTooltip': 'Import data', 
     30    'mf.editing.commit': 'Commit', 
     31    'mf.editing.commitTooltip': 'Commit data', 
     32    'mf.editing.delete': 'Delete', 
     33    'mf.editing.deleteTooltip': 'Delete selected feature', 
     34    'mf.editing.comboLabel': 'Layer to edit', 
     35    'mf.editing.confirmMessageTitle': 'Edited features', 
     36    'mf.editing.confirmMessage': 'There are uncommitted features, are you sure ' + 
     37                                 'you want to switch layer?', 
     38    'mf.editing.selectModifyFeature': 'Modify features', 
     39    'mf.editing.drawPointTitle': 'Draw points', 
     40    'mf.editing.drawLineTitle': 'Draw lines', 
     41    'mf.editing.drawPolygonTitle': 'Draw polygons', 
     42    'mf.editing.formTitle': 'Attributes', 
     43    'mf.editing.gridIdHeader': 'Id', 
     44    'mf.editing.gridStateHeader': 'State', 
     45    'mf.editing.gridTitle': 'Edited features', 
     46    'mf.editing.onContextClickMessage': 'Edit this feature', 
     47    'mf.editing.onBeforeUnloadMessage': 'The feature editing panel has ' + 
     48                                        'uncommitted features' 
    2649}); 
  • sandbox/camptocamp/MapFishUnhcr/client/mfbase/mapfish/widgets/editing/FeatureEditingPanel.js

    r1198 r1258  
    3737 
    3838    /** 
    39      * Property: map 
    40      * {<OpenLayers.Map>} - OpenLayers Map object 
     39     * Constant: COMBO_NONE_VALUE 
     40     * The value of the none entry in the combo. 
     41     */ 
     42    COMBO_NONE_VALUE: "__combo_none_value__", 
     43 
     44    /** 
     45     * Constant: COMBO_NONE_NAME 
     46     * The name (label) of the none entry in the combo. 
     47     */ 
     48    COMBO_NONE_NAME: OpenLayers.i18n("mf.editing.comboNoneName"), 
     49 
     50    /** 
     51     * APIProperty: map 
     52     * {<OpenLayers.Map>} OpenLayers Map object. 
    4153     */ 
    4254    map: null, 
    4355 
    4456    /** 
    45      * Property: layerConfig 
    46      * {Object} Hash of layers with config parameters 
    47      * 
    48      * Should look like : 
    49      * 
    50 
    51     campfacilities: { 
    52         text: 'Camps', 
    53         strategy: new OpenLayers.Strategy.BBOX({ratio: 4}), 
    54         protocol: new mapfish.Protocol.MapFish({'url': 'camps'}), 
    55         format: new OpenLayers.Format.GeoJSON(), 
    56         featuretypes: { 
    57             geometry: { 
    58                 type: OpenLayers.Geometry.MultiPolygon 
    59             }, 
    60             // See the documentation in the mapfish.widgets.editing.*Property classes for 
    61             // more details 
    62             properties: [ 
    63                 new mapfish.widgets.editing.StringProperty({name: 'comment'}), 
    64                 new mapfish.widgets.editing.IntegerProperty({name: 'status'}), 
    65                 new mapfish.widgets.editing.ComboProperty({name: '_type', url: 'campfacilitytypes'}), 
    66                 new mapfish.widgets.editing.IntegerProperty({name: 'name', showInGrid: true}), 
    67                 new mapfish.widgets.editing.FloatProperty({name: 'camp_id'}) 
    68             ] 
    69  
    70         } 
    71     }, 
    72     refugees : { 
    73         ... 
    74     } 
    75 
     57     * APIProperty: layerConfig 
     58     * {Object} Hash of layers with config parameters. 
     59     * 
     60     * Example: 
     61     * (code start) 
     62     * { 
     63     *     campfacilities: { 
     64     *         text: "Camps", 
     65     *         protocol: new mapfish.Protocol.MapFish({url: "camps"}), 
     66     *         featuretypes: { 
     67     *             geometry: { 
     68     *                 type: OpenLayers.Geometry.MultiPolygon 
     69     *             }, 
     70     *             // See the documentation in the mapfish.widgets.editing.*Property 
     71     *             // classes for more details 
     72     *             properties: [ 
     73     *                 new mapfish.widgets.editing.StringProperty( 
     74     *                     {name: 'comment'}), 
     75     *                 new mapfish.widgets.editing.IntegerProperty( 
     76     *                     {name: 'status'}), 
     77     *                 new mapfish.widgets.editing.ComboProperty( 
     78     *                     {name: '_type', url: 'campfacilitytypes'}), 
     79     *                 new mapfish.widgets.editing.IntegerProperty( 
     80     *                     {name: 'name', showInGrid: true}), 
     81     *                 new mapfish.widgets.editing.FloatProperty( 
     82     *                     {name: 'camp_id'}) 
     83     *             ] 
     84     *         } 
     85     *     }, 
     86     *     refugees: { 
     87     *         ... 
     88     *     } 
     89     * } 
     90     * (end) 
    7691     */ 
    7792    layerConfig: null, 
    7893 
    7994    /** 
    80      * Property: layersCombo 
    81      * {Ext.form.ComboBox} 
    82      */ 
    83     layersCombo: null, 
    84  
    85     /** 
    86      * Property: attributesEditingForm 
    87      * {Ext.FormPanel} 
    88      */ 
    89     attributesEditingForm: null, 
    90  
    91     /** 
    92      * Property: currentlyEditedFeature 
    93      * {<OpenLayers.Feature>} 
    94      */ 
    95     currentlyEditedFeature: null, 
     95     * Property: combo 
     96     * {Ext.form.ComboBox} The combo box to select the layer to edit. 
     97     */ 
     98    combo: null, 
     99 
     100    /** 
     101     * Property: form 
     102     * {Ext.FormPanel} The form to edit attributes. 
     103     */ 
     104    form: null, 
    96105 
    97106    /** 
    98107     * Property: store 
    99      * {Ext.data.Store} 
     108     * {Ext.data.Store} The feature store for the grid. 
    100109     */ 
    101110    store: null, 
    102111 
    103112    /** 
    104      * Property: featureStoreMediator 
    105      * {<mapfish.widgets.data.FeatureStoreMediator>} 
    106      */ 
    107     featureStoreMediator: null, 
    108  
    109     /** 
    110      * Property: editedFeatureGrid 
    111      * {Ext.grid.GridPanel} 
    112      */ 
    113     editedFeatureGrid: null, 
    114  
    115     /** 
    116      * Property: currentLayer 
    117      * {<OpenLayers.Layer.Vector>} 
    118      */ 
    119     currentLayer: null, 
     113     * Property: layerStoreMediator 
     114     * {<mapfish.widgets.data.LayerStoreMediator>} The layer store 
     115     * mediator, it updates the store each time features are modified, 
     116     * added to or removed from the layer. 
     117     */ 
     118    layerStoreMediator: null, 
     119 
     120    /** 
     121     * Property: grid 
     122     * {Ext.grid.GridPanel} The grid to store the edited features. 
     123     */ 
     124    grid: null, 
     125 
     126    /** 
     127     * Property: menu 
     128     * {Ext.menu.Menu} Context menu. 
     129     */ 
     130    menu: null, 
     131 
     132    /** 
     133     * Property: layer 
     134     * {<OpenLayers.Layer.Vector>} The vector layer. 
     135     */ 
     136    layer: null, 
     137 
     138    /** 
     139     * Property: currentLayerId 
     140     * {String} The identifier of the current edited layer. 
     141     */ 
     142    currentLayerId: null, 
    120143 
    121144    /** 
    122145     * Property: modifyControl 
    123      * {<OpenLayers.Control.ModifyFeature>} 
    124      */ 
    125     modifyControl: null, 
     146     * {<OpenLayers.Control.ModifyFeature>}  The modify feature 
     147     * control. 
     148     */ 
     149    modifyFeatureControl: null, 
    126150 
    127151    /** 
    128152     * Property: drawFeatureControl 
    129      * {<OpenLayers.Control.DrawFeature>} 
     153     * {<OpenLayers.Control.DrawFeature>} The draw feature control. 
    130154     */ 
    131155    drawFeatureControl: null, 
     
    133157    /** 
    134158     * Property: importBtn 
    135      * {Ext.Button} - Reference to the import button 
     159     * {Ext.Button} The import button. 
    136160     */ 
    137161    importBtn: null, 
     
    139163    /** 
    140164     * Property: commitBtn 
    141      * {Ext.Button} - Reference to the commit button 
     165     * {Ext.Button} Rhe commit button. 
    142166     */ 
    143167    commitBtn: null, 
     
    145169    /** 
    146170     * Property: deleteBtn 
    147      * {Ext.Button} - Reference to the delete button 
     171     * {Ext.Button} The delete button. 
    148172     */ 
    149173    deleteBtn: null, 
    150174 
    151175    /** 
     176     * Property: attributesFormDefaults 
     177     * {Ext.data.Record} A record representing default attributes 
     178     * in the form. 
     179     */ 
     180    attributesFormDefaults: null, 
     181 
     182    /** 
    152183     * Method: initComponent 
    153      *      Initialize the component. 
     184     * Initialize the component. 
    154185     */ 
    155186    initComponent: function() { 
    156         // sanity checks 
    157187        if (!this.map) { 
    158188            OpenLayers.Console.error( 
    159                 "Mandatory param for FeatureEditingPanel missing: map"); 
     189                "map option for FeatureEditingPanel missing"); 
    160190        } 
    161191        if (!this.layerConfig) { 
    162192            OpenLayers.Console.error( 
    163                 "Mandatory param for FeatureEditingPanel missing: layerConfig"); 
     193                "layerConfig option for FeatureEditingPanel missing"); 
    164194        } 
    165195 
    166196        this.layout = 'form'; 
    167  
    168         // layer chooser combobox 
    169         var combo = this.buildLayersCombo(); 
    170         this.items = [combo]; 
    171  
    172         this.initToolbar(); 
     197        this.tbar = this.createToolbar(); 
    173198 
    174199        mapfish.widgets.editing.FeatureEditingPanel.superclass.initComponent.apply(this); 
    175200 
    176         window.onbeforeunload = (function(e) { 
    177             if (this.isDirty()) { 
    178                 return "There are unsaved changes!"
    179             } 
    180         }).createDelegate(this); 
    181  
    182         this.addEvents( 
    183             /** 
    184              * @event beforecommit 
    185              * Triggered when the commit button is hit and 
    186              *      before the actual commit occurs 
    187              */ 
    188             'beforecommit', 
    189             /** 
    190              * @event commit 
    191              * Triggered when the commit is done. 
    192              */ 
    193             'commit' 
    194         ); 
    195     }, 
    196  
    197     /*
    198      * Method: initToolbar 
    199      * 
    200      */ 
    201     initToolbar: function() { 
     201        this.add(this.createLayerCombo()); 
     202 
     203        this.on("destroy", this.destroyResources, this)
     204 
     205        this.on("enable", this.setUp, this); 
     206        this.on("disable", this.tearDown, this); 
     207 
     208        // for accordion 
     209        this.on('expand', this.setUp, this); 
     210        this.on('collapse', this.tearDown, this); 
     211 
     212        // for tabs 
     213        this.on('activate', this.setUp, this); 
     214        this.on('deactivate', this.tearDown, this); 
     215 
     216        this.addEvents('beforecommit', 'commit'); 
     217    }, 
     218 
     219    /** 
     220    * Method: createToolbar 
     221     * Create the toolbar with the editing tools. 
     222   
     223     * Returns: 
     224     * {<mapfish.widgets.toolbar.Toolbar>} MapFish toolbar. 
     225     */ 
     226    createToolbar: function() { 
    202227        this.importBtn = new Ext.Button({ 
    203             text: 'Import'
    204             tooltip: 'Import data for the current layer and extent'
     228            text: OpenLayers.i18n("mf.editing.import")
     229            tooltip: OpenLayers.i18n("mf.editing.importTooltip")
    205230            disabled: true, 
    206231            handler: function() { 
     
    210235        }); 
    211236        this.commitBtn = new Ext.Button({ 
    212             text: 'Commit'
    213             tooltip: 'Commit features'
     237            text: OpenLayers.i18n("mf.editing.commit")
     238            tooltip: OpenLayers.i18n("mf.editing.commitTooltip")
    214239            disabled: true, 
    215240            handler: function() { 
     
    219244        }); 
    220245        this.deleteBtn = new Ext.Button({ 
    221             text: 'Delete'
    222             tooltip: 'Delete selected features'
     246            text: OpenLayers.i18n("mf.editing.delete")
     247            tooltip: OpenLayers.i18n("mf.editing.deleteTooltip")
    223248            disabled: true, 
    224249            handler: function() { 
    225250                var toDelete = this.deleteFeatures(); 
    226                 this.featureStoreMediator.addFeatures(toDelete); 
     251                var fsm = this.layerStoreMediator.featureStoreMediator; 
     252                fsm.addFeatures(toDelete); 
    227253            }, 
    228254            scope: this 
     
    235261            this.deleteBtn 
    236262        ]; 
    237  
    238         var tb = new mapfish.widgets.toolbar.Toolbar({ 
     263        return new mapfish.widgets.toolbar.Toolbar({ 
    239264            items: buttons, 
    240265            map: this.map 
    241266        }); 
    242         this.tbar = tb; 
    243     }, 
    244  
    245     /** 
    246      * Method: isDirty 
    247      * Checks for unsaved (uncommited) changes 
    248      * 
    249      * Returns 
    250      * {Boolean} - true is there are no unsaved changes 
    251      */ 
    252     isDirty: function() { 
    253         return (this.store && 
    254                 this.store.getCount() > 0); 
    255     }, 
    256  
    257     /** 
    258      * Method: prepareChangeLayer 
    259      * Is called when the user selects a layer in the combobox. 
    260      *     Makes the sanity checks before we change layer. 
    261      * 
    262      * Parameters: 
    263      * layer - {String} The layer id (key in layerConfig) 
    264      */ 
    265     prepareChangeLayer: function(layer) { 
     267    }, 
     268 
     269    /** 
     270     * Method: refreshFeatures 
     271     * Refresh the vector layor. 
     272     */ 
     273    refreshFeatures: function() { 
     274        // we created the layer ourself so we're assured there's 
     275        // only one strategy configured into it 
     276        this.layer.strategies[0].update({force: true}); 
     277    }, 
     278 
     279    /** 
     280     * Method: commitFeatures 
     281     * Commit the modified features. 
     282     */ 
     283    commitFeatures: function() { 
     284        // callback func called on each update and create 
     285        function onUpdateCreate(resp) { 
     286            // if the modify feature control has a selected feature, 
     287            // unselect it 
     288            if (this.modifyFeatureControl.feature) { 
     289                this.modifyFeatureControl.unselectFeature( 
     290                    {feature: this.modifyFeatureControl.feature} 
     291                ); 
     292            } 
     293            var toDestroy = (resp.reqFeatures instanceof Array) ? 
     294                            resp.reqFeatures : [resp.reqFeatures]; 
     295            this.layer.destroyFeatures(toDestroy); 
     296            this.layer.addFeatures(resp.features); 
     297            if (resp.last) { 
     298                this.fireEvent("commit"); 
     299            } 
     300        } 
     301        // callback func called on each delete 
     302        function onDelete(resp) { 
     303            var features = resp.reqFeatures; 
     304            if (!(features instanceof Array)) { 
     305                features = [features]; 
     306            } 
     307            this.layer.destroyFeatures(features); 
     308            if (resp.last) { 
     309                this.fireEvent("commit"); 
     310            } 
     311        } 
     312        this.fireEvent("beforecommit"); 
     313        this.layer.protocol.commit(this.layer.features, { 
     314            "create": { 
     315                scope: this, 
     316                callback: onUpdateCreate 
     317            }, 
     318            "update": { 
     319                scope: this, 
     320                callback: onUpdateCreate 
     321            }, 
     322            "delete": { 
     323                scope: this, 
     324                callback: onDelete 
     325            } 
     326        }); 
     327    }, 
     328 
     329    /** 
     330     * Method: deleteFeatures 
     331     * 
     332     * Returns: 
     333     * {Array({<OpenLayers.Feature.Vector>})} The array of features 
     334     *     that are marked for deletion. 
     335     */ 
     336    deleteFeatures: function() { 
     337        var toDelete = []; 
     338        var selected = this.layer.selectedFeatures; 
     339        for (var i = selected.length - 1; i >= 0; i--) { 
     340            var feature = selected[i]; 
     341            if (feature.state == OpenLayers.State.INSERT) { 
     342                // feature was created as part of the current "transaction", 
     343                // so just destroy it right away 
     344                this.layer.destroyFeatures([feature]); 
     345            } else { 
     346                feature.state = OpenLayers.State.DELETE; 
     347                toDelete.push(feature); 
     348            } 
     349            // if the modify feature control has a selected feature, 
     350            // unselect it 
     351            if (this.modifyFeatureControl.feature) { 
     352                this.modifyFeatureControl.selectControl.unselect( 
     353                    this.modifyFeatureControl.feature 
     354                ); 
     355            } 
     356        } 
     357        return toDelete; 
     358    }, 
     359 
     360    /** 
     361     * Method: createLayerCombo 
     362     * Create a combobox to let user choose the layer to edit. 
     363     * 
     364     * Returns: 
     365     * {Ext.form.ComboxBox} A combobox 
     366     */ 
     367    createLayerCombo: function() { 
     368        var data = [[this.COMBO_NONE_VALUE, this.COMBO_NONE_NAME]]; 
     369        for (var i in this.layerConfig) { 
     370            data.push([i, this.layerConfig[i].text]); 
     371        } 
     372        var store = new Ext.data.SimpleStore({ 
     373            fields: ['value', 'text'], 
     374            data : data 
     375        }); 
     376        this.combo = new Ext.form.ComboBox({ 
     377            fieldLabel: OpenLayers.i18n("mf.editing.comboLabel"), 
     378            name: "editingLayer", 
     379            hiddenName: "editingLayer", 
     380            displayField: "text", 
     381            valueField: "value", 
     382            mode: "local", 
     383            triggerAction: "all", 
     384            editable: false, 
     385            store: store, 
     386            listeners: { 
     387                select: function(combo, record, index) { 
     388                    this.prepareSwitchLayer(record.data.value); 
     389                }, 
     390                scope: this 
     391            } 
     392        }); 
     393        return this.combo; 
     394    }, 
     395 
     396    /** 
     397     * Method: prepareSwitchLayer 
     398     * Called when the user selects a layer in the combobox. 
     399     * 
     400     * Parameters: 
     401     * id - {String} The layer id (key in layerConfig) 
     402     */ 
     403    prepareSwitchLayer: function(id) { 
    266404        if (this.isDirty()) { 
    267             Ext.Msg.confirm('Edited features', 
    268                 'There are uncommited updates. Are ' + 
    269                 'you sure you want to change the editing layer.'
     405            Ext.Msg.confirm( 
     406                OpenLayers.i18n("mf.editing.confirmMessageTitle"), 
     407                OpenLayers.i18n("mf.editing.confirmMessage")
    270408                function(btn, text){ 
    271                     if (btn == 'yes'){ 
    272                         // process text value and close... 
    273                         this.changeLayer(layer); 
     409                    if (btn == "yes") { 
     410                        this.switchLayer(id); 
    274411                    } else { 
    275                         this.layersCombo.setValue(this.currentLayer.name); 
     412                        this.combo.setValue(this.currentLayerId); 
    276413                    } 
    277414                }, 
    278                 this // scope 
     415                this 
    279416            ); 
    280417        } else { 
    281             this.changeLayer(layer); 
    282         } 
    283  
    284     }, 
    285  
    286     /** 
    287      * Method: changeLayer 
    288      * Is called by prepareChangeLayer 
    289      *     Builds the OL Layer, and Ext form and grid. 
    290      * 
    291      * Parameters: 
    292      * layer - {String} The layer id (key in layerConfig) 
    293      */ 
    294     changeLayer: function(layer) { 
    295         this.createLayer(layer); 
    296         this.importBtn.enable(); 
    297         this.buildAttributesForm(layer); 
    298         this.buildFeatureGrid(layer); 
     418            this.switchLayer(id); 
     419        } 
     420    }, 
     421 
     422    /** 
     423     * Method: switchLayer 
     424     * 
     425     * Parameters: 
     426     * id - {String} The layer id (key in layerConfig) 
     427     */ 
     428    switchLayer: function(id) { 
     429        if (id != this.COMBO_NONE_VALUE) { 
     430            var config = this.layerConfig[id]; 
     431            this.configureLayer(config); 
     432            this.createStore(config); 
     433            this.createModifyFeatureControl(); 
     434            this.createDrawFeatureControl(config); 
     435            this.createLayerStoreMediator(); 
     436            this.importBtn.enable(); 
     437            this.createForm(config); 
     438            this.createGrid(config); 
     439        } else { 
     440            this.destroyAllResources(); 
     441        } 
     442        this.currentLayerId = id; 
     443    }, 
     444 
     445    /** 
     446     * Method: configureLayer 
     447     * 
     448     * Parameters: 
     449     * config - {Object} The layer configuration. 
     450     */ 
     451    configureLayer: function(config) { 
     452        var layer = this.layer; 
     453        if (!layer) { 
     454            layer = this.layer = this.createLayer(); 
     455        } 
     456        // we don't want to destroy the protocol when the layer 
     457        // is destroyed 
     458        config.protocol.autoDestroy = false; 
     459        layer.protocol = config.protocol; 
     460        if (OpenLayers.Util.indexOf(this.map.layers, layer) < 0) { 
     461            this.map.addLayer(layer); 
     462        } 
     463        layer.destroyFeatures(); 
    299464    }, 
    300465 
    301466    /** 
    302467     * Method: createLayer 
    303      * Creates the vector layer in the map, and the corresponding controls 
    304      *     for the map 
    305      * 
    306      * Parameters: 
    307      * layer - {String} The layer id (key in layerConfig) 
    308      */ 
    309     // FIXME: this method does much more than creating a layer. It should be split. 
    310     createLayer: function(layer) { 
    311         var config = this.layerConfig[layer]; 
    312         // sanity checks 
    313         if (!config.text) { 
    314             config.text = 'vector'; 
    315         } 
    316  
    317         this.getTopToolbar().deactivate(); 
    318         if (this.modifyControl) { 
    319             this.modifyControl.destroy(); 
    320             this.getTopToolbar().getButtonForControl(this.modifyControl).destroy(); 
    321         } 
    322         if (this.drawFeatureControl) { 
    323             this.drawFeatureControl.destroy(); 
    324             this.getTopToolbar().getButtonForControl(this.drawFeatureControl).destroy(); 
    325         } 
    326         if (this.currentLayer) { 
    327             // set this.currentLayer to null before destroying the layer, to 
    328             // avoid the problems with a layer with no map object in the 
    329             // moveLayerToTop function below 
    330             var toDestroy = this.currentLayer; 
    331             this.currentLayer = null; 
    332             toDestroy.destroy(); 
    333         } 
    334  
     468     * Create the vector layer. 
     469     */ 
     470    createLayer: function() { 
     471        var layer = new OpenLayers.Layer.Vector( 
     472            OpenLayers.Util.createUniqueID("mf.ediding"), { 
     473            strategies: [this.createStrategy()], 
     474            styleMap: this.createStyleMap(), 
     475            displayInLayerSwitcher: false 
     476        }); 
     477        layer.events.register( 
     478            "featureselected", this, this.onFeatureselected 
     479        ); 
     480        layer.events.register( 
     481            "featureunselected", this, this.onFeatureunselected 
     482        ); 
     483        layer.events.register( 
     484            "featureremoved", this, this.onFeatureremoved 
     485         ); 
     486        layer.events.register( 
     487            "featuremodified", this, this.onFeaturemodified 
     488        ); 
     489        return layer; 
     490    }, 
     491 
     492    /** 
     493     * Method: destroyLayer 
     494     * Destroy the vector layer. 
     495     */ 
     496    destroyLayer: function() { 
     497        var layer = this.layer; 
     498        if (layer) { 
     499            layer.destroy(); 
     500            this.layer = null; 
     501        } 
     502    }, 
     503 
     504    /** 
     505     * Method: createStyleMap 
     506     * Create a style map for the vector layer. 
     507     * 
     508     * Returns: 
     509     * {<OpenLayer.StyleMap>} The style map. 
     510     */ 
     511    createStyleMap: function() { 
     512        var styleMap = new OpenLayers.StyleMap(); 
    335513        // create a styleMap for the vector layer so that features 
    336514        // have different styles depending on their state 
    337         // FIXME: the lookup should be given as an option in constructor 
    338515        var context = function(feature) { 
    339516            return { 
    340517                state: feature.state || OpenLayers.State.UNKNOWN 
    341518            }; 
    342         } 
    343         var styleMap = new OpenLayers.StyleMap(); 
     519        }; 
    344520        var lookup = { 
    345521            "Unknown": {}, 
     
    358534                strokeColor: "green" 
    359535            } 
    360         } 
     536        }; 
    361537        styleMap.addUniqueValueRules("default", "state", lookup, context); 
    362  
    363         config.strategy.autoDestroy = false; 
    364         config.protocol.autoDestroy = false; 
    365  
    366         this.currentLayer = new OpenLayers.Layer.Vector(layer, { 
    367             strategies: [config.strategy], 
    368             protocol: config.protocol, 
    369             format: config.format, 
    370             styleMap: styleMap, 
    371             displayInLayerSwitcher: false 
    372         }); 
    373  
    374         this.map.addLayer(this.currentLayer); 
    375  
    376         this.currentLayer.events.on({ 
    377             'featuremodified': function(obj) { 
    378                 var feature = obj.feature; 
    379                 if (feature.state != OpenLayers.State.INSERT) { 
    380                     feature.state = OpenLayers.State.UPDATE; 
    381                 } 
    382              } 
    383          }); 
    384  
    385         var title, handler, iconCls; 
    386         switch (config.featuretypes.geometry.type) { 
    387             case OpenLayers.Geometry.Point: 
    388                 title = 'draw point'; 
    389                 handler = OpenLayers.Handler.Point; 
    390                 iconCls = 'drawpoint'; 
    391                 break; 
    392             case OpenLayers.Geometry.Polygon: 
    393             case OpenLayers.Geometry.MultiPolygon: 
    394                 title = 'draw polygon'; 
    395                 handler = OpenLayers.Handler.Polygon; 
    396                 iconCls = 'drawpolygon'; 
    397                 break; 
    398             case OpenLayers.Geometry.LineString: 
    399             case OpenLayers.Geometry.MultiLineString: 
    400                 title = 'draw line'; 
    401                 handler = OpenLayers.Handler.Path; 
    402                 iconCls = 'drawline'; 
    403                 break; 
    404         } 
    405  
    406         this.modifyControl = new OpenLayers.Control.ModifyFeature( 
    407             this.currentLayer, 
    408             { 
    409                 mode: OpenLayers.Control.ModifyFeature.RESHAPE | OpenLayers.Control.ModifyFeature.DRAG, 
    410                 title: 'select and modify a feature' 
    411             } 
    412         ); 
    413         // the modify feature control's deactivate method calls selectControl.unselect 
    414         // if a feature is being modified, this causes problem if that feature isn't 
    415         // known by the vector layer 
    416         this.modifyControl.layer.events.on({ 
    417             featureremoved: function(obj) { 
    418                 if (obj.feature == this.feature) { 
    419                     this.feature = null; 
    420                 } 
    421             }, 
    422             scope: this.modifyControl 
    423         }); 
    424  
    425         this.drawFeatureControl = new OpenLayers.Control.DrawFeature( 
    426             this.currentLayer, handler, { 
    427                 title: title, 
    428                 featureAdded: OpenLayers.Function.bind(function(feature) { 
    429                     feature.state = OpenLayers.State.INSERT; 
    430                     // because "featuresadded" has already been triggered in 
    431                     // Layer/Vector.js, we trigger it once more 
    432                     this.currentLayer.events.triggerEvent("featuresadded", 
    433                                        {features: [feature]}); 
    434                     this.modifyControl.selectControl.select(feature); 
    435                     this.attributesEditingForm.getForm().loadRecord(this.attributesFormDefaults); 
    436                     this.modifyControl.activate(); 
    437                 }, this) 
    438         }); 
    439  
    440         // The following works around a bug in OpenLayers. When switching from the modify 
    441         // feature control to the drawing feature control, the modify feature control 
    442         // resets the vector layer's z-index to its value based on its index in the 
    443         // layers array, by registering to the drawing feature control's activate 
    444         // and deactivate events we set the vector layer's z-index appropriately. 
    445         // FIXME this workaround must be removed once this is fixed in OpenLayers 
    446         function moveLayerToTop(evt) { 
    447             if (this.currentLayer && (!evt || !evt.property || evt.property == "order")) { 
    448                 var index = Math.max(this.currentLayer.map.Z_INDEX_BASE['Feature'] - 1, 
    449                     this.currentLayer.getZIndex()) + 1; 
    450                 this.currentLayer.setZIndex(index); 
    451             } 
    452         } 
    453         this.drawFeatureControl.events.on({ 
    454             activate: moveLayerToTop, 
    455             deactivate: function() { 
    456                 if (this.currentLayer) { 
    457                     var index = this.currentLayer.getZIndex() - 1; 
    458                     if (index >= this.currentLayer.map.Z_INDEX_BASE['Feature']) { 
    459                         this.currentLayer.setZIndex(index); 
    460                     } else { 
    461                         this.currentLayer.map.setLayerZIndex(this.currentLayer, 
    462                             this.currentLayer.map.getLayerIndex(this.currentLayer)); 
    463                     } 
    464                 } 
    465             }, 
    466             scope: this 
    467         }); 
    468         this.map.events.on({ 
    469             "changelayer": moveLayerToTop, 
    470             "removelayer": moveLayerToTop, 
    471             "scope": this 
    472         }); 
    473  
    474         this.getTopToolbar().addControl(this.modifyControl, { 
    475             iconCls: 'modifyfeature', 
    476             toggleGroup: 'map' 
    477         }); 
    478         this.getTopToolbar().addControl(this.drawFeatureControl, { 
    479             iconCls: iconCls, 
    480             toggleGroup: 'map' 
    481         }); 
    482         this.modifyControl.activate(); 
    483  
     538        return styleMap; 
     539    }, 
     540 
     541    /** 
     542     * Method: createStrategy 
     543     * Create a BBOX strategy for the vector layer. 
     544     * 
     545     * Returns: 
     546     * {<OpenLayers.Strategy.BBOX>} 
     547     */ 
     548    createStrategy: function() { 
     549        return new OpenLayers.Strategy.BBOX({ 
     550            autoActivate: false 
     551        }); 
     552    }, 
     553 
     554    /** 
     555     * Method: onFeatureselected 
     556     * 
     557     * Parameters: 
     558     * obj - {Object} Object with a feature property 
     559     */ 
     560    onFeatureselected: function(obj) { 
     561        var f = obj.feature; 
     562        this.deleteBtn.enable(); 
     563        this.selectInGrid(f); 
     564        this.editAttributes(f); 
     565    }, 
     566 
     567    /** 
     568     * Method: onFeatureunselected 
     569     * 
     570     * Parameters: 
     571     * obj - {Object} Object with a feature property 
     572     */ 
     573    onFeatureunselected: function(obj) { 
     574        this.deleteBtn.disable(); 
     575        this.unselectInGrid(); 
     576        this.form.getForm().reset(); 
     577        this.form.setDisabled(true); 
     578    }, 
     579 
     580    /** 
     581     * Method: createStore 
     582     * Create the store containing the edited features. 
     583     */ 
     584    createStore: function(config) { 
     585        this.destroyStore(); 
    484586        var fields = []; 
    485         var properties = this.layerConfig[layer].featuretypes.properties; 
     587        var properties = config.featuretypes.properties; 
    486588        for (var i = 0; i < properties.length; i++) { 
    487589            fields.push(properties[i].getRecordType()); 
    488590        } 
    489  
    490         this.store = new Ext.data.GroupingStore({ 
     591        var store = new Ext.data.GroupingStore({ 
    491592            reader: new mapfish.widgets.data.FeatureReader( 
    492593                {}, fields 
     
    494595            groupField: "state" 
    495596        }); 
    496  
    497         var layerStoreMediator = new mapfish.widgets.data.LayerStoreMediator({ 
     597        store.on("add", this.updateCommitBtnState, this); 
     598        store.on("remove", this.updateCommitBtnState, this); 
     599        store.on("clear", this.updateCommitBtnState, this); 
     600        store.on("load", this.updateGridSelection, this); 
     601        this.store = store; 
     602    }, 
     603 
     604    /** 
     605     * Method: destroyStore 
     606     * Destroy the feature store. 
     607     */ 
     608    destroyStore: function() { 
     609        var store = this.store; 
     610        if (store) { 
     611            // for unknown reason this method is in Ext's API 
     612            // doc, use it anyway as it's the safest method to 
     613            // unregister listeners registered in the store 
     614            store.destroy(); 
     615            this.store = null; 
     616        } 
     617    }, 
     618 
     619    /** 
     620     * Method: updateCommitBtnState 
     621     * Enable or disable the commit button based on whether there 
     622     * are records or store in the store. 
     623     */ 
     624    updateCommitBtnState: function(store) { 
     625        this.commitBtn.setDisabled(!(store.getCount() > 0)); 
     626    }, 
     627 
     628    /** 
     629     * Method: updateGridSelection 
     630     * Make the selection in the grid reflect what's selected in 
     631     * the layer. 
     632     */ 
     633    updateGridSelection: function(store, records, options) { 
     634        for (var i = 0; i < records.length; i++) { 
     635            var feature = records[i].data.feature; 
     636            if (OpenLayers.Util.indexOf( 
     637                    this.layer.selectedFeatures, feature) >= 0) { 
     638                this.selectInGrid(feature); 
     639            } 
     640        } 
     641    }, 
     642 
     643    /** 
     644     * Method: createModifyFeatureControl 
     645     * Create a modify feature control. 
     646     */ 
     647    createModifyFeatureControl: function() { 
     648        this.destroyModifyFeatureControl(); 
     649        var ctrl = new OpenLayers.Control.ModifyFeature(this.layer, { 
     650            mode: OpenLayers.Control.ModifyFeature.RESHAPE | 
     651                  OpenLayers.Control.ModifyFeature.DRAG, 
     652            title: OpenLayers.i18n("mf.editing.selectModifyFeature") 
     653        }); 
     654        this.getTopToolbar().addControl(ctrl, { 
     655            iconCls: 'modifyfeature', 
     656            toggleGroup: 'map' 
     657        }); 
     658        ctrl.activate(); 
     659        this.modifyFeatureControl = ctrl; 
     660    }, 
     661 
     662    /** 
     663     * Method: destroyModifyFeatureControl 
     664     * Destroy the modify feature control. 
     665     */ 
     666    destroyModifyF