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 /** api: (define)
 16  *  module = framework.widgets.search
 17  *  class = FeatureInfoPanel
 18  *  base_link = `Ext.Panel <http://dev.sencha.com/deploy/ext-3.3.1/docs/?class=Ext.Panel>`_
 19  */
 20 
 21 /** api: example
 22  *  Sample code showing how to configure a FeatureInfoPanel.
 23  *  All regular ExtJS `Ext.Panel <http://dev.sencha.com/deploy/ext-3.3.1/docs/?class=Ext.Panel>`_
 24  *  config params also apply.
 25  *  The ``infoFormat`` config parameter is the default ``INFO_FORMAT`` to be used for WMS GetFeatureInfo (GFI).
 26  *  This value can be overruled by an optional per-Layer ``infoFormat`` WMS Layer config parameter.
 27  *  GetFeatureInfo-response data may be displayed as a Grid, a Tree or formatted XML. The ``displayPanels``
 28  *  config option can be used to trigger a menu with display options. Note also the use of "GridCellRenderers".
 29  *  These allow you to render specific formatting of cell content within the feature grid. For example
 30  *  URL substitution to render external links in a new tab or browser window. You can even supply your own formatting
 31  *  function. This function is according to the ExtJS ColumnModel renderers (see e.g. http://snipplr.com/view/40942).
 32  *
 33  *  .. code-block:: javascript
 34  *
 35  *			 xtype: 'framework_featureinfopanel',
 36  *			 id: 'framework-feature-info',
 37  *			 region: "south",
 38  *			 border: true,
 39  *			 collapsible: true,
 40  *			 collapsed: true,
 41  *			 height: 205,
 42  *			 split: true,
 43  *			 infoFormat: 'application/vnd.ogc.gml',
 44  *			 displayPanels: ['Grid', 'XML'],
 45  *			 exportFormats: ['CSV', 'XLS'],
 46  *			 maxFeatures: 10,
 47  *			 discardStylesForDups: true,
 48  *			 gridCellRenderers: [
 49  *						{
 50  *							featureType: 'cities',
 51  *							attrName: 'City',
 52  *							renderer: {
 53  *								fn : framework.widgets.GridCellRenderer.directLink,
 54  *								options : {
 55  *									url: 'http://en.wikipedia.org/wiki/{City}',
 56  *									target: '_new'
 57  *								}
 58  *							}
 59  *						},
 60  *						{
 61  *							featureType: 'cities',
 62  *							attrName : 'Country',
 63  *							renderer :  {
 64  *								fn : framework.widgets.GridCellRenderer.browserPopupLink,
 65  *								options : {
 66  *									url: 'http://en.wikipedia.org/wiki/{Country}',
 67  *									winName: 'demoWin',
 68  *									width: 400,
 69  *									height: 800,
 70  *									scrollbars: 'yes'
 71  *								}
 72  *							}
 73  *						},
 74  *						{   // Example for custom HTML, could use also with e.g. links
 75  *							featureType: 'cities',
 76  *							attrName : 'longitude',
 77  *							renderer :  {
 78  *								fn : framework.widgets.GridCellRenderer.valueSubstitutor,
 79  *								options : {
 80  *									template: '<i>ll={latitude},{longitude}{empty}</i>'
 81  *								}
 82  *							}
 83  *						},
 84  *						{
 85  *							// Example: supply your own function, parms as in ExtJS ColumnModel
 86  *							featureType: 'cities',
 87  *							attrName : 'population',
 88  *							renderer :  {
 89  *								fn : function(value, metaData, record, rowIndex, colIndex, store) {
 90  *									// Custom formatting, may also use this.options if needed
 91  *									return '<b>' + value + ' inh.</b>';
 92  *								},
 93  *								options : {
 94  *
 95  *								}
 96  *							}
 97  *						}
 98  *
 99  *		 }
100  *
101  */
102 
103 
104 /**
105  * A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers.
106  * @name_ FeatureInfoPanel
107  * @class A Panel designed to hold WMS GetFeatureInfo (GFI) data for one or more WMS layers.
108  * @constructor
109  @extends <a target="_blank" href="http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.form.ComboBox">Ext.form.ComboBox</a>
110  */
111 framework.widgets.search.FeatureInfoPanel = Ext.extend(Ext.Panel,
112         /** 
113          * @lends framework.widgets.search.FeatureInfoPanel.prototype 
114          */
115                 {
116                     /** api: title
117                      *  
118                      *  Default title of the panel. If not set
119                      *  the value ``Feature Info`` will be used.
120                      * @public
121                      * @type String
122                      */
123                     title: 'Feature Info',
124                     /** api: config[maxFeatures]
125                      *  
126                      *  Default GFI MAX_FEATURES parameter. Will be ``5`` if not set.
127                      *  @public
128                      *  @type int
129                      */
130                     maxFeatures: 5,
131                     /** api: config[displayPanels]
132                      * 
133                      * String array  of types of Panels to display GFI info in, default value is ['Grid'],  a grid table. Other values are 'XML' and 'Tree'.
134                      * If multiple display values are given a menu will be shown to switch display types.
135                      * @public
136                      * @type String Array
137                      */
138                     displayPanels: ['Grid'],
139                     /** api: config[exportFormats]
140                      *
141                      * Array of document formats to be used when exporting the content of a GFI response. This requires the server-side CGI script
142                      * ``framework.cgi`` to be installed. Exporting results in a download of a document with the contents of the (Grid) Panel.
143                      * For example when 'XLS' is configured, exporting will result in the Excel (or compatible) program to be
144                      * started with the GFI data in an Excel worksheet.
145                      * Option values are 'CSV' and/or 'XLS', , 'GMLv2', 'GeoJSON', 'WellKnownText' default is, ``null``, meaning no export (results in no export menu).
146                      * The value ['CSV', 'XLS'] configures a menu to choose from a ``.csv`` or ``.xls`` export document format.
147                      * @public
148                      * @type String Array
149                      */
150                     exportFormats: ['CSV', 'XLS', 'GMLv2', 'GeoJSON', 'WellKnownText'],
151                     /** api: config[infoFormat]
152                      *  
153                      *  Default GFI INFO_FORMAT parameter, may be overruled per Layer object infoFormat WMS param. If not set
154                      *  the value ``application/vnd.ogc.gml`` will be used.
155                      *  @public
156                      *  @type String
157                      */
158                     infoFormat: 'application/vnd.ogc.gml',
159                     /** api: config[hover]
160                      *  
161                      *  Show features on hovering.
162                      *  @public
163                      *  @type Boolean
164                      */
165                     hover: false,
166                     /** api: config[drillDown]
167                      *  
168                      *  Show features from all visible layers that are queryable.
169                      *  @public
170                      *  @type Boolean
171                      */
172                     drillDown: true,
173                     /** api: config[layer]
174                      *  
175                      *  The layer to get feature information from. Parameter value will be ``""`` if not set.
176                      *  If not set, all visible layers of the map will be searched. In case the drillDown
177                      *  parameter is ``false``, the topmost visible layer will searched.
178                      *  @public
179                      *  @type String
180                      */
181                     layer: "",
182                     /** api: config[discardStylesForDups]
183                      *  
184                      *  In case the same Layer is present multiple times, request only once without any STYLES= parameter.
185                      *  Default is ``false``.
186                      *  @public
187                      *  @type Boolean
188                      */
189                     discardStylesForDups: false,
190                     /** api: config[showTopToolbar]
191                      *  
192                      *  Show the toolbar with object count, clear and export buttons.
193                      *  Default is ``false``.
194                      *  @public
195                      *  @type Boolean
196                      */
197                     showTopToolbar: true,
198                     /** api: config[showGeometries]
199                      *  
200                      *  Should the feature geometries be shown? Default ``true``.
201                      *  @public
202                      *  @type Boolean
203                      */
204                     showGeometries: true,
205                     /** api: config[featureSelection]
206                      *  
207                      *  Should the feature geometries that are shown be selectable in grid and map? Default ``true``.
208                      *  @public
209                      *  @type Boolean
210                      */
211                     featureSelection: true,
212                     /** Internal vars */
213                     /** api: config[pop]
214                      *  
215                      *  @public
216                      *  @type 
217                      */
218                     pop: null,
219                     /** Internal vars */
220                     /** api: config[map]
221                      *  
222                      *  A configured map or a configuration object
223                      *  @public
224                      *  @type OpenLayers.Map or Object
225                      */
226                     map: null,
227                     /** api: config[displayPanel]
228                      *  
229                      *  A configured map or a configuration object
230                      *  @public
231                      *  @type Boolean
232                      *  
233                      */
234                     displayPanel: null,
235                     /** api: config[lastEvt]
236                      *  
237                      *  A configured map or a configuration object
238                      *  @public
239                      *  @type Object
240                      *  
241                      */
242                     lastEvt: null,
243                     /** api: config[olControl]
244                      *  
245                      *  A configured map or a configuration object
246                      *  @public
247                      *  @type Object Openlayers.Control
248                      *  
249                      */
250                     olControl: null,
251                     /** api: config[tb]
252                      *  
253                      *  A configured map or a configuration object
254                      *  @public
255                      *  @type Object
256                      *  
257                      */
258                     tb: null,
259                     /** api: config[zoomToDataExtent]
260                      *  
261                      *  Zoom to layer data extent when loaded ?.
262                      *  @public
263                      *  @type Boolean
264                      */
265                     zoomToDataExtent: true,
266                     /** api: config[hiddenLayers]
267                      *  
268                      *  show feature for hidden layers.
269                      *  @public
270                      *  @type Boolean
271                      */
272                     hiddenLayers: false,
273                     mapVoli: null,
274                     panelsContainer: null,
275                     /**
276                      * Image icon button.
277                      * @public
278                      * @type String
279                      */
280                     iconButton: 'theme/app/img/camera16x16.png',
281                     /** Init the component creating a store and object'events.
282                      * @private
283                      */
284                     initComponent: function() {
285 
286                         // For closures ("this" is not valid in callbacks)
287                         var self = this;
288                         Ext.apply(this, {
289                             layout: "fit"
290                         });
291                         this.display = this.displayGrid;
292                         framework.widgets.search.FeatureInfoPanel.superclass.initComponent.call(this);
293                         if (!this.map)
294                             this.map = this.target.mapPanel.map;
295                         /***
296                          * Add a WMSGetFeatureInfo control to the map if it is not yet present
297                          * The control could already have been set. If not try and find
298                          * the control in the map.
299                          */
300                         if (!this.olControl) {
301                             var controls = this.map.getControlsByClass("OpenLayers.Control.WMSGetFeatureInfo");
302                             if (controls && controls.length > 0) {
303                                 for (var index = 0; index < controls.length; index++) {
304                                     //Control should not be the one used for tooltips.
305                                     if (controls[index].id !== "framework-feature-info-hover") {
306                                         this.olControl = controls[index];
307                                         // Overrule with our own info format and max features
308                                         this.olControl.infoFormat = this.infoFormat;
309                                         this.olControl.maxFeatures = this.maxFeatures;
310                                         this.olControl.hover = this.hover;
311                                         this.olControl.drillDown = this.drillDown;
312                                         break;
313                                     }
314                                 }
315                             }
316 
317                             // No GFI control present: create new and add to Map
318                             if (!this.olControl) {
319                                 this.olControl = new OpenLayers.Control.WMSGetFeatureInfo({
320                                     maxFeatures: this.maxFeatures,
321                                     queryVisible: true,
322                                     infoFormat: this.infoFormat,
323                                     hover: this.hover,
324                                     drillDown: this.drillDown
325                                 });
326                                 this.map.addControl(this.olControl);
327                             }
328                         }
329                         // Register WMSGetFeatureInfo control event handlers
330                         this.olControl.events.register("getfeatureinfo", this, this.handleGetFeatureInfo);
331                         this.olControl.events.register("beforegetfeatureinfo", this, this.handleBeforeGetFeatureInfo);
332                         this.olControl.events.register("nogetfeatureinfo", this, this.handleNoGetFeatureInfo);
333                         this.addListener("afterrender", this.onPanelRendered, this);
334                         this.addListener("render", this.onPanelRender, this);
335                         this.addListener("show", this.onPanelShow, this);
336                         this.addListener("hide", this.onPanelHide, this);
337                     },
338                     /** api: method[onPanelRender]
339                      *  Called when Panel has been rendered.
340                      *  @private
341                      */
342                     onPanelRender: function() {
343                         this.mask = new Ext.LoadMask(this.body, {msg: 'Loading...'});
344                     },
345                     /** api: method[onPanelRendered]
346                      *  Called when Panel has been rendered.
347                      *  @private
348                      */
349                     onPanelRendered: function() {
350                         if (this.ownerCt) {
351                             this.ownerCt.addListener("hide", this.onPanelHide, this);
352                             this.ownerCt.addListener("show", this.onPanelShow, this);
353                         }
354                     },
355                     /** private: method[onPanelShow]
356                      * Called after our panel is shown.
357                      * @private
358                      */
359                     onPanelShow: function() {
360                         if(this.cartPanel)
361                             this.olControl.deactivate();
362                         if (this.tabPanel) {
363                             this.tabPanel.items.each(function(item) {
364                                 return item.showLayer ? item.showLayer() : true;
365                             }, this);
366                         }
367                     },
368                     /** private: method[onPanelHide]
369                      * Called  before our panel is hidden.
370                      * @private
371                      */
372                     onPanelHide: function() {
373                         if(this.cartPanel && this.map.layersSearchOnMap && Object.keys(this.map.layersSearchOnMap).length > 0)
374                             this.olControl.activate();
375                         if (this.tabPanel) {
376                             this.tabPanel.items.each(function(item) {
377                                 return item.hideLayer ? item.hideLayer() : true;
378                             }, this);
379                         }
380                     },
381                     /** private: method[initPanel]
382                      * 
383                      * @private
384                      */
385                     initPanel: function() {
386                         this.lastEvt = null;
387                         this.expand();
388                         if (this.tabPanel) {
389                             this.tabPanel.items.each(function(item) {
390                                 this.tabPanel.remove(item);
391                                 return item.cleanup ? item.cleanup() : true;
392                             }, this);
393                         }
394 
395                         if (this.displayPanel) {
396                             this.remove(this.displayPanel);
397                             this.displayPanel = null;
398                         }
399 
400                         this.displayOn = false;
401                     },
402                     /** private: method[handleBeforeGetFeatureInfo]
403                      * 
404                      * @private
405                      */
406                     handleBeforeGetFeatureInfo: function(evt) {
407                         //If the event was not triggered from this.olControl, do nothing
408                         if (evt.object !== this.olControl) {
409                             return;
410                         }
411 
412                         this.olControl.layers = [];
413                         // Needed to force accessing multiple WMS-es when multiple layers are visible
414                         this.olControl.url = null;
415                         this.olControl.drillDown = this.drillDown;
416                         // Select WMS layers that are visible and enabled (via featureInfoFormat or Layer info_format (capitalized by OL) prop)
417                         var layer;
418                         //If a layer is specified, try and find the layer in the map.
419                         if (this.layer) {
420                             var layers = this.map.getLayersByName(this.layer);
421                             if (layers) {
422                                 //Add the first layer found with name layer
423                                 layer = layers[0];
424                                 this.olControl.layers.push(layer);
425                             }
426                         }
427 
428                         //If no layer was specified or the specified layer was not found,
429                         //assign the visible WMS-layers to the olControl.
430                         if (this.olControl.layers.length == 0) {
431                             this.layerDups = {};
432                             for (var index = 0; index < this.map.layers.length; index++) {
433                                 layer = this.map.layers[index];
434                                 // Skip non-WMS layers
435                                 if (!layer instanceof OpenLayers.Layer.WMS || !layer.params) {
436                                     continue;
437                                 }
438                                 
439         
440                                 // Enable layers for GFI that have a GFI mime param specified                              
441                                 if (layer.visibility && (layer.featureInfoFormat || layer.params.INFO_FORMAT)) {
442                                     // Backward compatible with old configs that have only featureInfoFormat
443                                     // set to a mime type like "text/xml". layer.params.INFO_FORMAT determines the mime
444                                     // requested from WMS server.
445                                     if (!layer.params.INFO_FORMAT && layer.featureInfoFormat) {
446                                         layer.params.INFO_FORMAT = layer.featureInfoFormat;
447                                     }
448 
449                                     if (this.layerDups[layer.params.LAYERS]) {
450                                         // https://code.google.com/p/geoext-viewer/issues/detail?id=215
451                                         // what to do when we have duplicate layers, at least we may replace if
452                                         // one of them is without any STYLES or FILTER or CQL.
453                                         if (this.discardStylesForDups) {
454                                             // Make the STYLES empty for the request only (restore after the request)
455                                             var dupLayer = this.layerDups[layer.params.LAYERS];
456                                             dupLayer.savedStyles = dupLayer.params.STYLES;
457                                             dupLayer.params.STYLES = "";
458                                         }
459                                         continue;
460                                     }
461 
462 
463                                     this.olControl.layers.push(layer);
464                                     this.layerDups[layer.params.LAYERS] = layer;
465                                 }
466                             }
467                         }
468 
469                         this.initPanel();
470                         if (this.mask) {
471                             // Show loading mask
472                             this.mask.show();
473                         }
474 
475                         this.fireEvent('beforefeatureinfo', evt);
476                         // Try to fetch features from WFS/Vector layers
477                         this.handleVectorFeatureInfo(evt.object.handler.evt);
478                         // No layers with GFI and no features from Vector layers available: display message
479                         if (this.olControl.layers.length == 0 && this.features == null) {
480                             this.handleNoGetFeatureInfo();
481                         }
482                     },
483                     /** private: method[handleGetFeatureInfo]
484                      * 
485                      * @private
486                      */
487                     handleGetFeatureInfo: function(evt) {
488 
489                         // Always restore possible Layer duplicate STYLES
490                         if (this.discardStylesForDups) {
491                             // https://code.google.com/p/geoext-viewer/issues/detail?id=215
492                             for (var layerName in this.layerDups) {
493                                 var layerDup = this.layerDups[layerName];
494                                 if (layerDup.savedStyles) {
495                                     layerDup.params.STYLES = layerDup.savedStyles;
496                                     layerDup.savedStyles = null;
497                                 }
498                             }
499                         }
500 
501                         // If the event was not triggered from this.olControl, and not a Vector layer, do nothing
502                         if (evt && evt.object !== this.olControl) {
503                             return;
504                         }
505 
506                         // Hide the loading mask
507                         if (this.mask) {
508                             this.mask.hide();
509                         }
510 
511                         // Save result e.g. when changing views
512                         if (evt) {
513                             this.lastEvt = evt;
514                         }
515 
516                         if (!this.lastEvt) {
517                             return;
518                         }
519 
520                         this.displayFeatures(this.lastEvt);
521                     },
522                     /** private: method[handleGetFeatureInfo]
523                      * 
524                      * Determine if Vector features are touched.
525                      * @private
526                      */
527                     handleVectorFeatureInfo: function(evt) {
528                         this.vectorFeaturesFound = false;
529                         // Nasty hack but IE refuses to play nice and provide screen X,Y as all others!!
530 
531                         var screenX = Ext.isIE ? Ext.EventObject.xy[0] : evt.clientX;
532                         var screenY = Ext.isIE ? Ext.EventObject.xy[1] : evt.clientY;
533                         this.features = this.getFeaturesByXY(screenX, screenY);
534                         if (this.mask) {
535                             this.mask.hide();
536                         }
537 
538                         evt.features = this.features;
539                         if (evt.features && evt.features.length > 0) {
540                             this.vectorFeaturesFound = true;
541                             this.displayFeatures(evt);
542                         }
543                     },
544                     /** private: method[handleNoGetFeatureInfo]
545                      * 
546                      * @private
547                      */
548                     handleNoGetFeatureInfo: function() {
549                         // When also no visible Vector layers give warning
550                         if (!this.visibleVectorLayers) {
551                             Ext.Msg.alert('Warning', 'Feature Info unavailable (you may need to make some layers visible)');
552                         }
553                     },
554                     /**
555                      * private: method[getFeaturesByXY]
556                      * Get all features at the given screen location.
557                      *
558                      * Parameters:
559                      * x - {Number} Screen x coordinate.
560                      * y - {Number} Screen y coordinate.
561                      *
562                      * Returns:
563                      * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.
564                      *
565                      * From:
566                      * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/select/lib/OpenLayers/FeatureAgent.js
567                      * @private
568                      */
569                     getFeaturesByXY: function(x, y) {
570                         this.visibleVectorLayers = false;
571                         var features = [], targets = [], layers = [];
572                         var layer, target, feature, i, len;
573                         // go through all layers looking for targets
574                         for (i = this.map.layers.length - 1; i >= 0; --i) {
575                             layer = this.map.layers[i];
576                             if (layer.div.style.display !== "none") {
577                                 if (layer instanceof OpenLayers.Layer.Vector) {
578                                     target = document.elementFromPoint(x, y);
579                                     while (target && target._featureId) {
580                                         feature = layer.getFeatureById(target._featureId);
581                                         if (feature) {
582                                             var featureClone = feature.clone();
583                                             featureClone.type = layer.name;
584                                             featureClone.layer = layer;
585                                             features.push(featureClone);
586                                             target.style.display = "none";
587                                             targets.push(target);
588                                             target = document.elementFromPoint(x, y);
589                                             this.visibleVectorLayers = true;
590                                         } else {
591                                             // sketch, all bets off
592                                             target = false;
593                                         }
594                                     }
595                                 }
596                                 layers.push(layer);
597                                 layer.div.style.display = "none";
598                             }
599                         }
600                         // restore feature visibility
601                         for (i = 0, len = targets.length; i < len; ++i) {
602                             targets[i].style.display = "";
603                         }
604                         // restore layer visibility
605                         for (i = layers.length - 1; i >= 0; --i) {
606                             layers[i].div.style.display = "block";
607                         }
608                         return features;
609                     },
610                     /***
611                      * private: method[getFeatureType]
612                      * Try to get feature type name from a feature, this is often WMS-specific and a bit of black art.
613                      * TODO: determine for ESRI WMS.
614                      * @private
615                      */
616                     getFeatureType: function(feature) {
617 
618                         // If GFI returned GML, OL has may have parsed out the featureType
619                         // http://code.google.com/p/geoext-viewer/issues/detail?id=92
620                         if (feature.gml && feature.gml.featureType) {
621                             return feature.gml.featureType;
622                         }
623 
624                         // GeoServer-specific
625                         if (feature.fid && feature.fid.indexOf('undefined') < 0) {
626                             // TODO: this is nasty and GeoServer specific ?
627                             // We may check the FT e.g. from the GML tag(s) available in the evt
628                             // More specific, we need to. Because now with multiple layers, all are assigned to
629                             // unknown and you get strange column results when the featuretypes are mixed...
630                             var featureType = /[^\.]*/.exec(feature.fid);
631                             return (featureType[0] != "null") ? featureType[0] : null;
632                         }
633 
634                         // Mapserver-specific
635                         if (feature.type) {
636                             return feature.type;
637                         }
638 
639                         // ESRI-specific
640                         if (feature.attributes['_LAYERID_']) {
641                             // Try ESRI WMS GFI returns layername/featureType as attribute '_LAYERID_'  !
642                             // See http://webhelp.esri.com/arcims/9.3/general/mergedprojects/wms_connect/wms_connector/get_featureinfo.htm
643                             // See e.g. http://svn.flamingo-mc.org/trac/changeset/648/flamingo/trunk/fmc/OGWMSConnector.as
644                             return feature.attributes['_LAYERID_'];
645                         }
646 
647                         // TNO/DINO-specific
648                         if (feature.attributes['DINO_DBA.MAP_SDE_GWS_WELL_W_HEADS_VW.DINO_NR']) {
649                             // TODO find better way to determine and fix for DINO services
650                             //			var nodes = featureNode.childNodes;
651                             //			 var _featureType = "";
652                             //			 for (j = 0,jlen = nodes.length; j < jlen; ++j) {
653                             //				 var node = nodes[j];
654                             //				 if (node.nodeType !== 3) {
655                             //					 //Dirty fix for dino name needs to be stripped as it consists of 3 parts
656                             //					 var dino_name = node.getAttribute("name");
657                             //					 var _feat = dino_name.split(".");
658                             //					 if(_feat[0] === "DINO_DBA"){
659                             //						 attributes[_feat[2]] = node.getAttribute("value");
660                             //						 _featureType = _feat[1];
661                             //					 } else {
662                             //						 attributes[node.getAttribute("name")] = node.getAttribute("value");
663                             //					 }
664                             //				 }
665                             //			 }
666                             //		 }
667                             //		 _feature = new OpenLayers.Feature.Vector(geom, attributes, null);
668                             //
669                             //		 if(_featureType !== ""){
670                             //			 // Dirty fix for dino to maintain reference to layer
671                             //			 _feature.gml = {};
672                             //			 _feature.gml.featureType = _featureType;
673                             //			 _feature.fid = _featureType + "." + len;
674                             //			 _feature.layer = _featureType;
675                             //		 }
676                             //	var _feat = dino_name.split(".");
677                             //					 if(_feat[0] === "DINO_DBA"){
678                             //						 attributes[_feat[2]] = node.getAttribute("value");
679                             //						 _featureType = _feat[1];
680                             //					 } else {
681                             //						 attributes[node.getAttribute("name")] = node.getAttribute("value");
682                             //					 }
683                             // rec.attributes[0]
684                             return 'TNO_DINO_WELLS';
685                         }
686 
687                         // TNO/DINO-specific  (see above)
688                         if (feature.attributes['DINO_DBA.MAP_SDE_BRH_BOREHOLE_RD_VW.DINO_NR']) {
689                             return 'TNO_DINO_BOREHOLES';
690                         }
691 
692                         // Nothing found :-(
693                         return 'Unknown';
694                     },
695                     /***
696                      * private: method[getFeatureTitle]
697                      * Try to get feature title (user friendly name) for a feature type name.
698                      * @private
699                      */
700                     getFeatureTitle: function(featureType) {
701                         // Fall back to featureType if we can't find the name
702                         var featureTitle = featureType;
703                         // WMS/WFS GetFeature results don't return the Human Friendly name.
704                         // So we get it from the layer declaration here and use this for the tab titles.
705                         // Resolves Enhancement #164 - JM, 2013.01.30
706                         var layers = this.map.layers;
707                         for (var l = 0; l < layers.length; l++) {
708                             var nextLayer = layers[l];
709                             // Skip non-WMS layers and non-visible layers
710                             if (!nextLayer.params || (!this.hiddenLayers && !nextLayer.visibility)) {
711                                 continue;
712                             }
713 
714                             // Ensure cases match by making all lowerCase. May not otherwise.
715                             if (featureType.toLowerCase() == /([^:]*$)/.exec(nextLayer.params.LAYERS)[0].toLowerCase()) {
716                                 featureTitle = nextLayer.name;
717                                 break;
718                             }
719                         }
720 
721                         return featureTitle;
722                     },
723                     /***
724                      * private: method[displayFeatures]
725                      * 
726                      * @private
727                      */
728                     displayFeatures: function(evt) {
729 
730                         // Were any features returned ?
731                         if (evt.features && evt.features.length > 0) {
732                             if(!evt.features[0].gml && this.cartPanel)
733                                 return;
734                             if (!this.vectorFeaturesFound && this.displayPanel) {
735                                 this.remove(this.displayPanel);
736                                 this.displayPanel = null;
737                                 this.displayOn = false;
738                             }
739                             // Delegate to current display panel (Grid, Tree, XML)
740                             this.displayPanel = this.display(evt);
741                         } else if (!this.vectorFeaturesFound) {
742                             // No features found in WMS and Vector Layers: show message
743                             if(this.cartPanel){
744                                 this.displayPanel = this.displayInfo('Nessun elemento trovato nel punto selezionato.');
745                             }else{
746                                 this.displayPanel = this.displayInfo('No features found');
747                             }
748                             
749                         }
750 
751                         if (this.displayPanel && !this.displayOn) {
752                             this.add(this.displayPanel);
753                             this.displayPanel.doLayout();
754                         }
755 
756                         if (this.getLayout() instanceof Object && !this.displayOn) {
757                             this.getLayout().runLayout();
758                         }
759                         this.displayOn = true;
760                         this.fireEvent('featureinfo', evt);
761                     },
762                     /***
763                      * private: method[displayFeatures]
764                      * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as grid)
765                      * @private
766                      */
767                     displayGrid: function(evt) {
768                         var featureSets = {}, featureSet, featureType, featureTitle, featureSetKey, featureKeysOnCart;
769                         this.searchExtent = null;
770 //                        if(this.map.activeSearchPanel){
771 //                            this.map.activeSearchPanel.remove(this.map.activeSearchPanel.tabPanel);
772 //                        }
773 //                        this.map.activeSearchPanel = this;
774 //                        
775 //                        if(this.map.layersWithFeature && this.map.layersWithFeature.length > 0){
776 //                            for(var i=0;i<this.map.layersWithFeature.length;i++){
777 //                                this.map.layersWithFeature[i].destroyFeatures();
778 //                            }
779 //                            if (window.popupAddr) {
780 //                                window.popupAddr.close();
781 //                                window.popupAddr.destroy();
782 //                            }
783 //                        }
784 //                        this.map.layersWithFeature = [];
785 //                        
786                         //se esiste un carrello carico le chiavi delle feature presenti nel carrello
787                         if (this.cartPanel) {
788                             var cart = Ext.getCmp(this.cartPanel);
789                             featureKeysOnCart = cart.getKeysStore();
790                         }
791 
792                         // Extract feature set per feature type from total array of features
793                         for (var index = 0; index < evt.features.length; index++) {
794                             var feature = evt.features[index];
795                             // Get feature type name
796                             featureType = this.getFeatureType(feature);
797                             featureTitle = this.getFeatureTitle(featureType);
798                             featureSetKey = featureType + featureTitle;
799                             //se esiste un carrello allora selezionare le feature presenti sul carrello
800                             if (this.cartPanel) {
801                                 var key = featureType + '_' + feature.attributes.IMAGE;
802                                 if (featureKeysOnCart.indexOf(key) > -1)
803                                     feature.attributes.onCart = true;
804                                 else
805                                     feature.attributes.onCart = false;
806                             }
807 
808                             if (!featureSets[featureSetKey]) {
809                                 featureSet = {
810                                     featureType: featureType,
811                                     title: featureTitle,
812                                     features: []
813                                 };
814                                 featureSets[featureSetKey] = featureSet;
815                             }
816 
817 
818                             /***
819                              * Go through attributes and modify where needed:
820                              * - hyperlinks clickable
821                              * - illegal field names (with dots)
822                              * - custom hyperlinks
823                              */
824                             for (var attrName in feature.attributes) {
825 
826                                 // Check for hyperlinks
827                                 // Simple fix for issue 23
828                                 // http://code.google.com/p/geoext-viewer/issues/detail?id=23
829                                 var attrValue = feature.attributes[attrName];
830                                 if (attrValue && typeof attrValue == 'string' && attrValue.indexOf("http://") >= 0) {
831                                     // Display value as HTML hyperlink
832                                     feature.attributes[attrName] = '<a href="' + attrValue + '" target="_new">' + attrValue + '</a>';
833                                 }
834 
835                                 // GetFeatureInfo response may contain dots in the fieldnames, these are not allowed in ExtJS store fieldnames.
836                                 // Use a regex to replace the dots /w underscores.
837                                 if (attrName.indexOf(".") >= 0) {
838                                     var newAttrName = attrName.replace(/\./g, "_");
839                                     feature.attributes[newAttrName] = feature.attributes[attrName];
840                                     if (attrName != newAttrName) {
841                                         delete feature.attributes[attrName];
842                                     }
843                                 }
844                             }
845 
846                             featureSet.features.push(feature);
847                         }
848 
849                         // Remove any existing panel
850                         if (this.tabPanel != null && !this.displayOn) {
851                             this.remove(this.tabPanel);
852                             this.tabPanel = null;
853                         }
854 
855                         // Run through feature sets, creating a feature grid for each
856                         // and adding to the tab panel
857                         for (featureSetKey in featureSets) {
858                             featureSet = featureSets[featureSetKey];
859                             if (featureSet.features.length == 0) {
860                                 // Do not display empty feature set
861                                 continue;
862                             }
863 
864                             var grid = {};
865                             if (this.cartPanel) {
866                                 grid = new framework.widgets.search.FeatureSearchGridPanel({
867                                     title: featureSet.title,
868                                     cartPanel: this.cartPanel,
869                                     basePathResources: this.basePathResources,
870                                     managerProductPanel: this.managerProductPanel,
871                                     urlGeoNetwork: this.urlGeoNetwork,
872                                     featureType: featureSet.featureType,
873                                     header: false,
874                                     features: featureSet.features,
875                                     autoConfig: true,
876                                     showGeometries: this.showGeometries,
877                                     featureSelection: this.featureSelection,
878                                     gridCellRenderers: this.gridCellRenderers,
879                                     showTopToolbar: this.showTopToolbar,
880                                     exportFormats: this.exportFormats,
881                                     father: this,
882                                     map: this.map,
883                                     enableColumnHide: false,
884                                     viewConfig: {
885                                         forceFit: true,
886                                         sortAscText: 'Ordine crescente',
887                                         sortDescText: 'Ordine decrescente',
888                                         columnsText: 'Colonne',
889                                         lockText: "Colonna fissa",
890                                         unlockText: "Colonna libera"
891                                     },
892                                     hropts: {
893                                         zoomOnRowDoubleClick: true,
894                                         zoomOnFeatureSelect: false,
895                                         zoomLevelPointSelect: 8,
896                                         zoomToDataExtent: this.zoomToDataExtent
897                                     }
898                                 });
899                             } else {
900                                 grid = new framework.widgets.search.FeatureGridPanel({
901                                     title: featureSet.title,
902                                     featureType: featureSet.featureType,
903                                     header: false,
904                                     features: featureSet.features,
905                                     autoConfig: true,
906                                     showGeometries: this.showGeometries,
907                                     featureSelection: this.featureSelection,
908                                     gridCellRenderers: this.gridCellRenderers,
909                                     showTopToolbar: this.showTopToolbar,
910                                     exportFormats: this.exportFormats,
911                                     map: this.map,
912                                     iconButton: this.iconButton,
913                                     hropts: {
914                                         zoomOnRowDoubleClick: true,
915                                         zoomOnFeatureSelect: false,
916                                         zoomLevelPointSelect: 8,
917                                         zoomToDataExtent: this.zoomToDataExtent
918                                     },
919                                     listeners: {
920                                         showAerialPhoto: function(record) {
921                                             this.fireEvent('showAerialPhoto', record);
922                                         },
923                                         scope: this
924                                     }});
925                             }
926 
927                             // Create tab panel for the first FT and add additional tabs for each FT
928                             if (!this.tabPanel) {
929                                 this.tabPanel = new Ext.TabPanel({
930                                     border: false,
931                                     autoDestroy: true,
932                                     enableTabScroll: true,
933                                     //height: this.getHeight(),
934                                     items: [grid],
935                                     activeTab: 0
936                                 });
937                             } else {
938                                 // Add to existing tab panel
939                                 this.tabPanel.add(grid);
940                                 this.tabPanel.setActiveTab(0);
941                             }
942 
943                             grid.loadFeatures(featureSet.features, featureSet.featureType);
944                         }
945                         return this.tabPanel;
946                     },
947                     /***
948                      * private: method[displayTree]
949                      * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as Tree)
950                      * @private
951                      */
952                     displayTree: function(evt) {
953                         var panel = new framework.widgets.XMLTreePanel();
954                         panel.xmlTreeFromText(panel, evt.text);
955                         return panel;
956                     },
957                     /***
958                      * private: method[displayFeatures]
959                      * Callback function for handling the result of an OpenLayers GetFeatureInfo request (display as XML)
960                      * @private
961                      */
962                     displayXML: function(evt) {
963                         var opts = {
964                             // DA VERIFICARE
965                             // html: '<div class="hr-html-panel-body"><pre>' + Heron.Utils.formatXml(evt.text, true) + '</pre></div>',
966                             html: '<div class="hr-html-panel-body"><pre>' + evt.text + '</pre></div>',
967                             preventBodyReset: true,
968                             autoScroll: true
969                         };
970                         return new Ext.Panel(opts);
971                     },
972                     /***
973                      * private: method[displayInfo]
974                      * Display info panel.
975                      * @private
976                      */
977                     displayInfo: function(infoStr) {
978                         var opts = {
979                             html: '<div class="hr-html-panel-body"><pre>' + infoStr + '</pre></div>',
980                             preventBodyReset: true,
981                             autoScroll: true
982                         };
983                         return new Ext.Panel(opts);
984                     }
985                 });
986         /** api: xtype = framework_featureinfopanel */
987         Ext.reg('framework_featureinfopanel', framework.widgets.search.FeatureInfoPanel);
988