1 /**
  2  * @fileOverview  
  3  * Copyright (c) 2013 Regione Autonoma della Sardegna
  4  * Published under the GPL license.<br>
  5  * See <a href="https://sardegnageoportale.it/webgis/license.txt">https://sardegnageoportale.it/webgis/license.txt</a>
  6  * for the full text of the license.
  7  * @version 0.1
  8  */
  9 
 10 /**
 11  * @namespace framework.widgets.search
 12  */
 13 Ext.namespace("framework.widgets.search");
 14 
 15 /**
 16  * A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers.
 17  * @name_ FeatureSearchGridPanel
 18  * @class A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers.
 19  * @constructor
 20  @extends <a target="_blank" href="http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.grid.GridPanel">Ext.grid.GridPanel,</a>
 21  */
 22 framework.widgets.search.FeatureSearchGridPanel = Ext.extend(Ext.grid.GridPanel,
 23         /** 
 24          * @lends framework.widgets.search.FeatureSearchGridPanel.prototype 
 25          */
 26                 {
 27                     /** api: config[downloadable]
 28                      * ``Boolean``
 29                      *  Should the features in the grid be downloadble?
 30                      *  Download can be effected in 3 ways:
 31                      *  1. via Grid export (CSV and XLS only)
 32                      *  2. downloading the original feature format (GML2)
 33                      *  3. (GeoServer only) requesting the server for a triggered download (all Geoserver WFS formats),
 34                      * @public
 35                      * @type Boolean
 36                      */
 37                     downloadable: true,
 38                     /** api: config[exportFormats]
 39                      *
 40                      * Array of document formats to be used when exporting the content of a GFI response. This requires the server-side CGI script
 41                      * ``heron.cgi`` to be installed. Exporting results in a download of a document with the contents of the (Grid) Panel.
 42                      * For example when 'XLS' is configured, exporting will result in the Excel (or compatible) program to be
 43                      * started with the GFI data in an Excel worksheet.
 44                      * Option values are 'CSV' and/or 'XLS', default is, ``null``, meaning no export (results in no export menu).
 45                      * The value ['CSV', 'XLS'] configures a menu to choose from a ``.csv`` or ``.xls`` export document format.
 46                      * @public
 47                      * @type [object] String Array 
 48                      * */
 49                     exportFormats: ['CSV', 'XLS', 'GMLv2', 'GeoJSON', 'WellKnownText'],
 50                     /** api: config[columnCapitalize]
 51                      * 
 52                      *  Should the column names be capitalized when autoconfig is true?
 53                      *  @public
 54                      *  @type Boolean 
 55                      */
 56                     columnCapitalize: true,
 57                     /** api: config[showTopToolbar]
 58                      * 
 59                      *  Should a top toolbar with feature count, clear button and download combo be shown? Default ``true``.
 60                      *  @public
 61                      *  @type Boolean 
 62                      */
 63                     showTopToolbar: true,
 64                     /** api: config[showGeometries]
 65                      *  ``Boolean``
 66                      *  Should the feature geometries be shown? Default ``true``.
 67                      */
 68                     showGeometries: true,
 69                     /** api: config[featureSelection]
 70                      *  
 71                      *  Should the feature geometries that are shown be selectable in grid and map? Default ``true``.
 72                      *  @public
 73                      *  @type Boolean 
 74                      */
 75                     featureSelection: true,
 76                     /** api: config[loadMask]
 77                      *  
 78                      *  @public
 79                      *  @type Boolean 
 80                      */
 81                     loadMask: true,
 82                     /** api: config[bbar]
 83                      *  
 84                      *  @public
 85                      *  @type Object Ext.PagingToolbar
 86                      */
 87                     /* bbar: new Ext.PagingToolbar({
 88                      pageSize: 25,
 89                      store: store,
 90                      displayInfo: true,
 91                      displayMsg: 'Displaying objects {0} - {1} of {2}',
 92                      emptyMsg: "No objects to display"
 93                      }),*/
 94 
 95                     /** api: config[exportConfigs]
 96                      *  
 97                      *  The supported configs for formatting and exporting feature data. Actual presented download options
 98                      *  are configured with exportFormats.
 99                      *  @public
100                      *  @type Object 
101                      */
102                     exportConfigs: {
103                         CSV: {
104                             formatter: 'CSVFormatter',
105                             fileExt: '.csv',
106                             mimeType: 'text/csv'
107                         },
108                         XLS: {
109                             formatter: 'ExcelFormatter',
110                             fileExt: '.xls',
111                             mimeType: 'application/vnd.ms-excel'
112                         },
113                         GMLv2: {
114                             formatter: 'OpenLayersFormatter',
115                             format: new OpenLayers.Format.GML.v2({featureType: 'heronfeat', featureNS: 'http://heron-mc.org'}),
116                             fileExt: '.gml',
117                             mimeType: 'text/xml'
118                         },
119                         GeoJSON: {
120                             formatter: 'OpenLayersFormatter',
121                             format: 'OpenLayers.Format.GeoJSON',
122                             fileExt: '.json',
123                             mimeType: 'text/plain'
124                         },
125                         WellKnownText: {
126                             formatter: 'OpenLayersFormatter',
127                             format: 'OpenLayers.Format.WKT',
128                             fileExt: '.wkt',
129                             mimeType: 'text/plain'
130                         }
131                     },
132                     /** api: config[separateSelectionLayer]
133                      *  
134                      *  Should selected features be managed in separate overlay Layer (handy for printing) ?.
135                      *  @public
136                      *  @type Boolean
137                      */
138                     separateSelectionLayer: false,
139                     /** api: config[zoomOnFeatureSelect]
140                      *  
141                      *  Zoom to feature (extent) when selected ?.
142                      *  @public
143                      *  @type Boolean
144                      */
145                     zoomOnFeatureSelect: false,
146                     /** api: config[zoomOnRowDoubleClick]
147                      *  
148                      *  Zoom to feature (extent) when row is double clicked ?.
149                      *  @public
150                      *  @type Boolean
151                      */
152                     zoomOnRowDoubleClick: true,
153                     /** api: config[zoomLevelPointSelect]
154                      *  
155                      *  Zoom level for point features when selected, default ``10``.
156                      *  @public
157                      *  @type Integer
158                      */
159                     zoomLevelPointSelect: 10,
160                     /** api: config[zoomLevelPoint]
161                      *  
162                      *  Zoom level when layer is single point feature, default ``10``.
163                      *  @public
164                      *  @type Integer
165                      */
166                     zoomLevelPoint: 10,
167                     /** api: config[zoomToDataExtent]
168                      *  
169                      *  Zoom to layer data extent when loaded ?.
170                      *  @public
171                      *  @type Boolean
172                      */
173                     zoomToDataExtent: false,
174                     /** api: config[autoConfig]
175                      *  
176                      *  Should the store and grid columns autoconfigure from loaded features?.
177                      *  @public
178                      *  @type Boolean
179                      */
180                     autoConfig: true,
181                     /** api: config[vectorLayerOptions]
182                      *  
183                      *  Options to be passed on Vector constructors.
184                      *  @public
185                      *  @type Object
186                      */
187                     vectorLayerOptions: {noLegend: true, displayInLayerSwitcher: false},
188                     /**
189                      * If it's true there will be added a button to perform "showAerialPhoto" event,
190                      * otherwise it will be performed using double click on selected row.
191                      * Default is true.
192                      * @public
193                      * @type boolean
194                      */
195                     openOnButton: true,                  
196                     
197                     /** api: config[cartPanel]
198                      * 
199                      *  id cart panel.
200                      *  @public
201                      *  @type String
202                      */
203                     cartPanel: null,
204                     /** api: config[basePathResources]
205                      * 
206                      *  path products resources
207                      *  @public
208                      *  @type String
209                      */
210                     basePathResources: null,
211                     /** api: config[managerProductPanel]
212                      * 
213                      *  id products panel
214                      *  @public
215                      *  @type String
216                      */
217                     managerProductPanel: null,
218                     /** api: config[urlGeoNetwork]
219                      * 
220                      *  Url goeNetwork
221                      *  @public
222                      *  @type String
223                      */
224                     urlGeoNetwork: null,           
225                     /** 
226                      *  Override init component
227                      *  @private 
228                      */      
229                     initComponent: function() {
230                         // If columns specified we don't do autoconfig (column guessing from features)
231                         if (this.columns) {
232                             this.autoConfig = false;
233                         }
234 
235                         // specific config (besides GridPanel config)
236                         Ext.apply(this, this.hropts);
237                         
238                         // If we have feature selection enabled we must show geometries
239                         if (this.featureSelection) {
240                             this.showGeometries = true;
241                         }
242 
243                         if (this.showGeometries) {
244                             // Define OL Vector Layer to display search result features
245                             var layer = this.layer = new OpenLayers.Layer.Vector(this.title, this.vectorLayerOptions);
246 
247                             if (!this.map)
248                                 this.map = this.target.mapPanel.map;
249                             this.map.addLayer(this.layer);
250                             
251                             var layerSelected = this.map.getLayersByName(this.layer.name)[0];
252                             this.metadato = layerSelected.metadato;
253                             this.tipologia = layerSelected.tipologia;
254                             this.anno = layerSelected.anno;
255                             this.note = layerSelected.note;
256                             this.hdd = layerSelected.hdd;
257 
258                             var self = this;
259 
260                             if (self.cartPanel) {
261                                 this.layer.events.on(
262                                         {featureselected: function(e) {
263                                                 //create popup
264                                                 self.currentFeature = e.feature;
265                                                 self.cleanWindow();
266                                                 window.popupAddr = new GeoExt.Popup({
267                                                     //title: 'Ricerca indirizzo',
268                                                     map: e.feature.layer.map,
269                                                     location: e.feature,
270                                                     father: self,
271                                                     width: 320,
272                                                     border: false,
273                                                     maximizable: false,
274                                                     collapsible: false,
275                                                     resizable: false,
276                                                     items: [
277                                                         {xtype: "label",
278                                                             html: '<div class="popupAddr"><b>' + e.object.name + '</b><br>' +
279                                                                     'Foto <b>' + e.feature.attributes.IMAGE + '</b>' +
280                                                                     '<br><br><a href="' + self.metadato + '" target="_blank">Scheda metadato</a>' +
281                                                                     '<br><a href="#" onClick="Ext.getCmp(\'btnFireShowPhoto\').handler.call(Ext.getCmp(\'btnFireShowPhoto\').scope);">Anteprima</a>' +
282                                                                     '<br><br>Immagine in alta risoluzione' +
283                                                                     '<br><input type="text" value="' + self.getPathImageHR(e.feature.gml.featureType,e.feature.attributes.IMAGE,self.hdd) + '" readonly size="30"><br>' +
284                                                                     '</div>'
285 
286                                                         }, {
287                                                             xtype: "button",
288                                                             id: 'btnFireShowPhoto',
289                                                             hidden: true,
290                                                             margin: {left: '10px'},
291                                                             handler: function() {
292                                                                 self.showFoto(self.currentFeature.gml.featureType, self.currentFeature.data.IMAGE);
293                                                             }
294                                                         }, {
295                                                             xtype: "button",
296                                                             disabled:e.feature.attributes.IMAGE != null?false:true,
297                                                             cls:'addToCartButton',
298                                                             text: 'AGGIUNGI AL CARRELLO',
299                                                             handler: function() {
300                                                                 var items = self.store.data.items;
301                                                                 var item;
302                                                                 for (var i = 0; i < items.length; i++) {
303                                                                     if (items[i].data.feature.id == self.currentFeature.id) {
304                                                                         if (items[i].data.onCart != true) {
305                                                                             self.store.getAt(i).set('onCart', true);
306                                                                             self.store.getAt(i).commit();
307                                                                         }
308                                                                         return;
309                                                                     }
310                                                                 }
311 
312                                                             }
313                                                         }, {
314                                                             xtype: "button",
315                                                             cls:'modifyProductButton',
316                                                             hidden: (window.btnProdotti && window.btnProdotti.isVisible()) ? false : true,
317                                                             text: 'MODIFICA PRODOTTO',
318                                                             handler: function() {
319                                                                 if (window.btnProdotti && window.btnProdotti.isVisible()) {
320                                                                     window.btnProdotti.toggle();
321                                                                     window.btnProdotti.handler.call(window.btnProdotti);
322                                                                     var productJson = {productId: self.currentFeature.gml.featureType, productName: self.title, productType: self.tipologia, productYear: self.anno, productSchedaMetadatoEsistente: true, productMetadata: self.metadato,productNote:self.note};
323                                                                     Ext.getCmp(self.managerProductPanel).fireEvent("modifyProduct", productJson);
324                                                                     self.cleanWindow();
325                                                                 } else
326                                                                     alert('Modifica non abilitata!');
327 
328                                                             }
329                                                         },{xtype: "label",
330                                                             html:'<br><br><br>'
331                                                         }
332                                                     ]
333                                                 }
334 
335                                                 );
336                                                 window.popupAddr.show();
337                                                 var winContent = self.ownerCt;
338                                                 while(winContent.ownerCt){
339                                                     winContent = winContent.ownerCt;
340                                                 }
341                                                 winContent.setPosition(0,0);
342                                             }},
343                                 {featureunselected: this.cleanWindow}
344 
345                                 )
346                             }
347 
348                             if (this.featureSelection && this.zoomOnFeatureSelect) {
349                                 // See http://www.geoext.org/pipermail/users/2011-March/002052.html
350                                 layer.events.on({
351                                     "featureselected": function(e) {
352                                         self.zoomToFeature(self, e.feature.geometry);
353                                     },
354                                     "dblclick": function(e) {
355                                         self.zoomToFeature(self, e.feature.geometry);
356                                     },
357                                     "scope": layer
358                                 });
359                             }
360 
361                             // May zoom to feature when grid row is double-clicked.
362                             if (this.zoomOnRowDoubleClick) {
363                                 this.on('celldblclick', function(grid, rowIndex, columnIndex, e) {
364                                     var record = grid.getStore().getAt(rowIndex);
365                                     var feature = record.getFeature();
366                                     self.zoomToFeature(self, feature.geometry);
367                                     if (!this.openOnButton)
368                                         //this.fireEvent('showAerialPhoto', record);
369                                         this.showFoto('');
370                                 });
371                             }
372 
373                             if (this.separateSelectionLayer) {
374                                 this.selLayer = new OpenLayers.Layer.Vector(this.title + '_Sel', {noLegend: true, displayInLayerSwitcher: false});
375                                 // selLayer.style = layer.styleMap.styles['select'].clone();
376                                 this.selLayer.styleMap.styles['default'] = layer.styleMap.styles['select'];
377                                 this.selLayer.style = this.selLayer.styleMap.styles['default'].defaultStyle;
378                                 // this.selLayer.style = layer.styleMap.styles['select'].clone();
379                                 layer.styleMap.styles['select'] = layer.styleMap.styles['default'].clone();
380                                 layer.styleMap.styles['select'].defaultStyle.fillColor = 'white';
381                                 layer.styleMap.styles['select'].defaultStyle.fillOpacity = 0.0;
382                                 this.map.addLayer(this.selLayer);
383                                 this.map.setLayerIndex(this.selLayer, this.map.layers.length - 1);
384                                 this.layer.events.on({
385                                     featureselected: this.updateSelectionLayer,
386                                     featureunselected: this.updateSelectionLayer,
387                                     scope: this
388                                 });
389                             }
390                         }
391 
392                         this.setupStore(this.features);
393 
394                         // Will take effort to support paging...
395                         // http://dev.sencha.com/deploy/ext-3.3.1/examples/grid/paging.html
396                         /* 
397                          this.bbar = new Ext.PagingToolbar({
398                          pageSize: 5,
399                          store: this.store,
400                          displayInfo: true,
401                          displayMsg: 'Displaying objects {0} - {1} of {2}',
402                          emptyMsg: "No objects to display"
403                          });
404                          */
405 
406 
407                         // Enables the interaction between features on the Map and Grid
408                         if (this.featureSelection && !this.sm) {
409                             this.sm = new GeoExt.grid.FeatureSelectionModel();
410                         }
411 
412                         if (this.showTopToolbar) {
413                             this.tbar = this.createTopToolbar();
414                         }
415                         
416                         framework.widgets.search.FeatureSearchGridPanel.superclass.initComponent.call(this);
417 
418                         // ExtJS lifecycle events
419                         this.addListener("afterrender", this.onPanelRendered, this);
420                         this.addListener("show", this.onPanelShow, this);
421                         this.addListener("hide", this.onPanelHide, this);
422                         if (this.cartPanel) {
423                             this.selModel.on("rowdeselect", this.cleanWindow)
424                         }
425                     },
426                     
427                     /** api: method[createTopToolbar]
428                      * Create the top toolbar.
429                      * @private
430                      */
431                     createTopToolbar: function() {
432 
433                         // Top toolbar text, keep var for updating
434                         var tbarItems = [this.tbarText = new Ext.Toolbar.TextItem({text: __(' ')})];
435                         tbarItems.push('->');
436 
437                         if (this.downloadable) {
438 
439                             // Multiple display types configured: add toolbar tabs
440                             // var downloadMenuItems = ['<b class="menu-title">' + __('Choose an Export Format') + '</b>'];
441                             var downloadMenuItems = [];
442                             var item;
443                             for (var j = 0; j < this.exportFormats.length; j++) {
444                                 var exportFormat = this.exportFormats[j];
445                                 item = {
446                                     text: __('as') + ' ' + exportFormat,
447                                     cls: 'x-btn',
448                                     iconCls: 'icon-table-export',
449                                     scope: this,
450                                     exportFormat: exportFormat,
451                                     handler: function(evt) {
452                                         this.exportData(evt.exportFormat);
453                                     }
454                                 };
455                                 downloadMenuItems.push(item);
456                             }
457 
458                             if (this.downloadInfo && this.downloadInfo.downloadFormats) {
459                                 var downloadFormats = this.downloadInfo.downloadFormats;
460                                 for (var k = 0; k < downloadFormats.length; k++) {
461                                     var downloadFormat = downloadFormats[k];
462                                     item = {
463                                         text: __('as') + ' ' + downloadFormat.name,
464                                         cls: 'x-btn',
465                                         iconCls: 'icon-table-export',
466                                         downloadFormat: downloadFormat.outputFormat,
467                                         fileExt: downloadFormat.fileExt,
468                                         scope: this,
469                                         handler: function(evt) {
470                                             this.downloadData(evt.downloadFormat, evt.fileExt);
471                                         }
472                                     };
473                                     downloadMenuItems.push(item);
474                                 }
475                             }
476 
477                             if (downloadMenuItems.length > 0) {
478                                 /* Add to toolbar. */
479                                 tbarItems.push({
480                                     text: __('Download'),
481                                     cls: 'x-btn-text-icon',
482                                     iconCls: 'icon-table-save',
483                                     tooltip: __('Choose a Download Format'),
484                                     menu: new Ext.menu.Menu({
485                                         style: {
486                                             overflow: 'visible'	 // For the Combo popup
487                                         },
488                                         items: downloadMenuItems
489                                     })
490                                 });
491                             }
492                         }
493 
494                         tbarItems.push('->');
495                         tbarItems.push({
496                             text: __('Clear'),
497                             cls: 'x-btn-text-icon',
498                             iconCls: 'icon-table-clear',
499                             tooltip: __('Remove all results'),
500                             scope: this,
501                             handler: function() {
502                                 this.removeFeatures();
503                             }
504                         });
505 
506                         return new Ext.Toolbar({enableOverflow: true, items: tbarItems});
507                     },
508                     /** api: method[loadFeatures]
509                      * Loads array of feature objects in store and shows them on grid and map.
510                      * @private
511                      */
512                     loadFeatures: function(features, featureType) {
513                         this.removeFeatures();
514                         this.featureType = featureType;
515 
516                         // Defensive programming
517                         if (!features || features.length == 0) {
518                             return;
519                         }
520 
521                         this.showLayer();
522                         this.removeFeaturesDuplicates();
523                         this.store.loadData(features);
524                         this.updateTbarText();
525 
526                         // Whenever Paging is supported...
527                         // http://dev.sencha.com/deploy/ext-3.3.1/examples/grid/paging.html
528                         // this.store.load({params:{start:0, limit:25}});
529 
530                         if (this.zoomToDataExtent) {
531                             if (features.length == 1 && features[0].geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
532                                 var point = features[0].geometry.getCentroid();
533                                 this.map.setCenter(new OpenLayers.LonLat(point.x, point.y), this.zoomLevelPoint);
534                             } else if (this.layer) {
535                                 if (this.map.layersWithFeature)
536                                     this.map.layersWithFeature.push(this.layer);
537                                 var extent = this.layer.getDataExtent();
538                                 if (this.father.searchExtent) {
539                                     this.father.searchExtent.extend(this.layer.getDataExtent());
540                                 } else {
541                                     this.father.searchExtent = this.layer.getDataExtent();
542 
543                                 }
544                                 this.map.zoomToExtent(this.father.searchExtent);
545                             }
546                         }
547                     },
548                     /** api: method[hasFeatures]
549                      * Does this Panel have features?.
550                      * @private
551                      */
552                     removeFeaturesDuplicates: function(){
553                         var map = {};
554                         var array = [];
555                         for(var i=0;i<this.features.length;i++){
556                             if(!map[this.features[i].attributes.IMAGE]){
557                                 map[this.features[i].attributes.IMAGE] = 'ok';
558                                 array.push(this.features[i]);
559                             }
560                         }
561                         this.features = array;
562                     },
563                     /** api: method[hasFeatures]
564                      * Does this Panel have features?.
565                      * @private
566                      */
567                     hasFeatures: function() {
568                         return this.store && this.store.getCount() > 0;
569                     },
570                     /** api: method[removeFeatures]
571                      * Removes all feature objects from store .
572                      * @private
573                      */
574                     removeFeatures: function() {
575                         if (this.store) {
576                             this.store.removeAll(false);
577                         }
578                         if (this.selLayer) {
579                             this.selLayer.removeAllFeatures({silent: true});
580                         }
581                         this.updateTbarText();
582                     },
583                     /** api: method[showLayer]
584                      * Show the layer with features on the map.
585                      * @private
586                      */
587                     showLayer: function() {
588                         // this.removeFeatures();
589                         if (this.layer) {
590                             if (this.selLayer) {
591                                 this.map.setLayerIndex(this.selLayer, this.map.layers.length - 1);
592                                 this.map.setLayerIndex(this.layer, this.map.layers.length - 2);
593                             } else {
594                                 this.map.setLayerIndex(this.layer, this.map.layers.length - 1);
595                             }
596                             if (!this.layer.getVisibility()) {
597                                 this.layer.setVisibility(true);
598                             }
599                             if (this.selLayer && !this.selLayer.getVisibility()) {
600                                 this.selLayer.setVisibility(true);
601                             }
602                         }
603                     },
604                     /** api: method[hideLayer]
605                      * Hide the layer with features on the map.
606                      * @private
607                      */
608                     hideLayer: function() {
609                         // this.removeFeatures();
610                         if (this.layer && this.layer.getVisibility()) {
611                             this.layer.setVisibility(false);
612                         }
613                         if (this.selLayer && this.selLayer.getVisibility()) {
614                             this.selLayer.setVisibility(false);
615                         }
616                         this.cleanWindow();
617 
618                     },
619                     /** api: method[hideLayer]
620                      * Hide the layer with features on the map.
621                      * @private
622                      */
623                     zoomToFeature: function(self, geometry) {
624                         if (!geometry) {
625                             return;
626                         }
627 
628                         // For point features center map otherwise zoom to geometry bounds
629                         if (geometry.getVertices().length == 1) {
630                             var point = geometry.getCentroid();
631                             self.map.setCenter(new OpenLayers.LonLat(point.x, point.y), self.zoomLevelPointSelect);
632                         } else {
633                             self.map.zoomToExtent(geometry.getBounds());
634                         }
635                     },
636                     /** api: method[zoomButtonRenderer]
637                      * 
638                      * @private
639                      */
640                     zoomButtonRenderer: function() {
641                         var id = Ext.id();
642 
643                         (function() {
644                             new Ext.Button({
645                                 renderTo: id,
646                                 text: 'Zoom'
647                             });
648 
649                         }).defer(25);
650 
651                         return (String.format('<div id="{0}"></div>', id));
652                     },
653                     /** private: method[setupStore]
654                      *  :param features: ``Array`` optional features.
655                      *  @private
656                      */
657                     setupStore: function(features) {
658                         if (this.store && !this.autoConfig) {
659                             return;
660                         }
661 
662                         // Prepare fields array for store from columns in Grid config.
663                         var storeFields = [];
664                         if (this.autoConfig && features) {
665                             this.columns = [];
666                             if (features.length > 0) {
667                                 var feature = features[0];
668                                 var fieldName;
669                                 for (fieldName in feature.attributes) {
670                                     var column;
671                                     // Capitalize header names
672                                     if (fieldName != 'onCart') {
673                                         column = {
674                                             header: this.columnCapitalize ? fieldName.substr(0, 1).toUpperCase() + fieldName.substr(1).toLowerCase() : fieldName,
675                                             width: 100,
676                                             dataIndex: fieldName,
677                                             sortable: true
678                                         };
679 
680                                         // Look for custom rendering
681                                         if (this.gridCellRenderers && this.featureType) {
682                                             var gridCellRenderer;
683                                             for (var k = 0; k < this.gridCellRenderers.length; k++) {
684                                                 gridCellRenderer = this.gridCellRenderers[k];
685                                                 if (gridCellRenderer.attrName && fieldName == gridCellRenderer.attrName) {
686                                                     if (gridCellRenderer.featureType && this.featureType == gridCellRenderer.featureType || !gridCellRenderer.featureType) {
687                                                         column.options = gridCellRenderer.renderer.options;
688                                                         column.renderer = gridCellRenderer.renderer.fn;
689                                                     }
690                                                 }
691                                             }
692                                         }
693                                         this.columns.push(column);
694                                         storeFields.push({name: column.dataIndex});
695                                     }
696                                 } //end of attributes scanning
697                                 //if requested, we'll add a button
698                                 if (this.openOnButton) {
699                                     var column = {
700                                         xtype: 'actioncolumn',
701                                         width: 32,
702                                         sortable: false,
703                                         items: [{
704                                                 icon: 'theme/app/img/camera16x16.png',
705                                                 tooltip: 'Apri foto aerea',
706                                                 handler: function(grid, rowIndex, colIndex) {
707                                                     var img = grid.getStore().getAt(rowIndex).data.IMAGE;
708                                                     this.showFoto(grid.featureType, img);
709                                                 },
710                                                 scope: this
711                                             }]
712                                     };
713                                     this.columns.push(column);
714                                     storeFields.push({name: 'button'});
715                                 }
716                                 //if requested, we'll add a checkBox
717                                 if (feature.attributes.onCart != undefined) {
718                                     var column = {
719                                         xtype: 'checkcolumn',
720                                         header: 'Carrello',
721                                         dataIndex: 'onCart',
722                                         father: this
723                                     };
724                                     this.columns.push(column);
725                                     storeFields.push({name: 'onCart'});
726                                 }
727                             }
728                         } else {
729                             Ext.each(this.columns, function(column) {
730                                 if (column.dataIndex) {
731                                     storeFields.push({name: column.dataIndex, type: column.type});
732                                 }
733                                 column.sortable = true;
734                             });
735                         }
736 
737                         // this.columns.push({ header: 'Zoom', width: 60, sortable: false, renderer: self.zoomButtonRenderer });
738 
739                         // Define the Store
740                         var storeConfig = {layer: this.layer, fields: storeFields};
741 
742                         // Optional extra store options in config
743                         Ext.apply(storeConfig, this.hropts.storeOpts);
744 
745                         this.store = new GeoExt.data.FeatureStore(storeConfig);
746                         this.store.father = this;
747                         this.store.layerId= feature.gml.featureType;
748                         this.store.on({
749                             update: function(obj, record, operation) {
750                                 var grid = this.father;
751                                 if (!this.father.cart) {
752                                     this.father.cart = Ext.getCmp(this.father.cartPanel);
753                                 }
754                                 if (record.data.onCart == true) {
755                                     var id = grid.featureType + '_' + record.data.IMAGE;
756                                     this.father.cart.fireEvent('addOnCart', id, this.father.title, grid.featureType, record.data.FOTOGRAMMA, record.data.STRISCIATA, grid.featureType, record.data.IMAGE);
757                                 } else {
758                                     var id = grid.featureType + '_' + record.data.IMAGE;
759                                     this.father.cart.fireEvent('removeFromCart', id);
760                                 }
761                             },
762                             rowdeselect: function() {
763                             }
764 
765                         });
766                     },
767                     /** private: method[updateSelectionLayer]
768                      *  :param evt: ``Object`` An object with a feature property referencing
769                      *                         the selected or unselected feature.
770                      *  @private
771                      */
772                     updateSelectionLayer: function(evt) {
773                         if (!this.showGeometries) {
774                             return;
775                         }
776                         this.selLayer.removeAllFeatures({silent: true});
777                         var features = this.layer.selectedFeatures;
778                         for (var i = 0; i < features.length; i++) {
779                             var feature = features[i].clone();
780                             this.selLayer.addFeatures(feature);
781                         }
782                     },
783                     /** api: method[onPanelRendered]
784                      *  Called when Panel has been rendered.
785                      *  @private
786                      */
787                     onPanelRendered: function() {
788                         if (this.ownerCt) {
789                             this.ownerCt.addListener("parenthide", this.onParentHide, this);
790                             this.ownerCt.addListener("parentshow", this.onParentShow, this);
791                         }
792                     },
793                     /** private: method[onPanelShow]
794                      * Called after our panel is shown.
795                      * @private
796                      */
797                     onPanelShow: function() {
798                         if (this.selModel && this.selModel.selectControl) {
799                             this.selModel.selectControl.activate();
800                         }
801                     },
802                     /** private: method[onPanelHide]
803                      * Called  before our panel is hidden.
804                      * @private
805                      */
806                     onPanelHide: function() {
807                         if (this.selModel && this.selModel.selectControl) {
808                             this.selModel.selectControl.deactivate();
809                         }
810                     },
811                     /** private: method[onParentShow]
812                      * Called usually before our panel is created.
813                      * @private
814                      */
815                     onParentShow: function() {
816                         this.showLayer();
817                     },
818                     /** private: method[onParentHide]
819                      * Cleanup usually before our panel is hidden.
820                      * @private
821                      */
822                     onParentHide: function() {
823                         this.removeFeatures();
824                         this.hideLayer();
825                         this.cleanWindow();
826                     },
827                     /** private: method[cleanup]
828                      * Cleanup usually before our panel is destroyed.
829                      * @private
830                      */
831                     cleanup: function() {
832                         this.removeFeatures();
833                         if (this.selModel && this.selModel.selectControl) {
834                             this.selModel.selectControl.deactivate();
835                             this.selModel = null;
836                         }
837 
838                         if (this.layer) {
839                             this.map.removeLayer(this.layer);
840                         }
841 
842                         if (this.selLayer) {
843                             this.map.removeLayer(this.selLayer);
844                         }
845                         return true;
846                     },
847                     /** private: method[updateTbarText]
848                      * Update text message in top toolbar.
849                      * @private
850                      */
851                     updateTbarText: function() {
852                         if (!this.tbarText) {
853                             return;
854                         }
855                         var objCount = this.store ? this.store.getCount() : 0;
856                         this.tbarText.setText(objCount + ' ' + (objCount != 1 ? __('Results') : __('Result')));
857                     },
858                     /** private: method[exportData]
859                      * Callback handler function for exporting and downloading the data to specified format.
860                      * @private
861                      */
862                     exportData: function(exportFormat) {
863 
864                         var store = this.store;
865 
866                         // Get config from preconfigured configs
867                         var config = this.exportConfigs[exportFormat];
868                         if (!config) {
869                             Ext.Msg.alert(__('Warning'), __('Invalid export format configured: ' + exportFormat));
870                             return;
871                         }
872 
873                         // Create the filename for download
874                         var featureType = this.featureType ? this.featureType : 'heron';
875                         config.fileName = featureType + config.fileExt;
876 
877                         // Use only the columns from the original data, not the internal feature store columns
878                         // 'fid', 'state' and the feature object itthis, see issue 181. These are the first 3 fields in
879                         // a GeoExt FeatureStore.
880                         config.columns = (store.fields && store.fields.items && store.fields.items.length > 3) ? store.fields.items.slice(3) : null;
881 
882                         // Format the feature or grid data to chosen format and force user-download
883                         var data = framework.data.DataExporter.formatStore(store, config, true);
884                         framework.data.DataExporter.download(data, config);
885                     },
886                     /** private: method[downloadData]
887                      * Callback handler function for direct downloading the data in specified format.
888                      * @private
889                      */
890                     downloadData: function(downloadFormat, fileExt) {
891 
892                         var downloadInfo = this.downloadInfo;
893                         downloadInfo.params.outputFormat = downloadFormat;
894                         downloadInfo.params.filename = downloadInfo.params.typename + fileExt;
895 
896                         var paramStr = OpenLayers.Util.getParameterString(downloadInfo.params);
897 
898                         var url = OpenLayers.Util.urlAppend(downloadInfo.url, paramStr);
899                         if (url.length > 2048) {
900                             Ext.Msg.alert(__('Warning'), __('Download URL string too long (max 2048 chars): ') + url.length);
901                             return;
902                         }
903 
904                         // Force user-download
905                         framework.data.DataExporter.directDownload(url);
906                     },
907                     /** public: method[showFoto]
908                      * show a photo in a new window
909                     * @public
910                     * @param {String} url photo
911                     */ 
912                     showFoto: function(productName,photoName) {
913                         if (!window.winFoto) {
914                             window.winFoto = new Ext.Window({
915                                 constrainHeader: true,
916                                 layout: 'fit',
917                                 closeAction: 'hide',
918                                 html: '<img src="resources/' + productName + '/low/' + photoName + '">', //
919                                 width: 315,
920                                 height: 333,
921                                 title: this.title + " - " + photoName,
922                                 listeners: {
923                                     close: function() {
924                                         window.winFoto.destroy();
925                                     }, scope: this
926                                 }
927                             }).show();
928                         } else {
929                             window.winFoto.setTitle(this.title + " - " + photoName);
930                             window.winFoto.update('<img src="resources/' + productName + '/low/' + photoName + '">');
931                             window.winFoto.setVisible(true);
932                         }
933                     },
934                     /** public: method[cleanWindow]
935                      * clean screen from windows photo and window feature info
936                     * @public
937                     * @param {String} url photo
938                     */ 
939                     cleanWindow: function(){
940                         if (window.popupAddr) {
941                             window.popupAddr.close();
942                             window.popupAddr.destroy();
943                             delete window.popupAddr;
944                         }
945                         if (window.winFoto) {
946                             window.winFoto.close();
947                             window.winFoto.destroy();
948                             delete window.winFoto;
949                         }
950                     },
951                     
952                     getPathImageHR: function(product,image,hdd){
953                         var sep = '/';
954                         var path = this.basePathResources[hdd];
955                         if(path.indexOf('/') == -1){
956                             sep = '\\';
957                             path = sep + sep + path;
958                         }
959                         return path + sep + product + sep + 'HR' + sep + image; 
960                     }
961 
962                 });
963 
964         /** api: xtype = framework_featuresearchgridpanel */
965         Ext.reg('framework_featuresearchgridpanel', framework.widgets.search.FeatureSearchGridPanel);
966