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