WAB Export to CSV Disable X Y Columns

2562
14
04-12-2018 03:22 PM
ZlatinaGounev1
New Contributor II

Hi There,

By default when exporting an attribute table in a Portal App that has been created with WAB2.6  X,Y fields are generated automatically for point data:

Export to CSV—Exports the attributes to a CSV file. The X, Y coordinates are included for the point feature layer and keep the same spatial reference as the data regardless the map projection. If records are selected, only the selected records are exported. If no records are selected, all the records are exported. To support this function, the Export Data property must be enabled for the corresponding feature service.

How can this functionality be disabled. I would like to give the app users to download the data for analysis purposes but without getting the actual X,Y.

Thank you

Tags (1)
14 Replies
RobertScheitlin__GISP
MVP Emeritus

Zlatina,

   If you using WAB Developer edition you can comment out one line in the [intsall dir]\server\apps\[app#]\widgets\AttributeTable\table\_FeatureTable.js file to prevent the x and y fields from being added.

Search fro this line and comment it out in the file mentioned above:

options.withGeometry = this.layer.geometryType === 'esriGeometryPoint';
ZlatinaGounev1
New Contributor II

Hi Robert,

Thank you for the suggestion. I have commented out the line of code you have suggested but I am still seeing the xy when I export point data to csv. Do you know of any other places in the code that change needs to be made?

Thanks

Zlatina

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Zlatina,

   After some more testing. Here is what I got to work for me.

[intsall dir]\server\apps\[app#]\widgets\AttributeTable\table\_FeatureTable.js

//change
if(_exportData.length){
  this._appendXY(_exportData);
}
//to
if(_exportData.length){
  //this._appendXY(_exportData);
}

and

//change
var _outFields = this.getOutFields(!!_exportData.length);
//to
var _outFields = this.getOutFields(false);//!!_exportData.length);

 

0 Kudos
ZlatinaGounev
New Contributor II

Hi Robert,

Below you will find my _FeatureTable.js file. I was not able to find the lines of code you have specified above to comment out. I am using WAB2.6.

Thanks

///////////////////////////////////////////////////////////////////////////
// Copyright © 2014 - 2017 Esri. All Rights Reserved.
//
// Licensed under the Apache License Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////
define([
  'dojo/_base/declare',
  'dojo/_base/html',
  'dijit/_WidgetBase',
  'jimu/dijit/Popup',
  'jimu/dijit/Message',
  'jimu/dijit/Filter',
  "dgrid/OnDemandGrid",
  "../dgrid/Selection", // use custom Selection
  "dgrid/extensions/ColumnHider",
  "dgrid/extensions/ColumnResizer",
  "dgrid/extensions/ColumnReorder",
  "dijit/Menu",
  "dijit/MenuItem",
  'dijit/Toolbar',
  'dijit/form/Button',
  'dijit/DropDownMenu',
  'dijit/form/ToggleButton',
  'dijit/form/DropDownButton',
  "dojo/Deferred",
  "dojo/when",
  'dojo/Evented',
  'dojo/touch',
  "dojo/store/Memory",
  "esri/config",
  "esri/lang",
  "esri/request",
  "esri/tasks/RelationParameters",
  "esri/tasks/RelationshipQuery",
  // "esri/layers/GraphicsLayer",
  "esri/layers/FeatureLayer",
  "esri/tasks/QueryTask",
  "esri/tasks/query",
  "esri/tasks/ProjectParameters",
  // "esri/graphic",
  // "esri/geometry/Point",
  "esri/geometry/Multipoint",
  "esri/geometry/geometryEngine",
  // "esri/geometry/Polyline",
  // "esri/geometry/Polygon",
  // "esri/symbols/SimpleLineSymbol",
  // "esri/symbols/SimpleFillSymbol",
  // "esri/Color",
  "esri/geometry/normalizeUtils",
  "esri/IdentityManager",
  'dojo/_base/lang',
  "dojo/on",
  "dojo/aspect",
  "dojo/_base/array",
  'jimu/dijit/LoadingIndicator',
  'jimu/dijit/FieldStatistics',
  'jimu/SelectionManager',
  'jimu/CSVUtils',
  'jimu/utils',
  '../utils',
  'dojo/query'
], function(
  declare,
  html,
  _WidgetBase,
  Popup,
  Message,
  Filter,
  OnDemandGrid,
  Selection,
  ColumnHider,
  ColumnResizer,
  ColumnReorder,
  Menu,
  MenuItem,
  Toolbar,
  Button,
  DropDownMenu,
  ToggleButton,
  DropDownButton,
  Deferred,
  when,
  Evented,
  touch,
  Memory,
  esriConfig,
  esriLang,
  esriRequest,
  RelationParameters,
  RelationshipQuery,
  // GraphicsLayer,
  FeatureLayer,
  QueryTask,
  Query,
  ProjectParameters,
  // Graphic,
  // Point,
  Multipoint,
  geometryEngine,
  // Polyline,
  // Polygon,
  // SimpleLineSymbol,
  // SimpleFillSymbol,
  // Color,
  normalizeUtils,
  esriId,
  lang,
  on,
  aspect,
  array,
  LoadingIndicator,
  FieldStatistics,
  SelectionManager,
  CSVUtils,
  jimuUtils,
  tableUtils,
  query
) {
  return declare([_WidgetBase, Evented], {
    baseClass: 'jimu-widget-attributetable-feature-table',
    _defaultFeatureCount: 2000, //default max records by one request
    _defaultBatchCount: 25, //default records per page
    _batchCount: 0,//records per page

    _filterObj: null, // come from filter popup
    _currentDef: null,
    //initial: initial state
    //processing: querying data
    //canceled: stop querying data to save network brandwidth
    //fullfilled: get the data
    _requestStatus: 'initial', // initial, processing, canceled, fulfilled

    map: null,
    matchingMap: false,
    layerInfo: null,
    configedInfo: null,
    // tableDisplayer: null,
    // parentWidget: null,
    footerHeight: 25,

    layer: null, // layerInfo.layerObject
    loading: null, // LoadingIndicator
    grid: null, // dgrid
    footer: null, // footer div
    selectedRowsLabel: null, // selected div on footer
    selectionRows: null, // id of rows was selected,[id]
    // griaphicLayer: null,
    nls: null,

    //_layerDefinition: null,
    // _filterObj: null,
    _dblClickResult: null,// a feature which be zoom to and show pupup by double click row

    actived: false, // tabcontent is selected
    showSelected: false, // if true only show selected rows
    tableCreated: false, // if true dgrid is created

    // for queryRelatedIds
    // it is set to true when click "Show Related Records" from another _FeatureTable
    // If true, when click "Show All Records", it will be set to false and layerType will be changed
    _relatedQuery: false,
    _relatedQueryIds: null,//[originalFeatureLayerObjectId]

    _selectionHandles: null,//save handles for selecion-complete event of FeatureLayer
    _selectionResults: null,//[Feature], used for SelectionManager.selectFeatures()
    //events:
    //data-loaded//emit after one query requst is done, but maybe not get real features
    //row-click
    //clear-selection
    //sort

    //show-all-records
    //show-selected-records
    //show-related-records
    //show-filter
    //apply-filter
    //toggle-columns
    //export-csv
    //zoom-to
    //refresh

    constructor: function(options) {
      options = options || {};
      this.set('map', options.map || null);
      this.set('matchingMap', !!options.matchingMap);
      this.set('layerInfo', options.layerInfo || null);
      this.set('layer', options.layer || null);
      this.set('configedInfo', options.configedInfo || null);

      this.set('relatedOriginalInfo', options.relatedOriginalInfo || null);
      this.set('relationship', options.relationship || null);

      // syncSelection if true scroll record to view after click feature in map
      this.set('syncSelection', !!options.syncSelection || true);
    },

    postCreate: function() {
      this.inherited(arguments);
      this._relatedQueryIds = [];
      this.createToolbar();
      this.loading = new LoadingIndicator();
      this.loading.placeAt(this.domNode);

      this.selectionManager = SelectionManager.getInstance();
      this._selectionResults = [];
      this.selectionRows = [];

      html.setAttr(this.domNode, 'data-layerinfoid', lang.getObject('layerInfo.id', false, this));

      if (this.get('map')) {
        this.own(on(this.map.root, touch.release, lang.hitch(this, function() {
          this._clickMap = true;
        })));
        this.own(on(this.map, 'extent-change', lang.hitch(this, '_onExtentChange')));
      }
    },

    startup: function() {
      this.inherited(arguments);
      this.toolbarHeight = parseInt(html.getStyle(this.toolbar.domNode, 'height'), 10) || 0;
    },

    setLayerDefinition: function(definition) {
      this._layerDefinition = definition;
    },

    getLayerDefinition: function() {
      return this._getLayerDifinition();
    },

    getFilterableFields: function() {
      if (this._layerDefinition && this.configedInfo) {
        var cloned = lang.clone(this._layerDefinition);
        return this._getFilterableFields(cloned.fields, this.configedInfo.layer.fields);
      } else {
        return [];
      }
    },

    getFilterObj: function() {
      return this._filterObj;
    },

    setFilterObj: function(fObj) {
      this._filterObj = fObj;
    },

    showRefreshing: function(isshow) {
      if (isshow) {
        this.loading.show();
      } else {
        this.loading.hide();
      }
    },

    active: function() {
      this.actived = true;
    },

    deactive: function() {
      this.actived = false;
    },

    cancelThread: function() {
      if (this._requestStatus !== 'fulfilled' && this._currentDef) {
        this._currentDef.cancel({
          canceledBySelf: true
        });
        this._requestStatus = 'canceled';
      }
    },

    isCanceled: function() {
      return this._requestStatus === 'canceled';
    },

    createToolbar: function() {
      var toolbar = new Toolbar({}, html.create("div"));

      var menus = new DropDownMenu();

      this.showSelectedRecordsMenuItem = new MenuItem({
        label: this.nls.showSelectedRecords,
        iconClass: "esriAttributeTableSelectPageImage",
        onClick: lang.hitch(this, this.toggleSelectedRecords)
      });
      menus.addChild(this.showSelectedRecordsMenuItem);

      this.showRelatedRecordsMenuItem = new MenuItem({
        label: this.nls.showRelatedRecords,
        iconClass: "esriAttributeTableSelectAllImage",
        onClick: lang.hitch(this, this.onShowRelatedRecordsClick)
      });
      menus.addChild(this.showRelatedRecordsMenuItem);

      this.filterMenuItem = new MenuItem({
        label: this.nls.filter,
        iconClass: "esriAttributeTableFilterImage",
        onClick: lang.hitch(this, this.onFilterMenuItemClick)
      });
      menus.addChild(this.filterMenuItem);

      this.toggleColumnsMenuItem = new MenuItem({
        label: this.nls.columns,
        iconClass: "esriAttributeTableColumnsImage",
        onClick: lang.hitch(this, this.onToggleColumnsClick)
      });
      menus.addChild(this.toggleColumnsMenuItem);

      if (!this.hideExportButton) {
        // always set exportButton to true
        this.exportCSVMenuItem = new MenuItem({
          label: this.nls.exportFiles,
          showLabel: true,
          iconClass: "esriAttributeTableExportImage",
          onClick: lang.hitch(this, this.onExportCSVClick)
        });
        menus.addChild(this.exportCSVMenuItem);
      }

      this.selectionMenu = new DropDownButton({
        label: this.nls.options,
        iconClass: "esriAttributeTableOptionsImage",
        dropDown: menus
      });
      toolbar.addChild(this.selectionMenu);

      this.matchingCheckBox = new ToggleButton({
        checked: this.matchingMap ? true : false,
        showLabel: true,
        label: this.nls.filterByExtent,
        onChange: lang.hitch(this, function(status) {
          this.set('matchingMap', status);
          this._onMatchingCheckBoxChange(status);
        })
      });
      toolbar.addChild(this.matchingCheckBox);

      this.zoomButton = new Button({
        label: this.nls.zoomto,
        iconClass: "esriAttributeTableZoomImage",
        onClick: lang.hitch(this, this.zoomTo)
      });
      toolbar.addChild(this.zoomButton);

      this.clearSelectionButton = new Button({
        label: this.nls.clearSelection,
        iconClass: "esriAttributeTableClearImage",
        onClick: lang.hitch(this, this.clearSelection, true, true)
      });
      toolbar.addChild(this.clearSelectionButton);

      this.refreshButton = new Button({
        label: this.nls.refresh,
        showLabel: true,
        iconClass: "esriAttributeTableRefreshImage",
        onClick: lang.hitch(this, this.refresh)
      });
      toolbar.addChild(this.refreshButton);

      // this.closeButton = new Button({
      //   title: this.nls.closeMessage,
      //   iconClass: "esriAttributeTableCloseImage",
      //   onClick: lang.hitch(this, this._onCloseBtnClicked)
      // });
      // html.addClass(this.closeButton.domNode, 'close-button');
      // toolbar.addChild(this.closeButton);

      html.place(toolbar.domNode, this.domNode);
      this.toolbar = toolbar;

      this.own(
        on(this, 'data-loaded,row-click,clear-selection', lang.hitch(this, 'changeToolbarStatus'))
      );
    },

    //queryByStoreObjectIds: [objectId]
    //queryByStoreObjectIds exists when layerType is RELATIONSHIPTABLE and matchingMapExtent is true
    startQuery: function(/*optional*/ queryByStoreObjectIds) {
      this.cancelThread();
      // if (this.tableCreated && this.layerInfo &&
      //   this.layerInfo.isTable && !this._relatedQuery) {
      //   return;
      // }

      this._requestStatus = 'processing';
      this.loading.show();
      if (!queryByStoreObjectIds || !queryByStoreObjectIds.length) {
        this._relatedQuery = false;
        this._relatedQueryIds = [];
        this.queryByStoreObjectIds = null;
      } else {// this case means filter by map extent in related mode
        //or view in table about featureSet
        this.queryByStoreObjectIds = queryByStoreObjectIds;
      }
      this._currentDef = this._getLayerObject();
      this._currentDef.then(lang.hitch(this, function(layerObject) {
        if (!this.domNode) {
          return;
        }

        this.layer = layerObject;
        var extent = (!this.layerInfo.isTable && this.matchingMap && this.map.extent) || null;
        if (extent && extent.spatialReference && extent.spatialReference.isWebMercator()) {
          //normalizeUtils.normalizeCentralMeridian is called because the extent maybe cross 180
          var normalizeDef = this._currentDef = normalizeUtils.normalizeCentralMeridian(
            [extent], null, lang.hitch(this, function(normalizedGeo) {
              return normalizedGeo[0];
            }));
          normalizeDef.then(lang.hitch(this, function(_extent) {
            this._doQuery(_extent, queryByStoreObjectIds);
          }), lang.hitch(this, function(err) {
            if (err && err.message !== 'Request canceled') {
              console.error(err);
            }
            this.changeToolbarStatus();
            this.loading.hide();
          }));
        } else {
          this._doQuery(extent, queryByStoreObjectIds);
        }
      }), lang.hitch(this, function(err) {
        console.error(err);
        this.changeToolbarStatus();
        this.loading.hide();
      }));
    },

    //this method is called when click "Show Related Records" option.
    queryRecordsByRelationship: function(params) {
      var layer = params && params.layer;
      var selectedIds = params && params.selectedIds; // original feautrelayer ids
      if (params && params.relationship) {
        this.set('relationship', params.relationship);
      }
      if (params && params.relatedOriginalInfo) {
        this.set('relatedOriginalInfo', params.relatedOriginalInfo);
      }
      var ship = this.relationship;

      //this._relatedQueryIds is previous selectedIds
      //selectedIds is new ids
      if (layer && ship && selectedIds && selectedIds.length > 0 &&
        !jimuUtils.isEqual(this._relatedQueryIds, selectedIds) &&
        lang.getObject('relatedOriginalInfo.layerObject.url', false, this)) {
        this.loading.show();
        this._requestStatus = 'processing';
        this._relatedQuery = true;

        // unchecked filter by map extent if query related records
        this.matchingCheckBox.set('checked', false);

        this.cancelThread();

        this._currentDef = this._getLayerObject();
        this._currentDef.then(lang.hitch(this, function(layerObject) {
          if (!this.domNode) {
            return;
          }

          this.layer = layerObject;

          var ofs = [];
          var response = this.layer;//point to related layer
          array.forEach(response.fields, function(f) {
            if (!esriLang.isDefined(f.show) || f.show === true ||
              (f.type === 'esriFieldTypeOID' ||
                (esriLang.isDefined(response.objectIdField) &&
                  f.name === response.objectIdField))) {
              ofs.push(f.name);
            }
          });
          var relatedQuery = new RelationshipQuery();
          relatedQuery.objectIds = selectedIds;
          relatedQuery.outFields = ofs.length ? ofs : ['*'];
          relatedQuery.relationshipId = ship.id;
          relatedQuery.returnGeometry = this.layer.geometryType === 'esriGeometryPoint';//false;

          var relatedDef = this._currentDef = layer.queryRelatedFeatures(relatedQuery,
            function(relatedFeatures) {
              return relatedFeatures;
            });
          relatedDef.then(lang.hitch(this, function(relatedFeatures) {
            if (!this.domNode) {
              return;
            }
            var results = {
              displayFieldName: ship.objectIdField,
              fields: response.fields,
              features: [],
              fieldAliases: null
            };
            var featureIds = {};
            //start to reduce duplicate features
            var unique = function(rf) {
              var rfId = rf.attributes[this.layer.objectIdField];
              if (!featureIds[rfId]) {
                featureIds[rfId] = true;
                results.features.push(rf);
              }
            };

            for (var p in relatedFeatures) {
              var _set = relatedFeatures[p];
              array.forEach(_set && _set.features, unique, this);
            }
            //end

            html.destroy(this.tipNode);
            if (results.features.length > 0) {
              if (!this.grid) {
                this._removeTable();
                html.place(this.loading.domNode, this.domNode);
              }
              var oFields = this._getOutFieldsFromLayerInfos(this.layer.objectIdField);
              this.queryExecute(oFields, results.features.length, false, results);
            } else {
              this.tipNode = html.toDom('<div>' + this.nls.noRelatedRecords + '</div>');
              this._removeTable();
              html.place(this.tipNode, this.domNode);
              html.place(this.loading.domNode, this.domNode);
            }
            this._relatedQueryIds = selectedIds;
            this.emit('data-loaded');

            this.loading.hide();
          }), lang.hitch(this, function(err) {
            if (err && err.message !== 'Request canceled') {
              console.error(err);
            }
            html.destroy(this.tipNode);
            this.tipNode = html.toDom('<div>' + this.nls.noRelatedRecords + '</div>');
            this._removeTable();
            html.place(this.tipNode, this.domNode);
            html.place(this.loading.domNode, this.domNode);
            this.loading.hide();
            this.changeToolbarStatus();
          }));
        }), lang.hitch(this, function(err) {
          console.error(err);
          this.loading.hide();
          this.changeToolbarStatus();
        }));
      } else {
        this.loading.hide();
      }
    },

    getSelectedRows: function() {
      if (!this.tableCreated) {
        return [];
      }
      return this.selectionRows;
    },

    zoomTo: function() {
      this._zoomToSelected();
    },

    toggleSelectedRecords: function() {
      if (this._relatedQuery && !this.showSelected && this.getSelectedRows().length === 0) {
        //from RELATIONSHIPTABLE to FEATURELAYER and need to send request to get all data
        this._relatedQuery = false;
        this._relatedQueryIds = [];
        if (this.layerInfo && !this.layerInfo.isTable) {
          this.matchingCheckBox.set('checked', true);
        }
        this.startQuery();
        this.showSelectedRecordsMenuItem.set('label', this.nls.showSelectedRecords);
        this.showSelected = false;
        this.emit('show-all-records', {
          layerInfoId: this.layerInfo.id
        });
        return;
      }

      if (!this.tableCreated) {
        return;
      }
      if (this.showSelected) {
        //both for FEATURELAYER and RELATIONSHIP
        //from selected mode to all mode, don't need to request data
        this.showAllSelectedRecords();
        this.showSelectedRecordsMenuItem.set('label', this.nls.showSelectedRecords);
        this.showSelected = false;
        this.emit('show-all-records', {
          layerInfoId: this.layerInfo.id
        });
      } else {
        //both for FEATURELAYER and RELATIONSHIP
        //from all mode to selected mode, don't need to request data
        this.showSelectedRecords();
        this.showSelectedRecordsMenuItem.set('label', this.nls.showAllRecords);
        this.showSelected = true;
        this.emit('show-selected-records', {
          layerInfoId: this.layerInfo.id
        });
      }
      // this.showSelected = !this.showSelected;
    },

    onShowRelatedRecordsClick: function() {
      this.emit('show-related-records', {
        layerInfoId: this.layerInfo.id,
        objectIds: this.getSelectedRows()
      });
    },

    onFilterMenuItemClick: function() {
      this.showRefreshing(true);
      this.getLayerDefinition()
        .then(lang.hitch(this, function(definition) {
          if (!this.domNode) {
            return;
          }
          definition = lang.clone(definition);

          var fFields = this.getFilterableFields();
          definition.fields = fFields;

          var filter = new Filter({
            noFilterTip: this.nls.noFilterTip,
            style: "width:100%;"
          });
          this._filterPopup = new Popup({
            titleLabel: this.nls.filter,
            width: 720,
            height: 485,
            content: filter,
            buttons: [{
              label: this.nls.ok,
              onClick: lang.hitch(this, function() {
                var partsObj = filter.toJson();
                if (partsObj && partsObj.expr) {
                  this.setFilterObj(partsObj);
                  this.clearSelection(false);
                  this.startQuery();
                  this._filterPopup.close();
                  this._filterPopup = null;

                  this.emit('apply-filter', {
                    layerInfoId: this.layerInfo.id,
                    expr: partsObj.expr
                  });
                } else {
                  new Message({
                    message: this.nls.setFilterTip
                  });
                }
              })
            }, {
              label: this.nls.cancel
            }]
          });
          var filterObj = this.getFilterObj();
          if (filterObj) {
            filter.buildByFilterObj(this.layer.url, filterObj, definition);
          } else {
            filter.buildByExpr(this.layer.url, null, definition);
          }
        }), lang.hitch(this, function(err) {
          if (!this.domNode) {
            return;
          }
          console.error(err);
        })).always(lang.hitch(this, function() {
          this.showRefreshing(false);
        }));
      this.emit('show-filter', {
        layerInfoId: this.layerInfo.id
      });
    },

    onToggleColumnsClick: function() {
      this.toggleColumns();
      this.emit('toggle-columns', {
        layerInfoId: this.layerInfo.id
      });
    },

    onExportCSVClick: function() {
      var popup = new Message({
        message: this.nls.exportMessage,
        titleLabel: this.nls.exportFiles,
        autoHeight: true,
        buttons: [{
          label: this.nls.ok,
          onClick: lang.hitch(this, function() {
            this.exportToCSV();
            popup.close();
          })
        }, {
          label: this.nls.cancel
        }]
      });
      this.emit('export-csv', {
        layerInfoId: this.layerInfo.id
      });
    },

    onSelectionComplete: function(evt) {
      var features = evt.features;
      if (!features) {
        return;
      }
      if (features.length === 0) {
        this.clearSelection(false);
      } else if (!this._selectBySelf(features)) {
        this.clearSelection(false);
        this._updateSelectRowsByFeatures(evt.features);
      }
    },

    changeToolbarStatus: function() {
      //selected/all option

      if (!this.tableCreated) {
        //dgrid not created, set initial state
        if (this._relatedQuery) {
          //if RELATIONSHIPTABLE mode, show "Show All Records"
          this.showSelectedRecordsMenuItem.set('disabled', false);
          this.showSelectedRecordsMenuItem.set('label', this.nls.showAllRecords);
        } else {
          this.showSelectedRecordsMenuItem.set('disabled', true);
        }

        this.showRelatedRecordsMenuItem.set('disabled', true);
        this.matchingCheckBox.set('disabled', true);
        this.filterMenuItem.set('disabled', true);
        this.toggleColumnsMenuItem.set('disabled', true);
        if (!this.hideExportButton) {
          this.exportCSVMenuItem.set('disabled', true);
        }
        this.zoomButton.set('disabled', true);
        return;
      }

      //now dgid is created
      var selectionRows = this.getSelectedRows();
      if (this.showSelected) {
        //if this.showSelected is true, means we are in selected mode
        //now we need to show "Show All Records"
        this.showSelectedRecordsMenuItem.set('label', this.nls.showAllRecords);
      } else {
        this.showSelectedRecordsMenuItem.set('label', this.nls.showSelectedRecords);
      }
      if (this.tableCreated && selectionRows && selectionRows.length > 0 &&
        this.layer && this.layer.objectIdField) {
        this.showSelectedRecordsMenuItem.set('disabled', false);
        this.clearSelectionButton.set('disabled', false);
      } else {
        this.showSelectedRecordsMenuItem.set('disabled', true);
        this.clearSelectionButton.set('disabled', true);
      }

      if (this._relatedQuery) {
        this.showSelectedRecordsMenuItem.set('disabled', false);
        if (this.tableCreated && selectionRows && selectionRows.length === 0) {
          this.showSelectedRecordsMenuItem.set('label', this.nls.showAllRecords);
        }
      }

      //related records option

      this.showRelatedRecordsMenuItem.set('disabled', true);
      if (this.layerInfo && this.isSupportQueryToServer() &&
        selectionRows && selectionRows.length > 0 &&
        this.layer && this.layer.objectIdField) {
        if (this._relatedDef && !this._relatedDef.isFulfilled()) {
          this._relatedDef.cancel();
        }

        var resDef = this.layerInfo.getRelatedTableInfoArray();
        this._relatedDef = resDef;
        resDef.then(lang.hitch(this, function(tableInfos) {
          if (!this.domNode) {
            return;
          }
          var selectionRows = this.getSelectedRows();
          if (this.tableCreated && tableInfos && tableInfos.length > 0 &&
            selectionRows && selectionRows.length > 0) {
            this.showRelatedRecordsMenuItem.set('disabled', false);
          }
        }));
      }

      //filter option
      if (this.tableCreated && this.isSupportQueryToServer() && !this._relatedQuery) {
        this.filterMenuItem.set('disabled', false);
      } else {
        this.filterMenuItem.set('disabled', true);
      }

      //map extent option
      this.matchingCheckBox.set('disabled', false);

      //Show/Hide Columns option
      if (this.tableCreated) {
        this.toggleColumnsMenuItem.set('disabled', false);
      } else {
        this.toggleColumnsMenuItem.set('disabled', true);
      }

      //export option
      if (!this.hideExportButton) {
        if (selectionRows && selectionRows.length > 0) {
          this.exportCSVMenuItem.set('label', this.nls.exportSelected);
        } else {
          this.exportCSVMenuItem.set('label', this.nls.exportAll);
        }
        if (this.tableCreated) {
          this.exportCSVMenuItem.set('disabled', false);
        } else {
          this.exportCSVMenuItem.set('disabled', true);
        }
      }

      //zoom to option
      if (this.layerInfo.isShowInMap()) {
        // this.showGraphic();
        if (this.tableCreated && selectionRows && selectionRows.length > 0) {
          this.zoomButton.set('disabled', false);
        } else {
          this.zoomButton.set('disabled', true);
        }
      } else {
        // if (this.tableCreated) {
        //   this.hideGraphic();
        // }
        this.zoomButton.set('disabled', true);
      }
      if (this.layerInfo.isTable) {
        this.matchingCheckBox.set('disabled', true);
        this.zoomButton.set('disabled', true);
      }
    },

    showSelectedRecords: function() {
      if (!this.tableCreated) {
        return;
      }
      var oid = this.layer.objectIdField;
      // this.grid._clickShowSelectedRecords = true;
      var ids = this.getSelectedRows();

      if (ids.length > 0 && this.grid) {
        // when refresh completed select these rows..
        if (this.grid.store instanceof Memory) {
          this.grid.set('query', lang.hitch(this, function(item) {
            if (typeof item === 'number' && ids.indexOf(item) > -1) {
              return true;
            } else if (ids.indexOf(item[oid]) > -1) {
              return true;
            }
            return false;
          }));
        } else {
          this.grid.set('query', function() {
            return ids;
          });
        }
      }
    },

    showAllSelectedRecords: function() {
      if (!this.tableCreated) {
        return;
      }
      if (this.showSelected) {
        this.grid.set('query', {});
        var selectedIds = this.getSelectedRows();
        this._updateSelectRowsByIds(selectedIds);
      }
    },

    clearSelection: function(requery, clearSelectionSet) {
      if (!this.tableCreated) {
        return;
      }
      this.grid.clearSelection();
      this.selectionRows = [];
      if (requery) {
        //from selected mode to all mode
        this.grid.set('query', {});
      }
      if (clearSelectionSet) {
        this.selectionManager.clearSelection(this.layer);
        this._selectionResults = [];
      }
      this._closePopup();
      // this.graphicsLayer.clear();

      this.setSelectedNumber();
      this.showSelected = false;

      this.emit('clear-selection');
    },

    refresh: function() {
      if (this.grid) {
        this.grid.clearSelection();
        // this.clearSelection(false);
      }

      if (!this._relatedQuery) {
        this.startQuery();
      }

      this.emit('refresh', {
        layerInfoId: this.layerInfo.id
      });
    },

    exportToCSV: function(fileName) {
      if (!this.layerInfo || !this.layer || !this.tableCreated) {
        return;
      }
      var _outFields = null;
      var pk = this.layer.objectIdField;
      // var types = this.layer.types;
      var data = this.getSelectedRowsData();

      _outFields = this._getOutFieldsFromLayerInfos(pk);
      _outFields = this._processExecuteFields(this.layer.fields, _outFields);

      var options = {};
      if (data && data.length > 0) {
        options.datas = data;
      } else if (this.grid.store instanceof Memory) {
        data = this.grid.store.data;
        options.datas = data;
      }

      //export geometry if shape type of layer is point
      if ('datas' in options && this.layer.geometryType === 'esriGeometryPoint') {
        var datas = lang.clone(options.datas);
        array.forEach(datas, function(d) {
          var geometry = d.geometry;
          if (geometry && geometry.type === 'point') {
            if ('x' in d) {
              d._x = geometry.x;
            } else {
              d.x = geometry.x;
            }

            if ('y' in d) {
              d._y = geometry.y;
            } else {
              d.y = geometry.y;
            }
          }

          delete d.geometry;
        });
        options.datas = datas;

        _outFields = lang.clone(_outFields);
        var name = "";
        if (_outFields.indexOf('x') !== -1) {
          name = '_x';
        } else {
          name = 'x';
        }
        _outFields.push({
          'name': name,
          alias: name,
          format: {
            'digitSeparator': false,
            'places': 6
          },
          show: true,
          type: "esriFieldTypeDouble"
        });
        if (_outFields.indexOf('y') !== -1) {
          name = '_y';
        } else {
          name = 'y';
        }
        _outFields.push({
          'name': name,
          alias: name,
          format: {
            'digitSeparator': false,
            'places': 6
          },
          show: true,
          type: "esriFieldTypeDouble"
        });
      }

      options.fromClient = false;
   // options.withGeometry = this.layer.geometryType === 'esriGeometryPoint';
      options.outFields = _outFields;
      options.formatNumber = false;
      options.formatDate = true;
      options.formatCodedValue = true;
      options.popupInfo = this.layerInfo.getPopupInfo();
      return CSVUtils.exportCSVFromFeatureLayer(
        fileName || this.configedInfo.name,
        this.layer, options);
    },

    toggleColumns: function() {
      if (this.tableCreated) {
        this.grid._toggleColumnHiderMenu();
      }
    },

    changeHeight: function(h) {
      if (this.grid && (h - this.toolbarHeight - this.footerHeight >= 0)) {
        html.setStyle(
          this.grid.domNode,
          "height", (h - this.toolbarHeight - this.footerHeight) + "px"
        );
      }
    },

    // showGraphic: function() {
    //   // if (this.graphicsLayer) {
    //   //   this.graphicsLayer.show();
    //   // }
    // },

    // hideGraphic: function() {
    //   // if (this.graphicsLayer) {
    //   //   this.graphicsLayer.hide();
    //   // }
    // },

    isSupportQueryToServer: function() {
      var hasLayerUrl = this.layer && this.layer.url && this.configedInfo.layer.url;
      var isCSVLayer = this.layer && this.layer.declaredClass === "esri.layers.CSVLayer";
      var isStreamLayer = this.layer && this.layer.declaredClass === "esri.layers.StreamLayer";

      return hasLayerUrl && !isCSVLayer && !isStreamLayer;
    },

    isSupportQueryOnClient: function() {
      var hasLayerUrl = this.layer && this.layer.url && this.configedInfo.layer.url;
      var isCSVLayer = this.layer && this.layer.declaredClass === "esri.layers.CSVLayer";
      var isStreamLayer = this.layer && this.layer.declaredClass === "esri.layers.StreamLayer";

      return !hasLayerUrl || isCSVLayer || isStreamLayer;
    },

    destroy: function() {
      if (!this._destroyed) {
        this.layerInfo = null;
        this.configedInfo = null;
        this.layer = null;

        if (this._selectionHandles) {
          array.forEach(this._selectionHandles, function(sh) {
            if (sh && sh.remove) {
              sh.remove();
            }
          });
        }

        this._closePopup();
        if (this._filterPopup && this._filterPopup.domNode) {
          this._filterPopup.close();
          this._filterPopup = null;
        }

        // if (this.graphicsLayer && this.graphicsLayer.clear) {
        //   this.graphicsLayer.clear();
        //   this.map.removeLayer(this.graphicsLayer);
        // }
        this._dblClickResult = null;
        if (this.grid) {
          this.grid.destroy();
        }
        this.map = null;
        this.nls = null;
        this.relationship = null;
        if (this._currentDef && !this._currentDef.isFulfilled()) {
          this._currentDef.cancel({
            canceledBySelf: true
          });
        }
        if (this._relatedDef && !this._relatedDef.isFulfilled()) {
          this._relatedDef.cancel({
            canceledBySelf: true
          });
        }

        this.inherited(arguments);
      }
    },

    _selectBySelf: function(features) {
      features = features || [];
      if (features.length !== this._selectionResults.length) {
        return false;
      } else {
        return array.every(this._selectionResults, function(sr) {
          return features.indexOf(sr) > -1;
        });
      }
    },

    _updateSelectRowsByFeatures: function(features) {
      var selectedIds = array.map(features, lang.hitch(this, function(f) {
        return f.attributes[this.layer.objectIdField];
      }));
      this._updateSelectRowsByIds(selectedIds);
    },

    _updateSelectRowsByIds: function(selectedIds) {
      if (this.grid && this.tableCreated) {
        this.selectionRows = selectedIds;
        array.forEach(selectedIds, lang.hitch(this, function(id) {
          this.grid.select(id);
        }));
        this.setSelectedNumber();

        this.changeToolbarStatus();
      }
    },

    _removeTable: function() {
      if (this.grid && this.grid.domNode) {
        this.grid.destroy();
        this.grid = null;
      }
      if (this.footer) {
        html.destroy(this.footer);
        this.footer = null;
        this.selectedRowsLabel = null;
      }
      this.tableCreated = false;
    },

    _onMatchingCheckBoxChange: function(status) {
      if (this.tableCreated && !this._relatedQuery) {
        this.cancelThread();
        this.queryByStoreObjectIds = null;
        this.startQuery();
      }
      if (this._requestStatus === 'fulfilled' && this.tableCreated && this._relatedQuery) {
        // this.startQuery(this.grid.store.data.);
        if (status) {
          this.queryByStoreObjectIds = array.map(this.grid.store.data,
          lang.hitch(this, function(item) {
            return item[this.layer.objectIdField];
          }));
          this.startQuery(this.queryByStoreObjectIds);
        } else {
          var selectedIds = this._relatedQueryIds;
          this._relatedQueryIds = []; // empty _relatedQueryIds for query related
          this.queryRecordsByRelationship({
            'layer': this.relatedOriginalInfo.layerObject,
            'selectedIds': selectedIds
          });
        }
      }
      if (!status) {
        this._currentExtent = null;
      }
    },

    _closePopup: function() {
      var sg = this.map.infoWindow.getSelectedFeature();
      var hasSG = sg && this._dblClickResult && sg === this._dblClickResult;
      var sameLayer = sg && sg._layer === this.layer;
      if (this.domNode && (hasSG || sameLayer)) {
        this.map.infoWindow.hide();
        this._dblClickResult = null;
      }
    },

    _getLayerObject: function() {
      var objectDef = this._currentDef = this.layerInfo.getLayerObject();
      function bindSelectionEvents(layer) {
        if (this._selectionHandles) {
          array.forEach(this._selectionHandles, function(sh) {
            if (sh && sh.remove) {
              sh.remove();
            }
          });
        }
        this._selectionHandles = [];

        this._selectionHandles.push(
          on(layer, 'selection-complete', lang.hitch(this, 'onSelectionComplete')));
      }
      return objectDef.then(lang.hitch(this, function(layerObject) {
        var def = new Deferred();
        if (layerObject.declaredClass === "esri.layers.ArcGISImageServiceLayer" ||
          layerObject.declaredClass === "esri.layers.ArcGISImageServiceVectorLayer") {
          var flayer = new FeatureLayer(layerObject.url);
          this.own(on(flayer, "load", lang.hitch(this, function(params) {
            lang.hitch(this, bindSelectionEvents)(params.layer);
            def.resolve(params.layer);
          })));
        } else {
          lang.hitch(this, bindSelectionEvents)(layerObject);
          def.resolve(layerObject);
        }

        return def;
      }));
    },

    _getLayerDifinition: function() {
      if (this._layerDefinition) {
        return when(lang.clone(this._layerDefinition));
      } else {
        return esriRequest({
          url: this.layer.url,
          content: {
            f: 'json'
          },
          handleAs: 'json',
          callbackParamName: 'callback'
        }).then(lang.hitch(this, function(definition) {
          this.setLayerDefinition(definition);
          return this.getLayerDefinition();
        }));
      }
    },

    _getFilterableFields: function(lFields, cFields) {
      return array.filter(lFields, function(lf) {
        return array.some(cFields, function(cf) {
          return lf.name === cf.name &&
            (cf.show || !esriLang.isDefined(cf.show)) &&
            lang.mixin(lf, cf);
        });
      });
    },

    _doQuery: function(normalizedExtent, queryByStoreObjectIds) {
      if (!this.layer) {
        return;
      }
      var pk = this.layer.objectIdField; // primary key always be display

      if (this.isSupportQueryToServer()) {
        this._queryToServer(normalizedExtent, pk, queryByStoreObjectIds);
      } else if (this.isSupportQueryOnClient()) {
        this._queryOnClient(normalizedExtent, pk, queryByStoreObjectIds);
      }
    },

    _queryOnClient: function(normalizedExtent, pk, queryByStoreObjectIds) {
      var json = {};//json has the similay structure with FeatureSet.
      if (this.layer.declaredClass === "esri.layers.StreamLayer") {
        json.features = this.layer.getLatestObservations();
      } else {
        json.features = array.filter(this.layer.graphics, lang.hitch(this, function(feature){
          return !feature.wabIsTemp;
        }));
      }

      if (queryByStoreObjectIds && queryByStoreObjectIds.length > 0) {
        json.features = array.filter(json.features, function(f) {
          return queryByStoreObjectIds.indexOf(f.attributes[this.layer.objectIdField]) > -1;
        }, this);
      }

      var lFields = this.layer.fields;
      var liFields = this.configedInfo.layer.fields;

      if (liFields) {
        //user has open AT setting page and we get this.configedInfo.layer.fields
        json.fields = array.filter(liFields, lang.hitch(this, function(field) {
          if (!esriLang.isDefined(field.show)) { // first open
            field.show = true;
          }
          if (field.name === pk &&
            (field.type === 'esriFieldTypeOID' || field.type === 'esriFieldTypeInteger')) {
            field._pk = true;
          }

          //add esri field type to liField
          for (var i = 0, len = lFields.length; i < len; i++) {
            if (lFields[i].name === field.name && !field.type) {
              field.type = lFields[i].type;
            }
          }
          return field.show || field._pk;
        }));
      } else {
        //user doesn't open AT setting page
        json.fields = array.filter(lFields, lang.hitch(this, function(field) {
          if (!esriLang.isDefined(field.show)) { // first open
            field.show = true;
          }
          if (field.name === pk &&
            (field.type === 'esriFieldTypeOID' || field.type === 'esriFieldTypeInteger')) {
            field._pk = true;
          }
          return field.show || field._pk;
        }));
      }

      var geometries = [];
      var len = json.features.length;
      for (var i = 0; i < len; i++) {
        if (json && json.features && json.features[i] && json.features[i].geometry) {
          geometries.push(json.features[i].geometry);
        }
      }
      if (normalizedExtent && esriConfig.defaults.geometryService && geometries.length > 0) {
        var sr1 = normalizedExtent.spatialReference;
        var sr2 = geometries[0].spatialReference;
        if(sr1.equals(sr2)){
          json.features = array.filter(json.features, lang.hitch(this, function(g) {
            return geometryEngine.intersects(normalizedExtent, g.geometry);
          }));
          this.queryExecute(
            json.fields, json.features.length, false, json, normalizedExtent
          );
        }else{
          var params = new RelationParameters();
          params.geometries1 = geometries;
          params.geometries2 = [normalizedExtent];
          params.relation = RelationParameters.SPATIAL_REL_INTERSECTION;

          var relationDef = this._currentDef = esriConfig.defaults.geometryService.relation(
            params, lang.hitch(this, function(pairs) {
              return pairs;
            }));
          relationDef.then(lang.hitch(this, function(json, pairs) {
            var n = pairs.length;
            //the filtered features
            var gs = [];
            for (var m = 0; m < n; m++) {
              gs.push(json.features[pairs[m].geometry1Index]);
            }
            json.features = gs;
            this.queryExecute(
              json.fields, json.features.length, false, json, normalizedExtent
            );
          }, json), lang.hitch(this, function(err) {
            if (err && err.message !== 'Request canceled') {
              console.error(err);
            }
            this.changeToolbarStatus();
            this.loading.hide();
          }));
        }
      } else {
        //don't filter features
        this.queryExecute(
          json.fields, json.features.length, false, json, normalizedExtent
        );
      }
    },

    /*
    pk: objectIdField
     */
    _queryToServer: function(normalizedExtent, pk, queryByStoreObjectIds) {
      this._getFeatureCount(normalizedExtent, queryByStoreObjectIds)
        .then(lang.hitch(this, function(recordCounts) {
          if (!this.domNode) {
            return;
          }
          if (queryByStoreObjectIds) {// RELATIONSHIPTABLE doesn't care pagination
            this._queryFeatureLayer(
              normalizedExtent, pk, recordCounts, false, queryByStoreObjectIds
            );
          } else {
            var currentLayer = this.layer;
            var maxCount = esriLang.isDefined(currentLayer.maxRecordCount) ?
              currentLayer.maxRecordCount : 1000;
            this._batchCount = Math.min(maxCount, this._defaultBatchCount);

            // some table isn't GIS based, so it have no objectIdField
            // issue#7122
            if (recordCounts <= maxCount || !this.layer.objectIdField) {
              //one request can cover all features, don't need to pages
              this._queryFeatureLayer(
                normalizedExtent, pk, recordCounts, false
              );
            } else {
              var oFields = this._getOutFieldsFromLayerInfos(pk);
              var results = {
                fields: this.layer.fields
              };
              this.layer._recordCounts = recordCounts;
              var supportsPagination = currentLayer.advancedQueryCapabilities &&
                currentLayer.advancedQueryCapabilities.supportsPagination;
              if (supportsPagination) {
                this.queryExecute(oFields, recordCounts, true, results, normalizedExtent
                );
              } else {
                this._getFeatureIds(pk, normalizedExtent)
                  .then(lang.hitch(this, function(objectIds) {
                    if (!this.domNode) {
                      return;
                    }
                    this.layer._objectIds = objectIds;

                    this.queryExecute(oFields, recordCounts, true, results, normalizedExtent);
                  }), lang.hitch(this, function(err) {
                    console.error(err);
                    this.changeToolbarStatus();
                  }));
              }
            }
          }
        }), lang.hitch(this, function(err) {
          console.error(err);
          this.changeToolbarStatus();
        }));
    },

    _getFeatureCount: function(normalizedExtent, queryByStoreObjectIds) {
      var def = new Deferred();
      var qt = new QueryTask(this.configedInfo.layer.url);
      var query = new Query();
      query.returnGeometry = false;
      query.where = this._getLayerFilterExpression();
      if (queryByStoreObjectIds) {
        query.where += ' AND ' + this.layer.objectIdField +
        ' IN (' + queryByStoreObjectIds.join() + ')';
      }

      if (normalizedExtent) {
        query.geometry = normalizedExtent;
      }

      // Because _getAttributeFilter of FeatureLayer will merge definitionExpression
      // to where property of Query,
      // we change to QueryTask to executeForCount.
      // var countDef = this._currentDef = this.layer.queryCount(query);

      var countDef = this._currentDef = qt.executeForCount(
        query,
        lang.hitch(this, function(results) {
          return results;
        })
      );
      countDef.then(lang.hitch(this, function(count) {
        def.resolve(count);
      }), lang.hitch(this, function(err) {
        if (err && err.message !== 'Request canceled') {
          console.error(err);
          console.log("Could not get feature count.");
        }
        this.loading.hide();
        def.reject(err);
      }));

      return def;
    },

    // get all records from server by only one request without pagination
    _queryFeatureLayer: function(normalizedExtent, pk,
      recordCounts, exceededLimit, queryByStoreObjectIds) {
      // function body
      var qt = new QueryTask(this.configedInfo.layer.url);
      var query = new Query();
      query.where = this._getLayerFilterExpression();
      if (queryByStoreObjectIds && pk) {
        query.where += ' AND ' + pk + ' IN (' + queryByStoreObjectIds.join() + ')';
      }
      var oFields = this._getOutFieldsFromLayerInfos(pk);
      if (oFields.length > 0) {
        var oNames = array.map(oFields, function(field) {
          return field.name;
        });
        query.outFields = oNames;
      } else {
        query.outFields = ["*"];
      }
      if (normalizedExtent) {
        query.geometry = normalizedExtent;
        query.spatialRelationship = Query.SPATIAL_REL_INTERSECTS;
      }
      query.outSpatialReference = lang.clone(this.map.spatialReference);

      query.returnGeometry = this.layer.geometryType === 'esriGeometryPoint';
      if (pk) {
        query.orderByFields = [pk + ' ASC'];
      }

      var taskDef = this._currentDef = qt.execute(
        query,
        lang.hitch(this, function(results) {
          return results;
        })
      );

      taskDef.then(lang.hitch(this, function(results) {
        this.queryExecute(oFields, recordCounts, exceededLimit, results, normalizedExtent);
      }), lang.hitch(this, function(err) {
        if (err && err.message !== 'Request canceled') {
          console.error(err);
        }
        this.changeToolbarStatus();
        this.loading.hide();
      }));
    },

    _getFeatureIds: function(pk, normalizedExtent) {
      var def = new Deferred();
      var qt = new QueryTask(this.configedInfo.layer.url);
      var query = new Query();
      query.returnGeometry = false;
      query.returnIdsOnly = true;
      query.where = this._getLayerFilterExpression();
      if (this.layer._orderByFields || pk) {
        query.orderByFields = this.layer._orderByFields || [pk + " ASC"];
      }

      if (normalizedExtent) {
        query.geometry = normalizedExtent;
      }

      // Because _getAttributeFilter of FeatureLayer will merge definitionExpression
      // to where property of Query,
      // we change to QueryTask to executeForCount.
      // var idsDef = this._currentDef = this.layer.queryIds(query);

      var idsDef = this._currentDef = qt.executeForIds(query, lang.hitch(this, function(results) {
        return results;
      }));
      idsDef.then(lang.hitch(this, function(objectIds) {
        def.resolve(objectIds);
      }), lang.hitch(this, function(err) {
        if (err && err.message !== 'Request canceled') {
          console.error(err);
          console.log("Could not get feature Ids");
        }
        def.resolve([]);
      }));

      return def;
    },

    /*
    oFields: out fields
    recordCounts: all records count
    exceededLimit: if true means pagination
    results: FeatureSet like structure
    nExtent: normalize map extent
     */
    queryExecute: function(oFields, recordCounts, exceededLimit, results, nExtent) {
      var data = [];
      var store = null;
      var columns = {};
      if (!this.domNode) {
        return;
      }
      // mixin porperty of field to result.fields
      results.fields = this._processExecuteFields(this.layer.fields, oFields);
      if (exceededLimit) {
        //if exceededLimit, means need pagination. In this case, we create cache store.
        //With cache store, dgrid get data dynamically.
        store = tableUtils.generateCacheStore(
          this.layer,
          recordCounts,
          this._batchCount,
          this._getLayerFilterExpression(),
          nExtent
        );
      } else {
        //If don't need pagination, we create memory store.
        data = array.map(results.features, lang.hitch(this, function(feature) {
          // return lang.clone(feature.attributes);
          var c = lang.clone(feature.attributes);
          return lang.mixin(c, {
            geometry: feature.geometry
          });
        }));

        store = tableUtils.generateMemoryStore(data, this.layer.objectIdField);
      }

      var _typeIdFild = this.layer.typeIdField;
      var _types = this.layer.types;
      var supportOrder = lang.getObject('advancedQueryCapabilities.supportsOrderBy',
        false, this.layer);
      var supportPage = lang.getObject('advancedQueryCapabilities.supportsPagination',
        false, this.layer);
      var supportStatistics = lang.getObject('advancedQueryCapabilities.supportsStatistics',
        false, this.layer);

      // AttributeTable does not work
      //when column name contains special character such as "." and "()"
      columns = tableUtils.generateColumnsFromFields(
        this.layerInfo.getPopupInfo(),
        results.fields, _typeIdFild, _types, (supportOrder && supportPage) || !exceededLimit,
        supportStatistics
      );

      // remove attachments temporary
      if (this.layer.hasAttachments && this.layer.objectIdField && this.configedInfo.showAttachments) {
        // columns.selectionHandle = {
        //   label: "",
        //   className: "selection-handle-column",
        //   hidden: false,
        //   unhidable: true,
        //   filed: "selection-handle-column",
        //   sortable: false,
        //   _cache: {
        //     sortable: false,
        //     statistics: false
        //   }
        // };
        columns.attachmentsColumn = this.formatAttachmentsColumn(); // add attachments column
      }
      //if autoWidth is ture, means no horizontal bar
      //if autoWidth is false, means we have too many fields and maybe need horizontal bar
      var autoWidth = (20 * 1 + 100 * results.fields.length) < html.getMarginBox(this.domNode).w;
      //create dgrid, only one time
      this.createTable(columns, store, recordCounts, autoWidth);
      this._currentExtent = nExtent;

      var selectedFeatures = this.layer.getSelectedFeatures();
      // it seems that grid.select(id) is not always emitting dgrid-select,
      // so persist the selection ids.
      if (this.selectionRows && this.selectionRows.length > 0) {
        this._updateSelectRowsByIds(this.selectionRows);
      } else if (selectedFeatures && selectedFeatures.length > 0) {
        this._updateSelectRowsByFeatures(selectedFeatures);
      } else {
        this.grid.clearSelection();
      }
      this.changeToolbarStatus();

      this.emit('data-loaded');
    },

    formatAttachmentsColumn: function() {
      var column = {
        label: this.nls.attachments,
        className: 'attachments-column',
        hidden: false,
        unhidable: true,
        field: 'at-show-attachments',
        sortable: false,
        _cache: {
          sortable: false,
          statistics: false
        }
      };
      // jshint unused: true
      column.renderCell = lang.hitch(this, function(obj, v, node) {
        /*jshint unused: false*/
        var oid = obj[this.layer.objectIdField];
        this.layer.queryAttachmentInfos(oid, lang.hitch(this, function(infos) {
          var liTemplates = array.map(infos, lang.hitch(this, function(ainfo) {
            var isImage = ainfo.name && (/\.(png|jpg|jpeg|gif)$/gi).test(ainfo.name);
            var fileType = isImage ? 'image-type' : 'file-type';
            var credential = esriId.findCredential(this.layer.url);
            var attachmentUrl = this.layer.url + '/' + oid + '/attachments/' + ainfo.id +
            (credential ? '?token=' + credential.token : "");
            return '<li class="' + fileType + '">' +
            '<a class="jimu-ellipsis" target="_blank" href="' + attachmentUrl + '">' + ainfo.name + '</a>' +
            '</li>';
          }));
          var filesNls = '';
          if(infos.length > 0){
            filesNls = ' ' + this.nls.files;
          }
          var template = '<div class="attachment-infos">' +
              '<div class="show-attachments-div">' +
                '<span class="show-attachments">' + infos.length + filesNls + '</span>' +
              '</div>' +
              '<div class="attachment-popup">' +
                '<div class="attachment-popup-header">' +
                  '<span>' + liTemplates.length + ' ' + this.nls.files + '</span>' +
                  '<span class="close jimu-float-trailing"></span>' +
                '</div>' +
                '<ul class="attachment-popup-content">' +
                  liTemplates.join('') +
                '</ul>' +
              '</div>' +
            '</div>';

          var templateDom = html.toDom(template);

          if(infos.length > 0){
            var showAttachmentsDiv = query('.show-attachments-div', templateDom)[0];
            html.addClass(showAttachmentsDiv, 'has-attachments');
          }

          node.appendChild(templateDom);
          this.own(on(node, 'click', lang.hitch(this, function(evt) {
            var target = evt && evt.target;
            var valid = html.hasClass(target, 'show-attachments-div') ||
                        html.hasClass(target, 'show-attachments') ||
                        html.hasClass(target, 'close');
            if (!valid) {
              return;
            }
            var oldVisibleAttachmentPopup = this._visibleAttachmentPopup;
            var attachmentPopup = query('.attachment-popup', node)[0];
            if (attachmentPopup && liTemplates.length > 0) {
              html.toggleClass(attachmentPopup, 'show');
            }
            if (html.hasClass(attachmentPopup, 'show')) {
              this._visibleAttachmentPopup = attachmentPopup;
            } else {
              this._visibleAttachmentPopup = null;
            }
            if(oldVisibleAttachmentPopup && oldVisibleAttachmentPopup !== attachmentPopup){
              html.toggleClass(oldVisibleAttachmentPopup, 'show');
            }
          })));
        }), lang.hitch(this, function(err) {
          console.error(err);

        }));
      });

      return column;
    },

    createTable: function(columns, store, recordCounts, autoWidthMode) {
      if (this.grid) {
        this.grid.set("store", store);
        this.grid.set('columns', columns);
        this.grid.refresh();
      } else {
        var json = {};
        json.columns = columns;
        json.store = store;
        json.keepScrollPosition = true;
        json.pagingDelay = 1000;//like search delay
        json.allowTextSelection = true;
        json.deselectOnRefresh = false;

        if (!this.layer.objectIdField) {
          json.minRowsPerPage = this.layer.maxRecordCount || 1000;
          json.maxRowsPerPage = this.layer.maxRecordCount || 1000;
          json.selectionMode = 'none';
        }

        var demands = [OnDemandGrid, Selection, ColumnHider, ColumnResizer, ColumnReorder];
        this.grid = new(declare(demands))(json, html.create("div"));
        html.place(this.grid.domNode, this.domNode);
        this.grid.startup();

        if (this.tipNode) {
          html.destroy(this.tipNode);
        }
        // // private preperty in grid
        // // _clickShowSelectedRecords
        // // when these value is true selected rows after refresh complete.
        // this.grid._clickShowSelectedRecords = false;

        // prevent select when press shift key to do multiple selection.
        this.own(on(this.ownerDocument, 'keydown', lang.hitch(this, function(evt) {
          if (this.grid && this.grid.allowTextSelection && evt.shiftKey) {
            this.grid._setAllowTextSelection(false);
          }
        })));
        this.own(on(this.ownerDocument, 'keyup', lang.hitch(this, function() {
          if (this.grid && !this.grid.allowTextSelection) {
            this.grid._setAllowTextSelection(true);
          }
        })));

        if(this.layer.objectIdField) {
          // bind grid evnt

          //show header menu: ASC,DSC,Statistics
          this.own(on(
            this.grid,
            ".dgrid-header .dgrid-cell:click",
            lang.hitch(this, this._onHeaderClick)
          ));

          // toogle visibility of field in columns menu
          this.own(on(
            this.grid,
            'dgrid-columnstatechange',
            lang.hitch(this, this._onColumnStateChange)
          ));

          //select rows
          this.own(on(
            this.grid,
            ".selection-handle-column:click",
            lang.hitch(this, this._onSelectionHandleClick)
          ));

          //dblclick to select one row and show popup on the feature, but not put into selection
          this.own(on(
            this.grid,
            ".dgrid-row:dblclick",
            lang.hitch(this, this._onDblclickRow)
          ));

          //dgrid sort
          this.own(on(this.grid, 'dgrid-sort', lang.hitch(this, function(evt) {
            this.emit('sort', evt);
          })));

          var inMap = this.map.getLayer(this.layer.id);
          if (this.syncSelection && inMap) {
            this.own(on(inMap, 'click', lang.hitch(this, this._onFeaturelayerClick)));
          }
        }
      }

      if (this.layer.objectIdField) {
        this.grid.set('sort', this.layer.objectIdField, false);
      }

      // fix dgrid bug
      // sometimes dgrid-scroller will overload dgrid-header
      if (this.getParent()) {
        var scroller = query('.dgrid-scroller', this.grid.domNode)[0];
        var header = query('.dgrid-header', this.grid.domNode)[0];
        var scrollerStyle = html.getComputedStyle(scroller);
        var headerBox = html.getMarginBox(header);
        var smt = parseInt(scrollerStyle.marginTop, 10);
        if (isFinite(smt) && headerBox && smt < headerBox.h) {
          html.setStyle(scroller, 'marginTop', headerBox.h + 'px');
        }
      }

      if (autoWidthMode) {
        html.addClass(this.domNode, 'auto-width');
      } else {
        html.removeClass(this.domNode, 'auto-width');
      }

      if (this.footer) {
        html.empty(this.footer);
      } else {
        this.footer = html.create('div', {
          'class': 'jimu-widget-attributetable-feature-table-footer'
        }, this.domNode);
      }
      var _footer = this.footer;
      var countLabel = html.create('div', {
        'class': 'dgrid-status self-footer',
        'innerHTML': recordCounts + ' ' +
          (this.layerInfo.isTable ? this.nls.records : this.nls.features) + ' '
      }, _footer);
      this.selectedRowsLabel = html.create('div', {
        'class': 'dgrid-status self-footer',
        'innerHTML': 0 + ' ' + this.nls.selected + ' ',
        'style': {
          display: this.layer.objectIdField ? '' : 'none'
        }
      }, countLabel, 'after');

      var height = html.getStyle(this.domNode, "height");
      this.changeHeight(height);

      this._requestStatus = 'fulfilled';
      this.tableCreated = true;
      html.place(this.loading.domNode, this.grid.domNode);
      this.loading.hide();
    },

    getSelectedRowsData: function() {
      if (!this.grid) {
        return null;
      }

      var oid = this.layer.objectIdField;
      var store = this.grid.store;
      var data = store._entityData || store.data;
      var selectedIds = this.getSelectedRows();

      var rows = array.map(selectedIds, lang.hitch(this, function(id) {
        for (var i = 0, len = data.length; i < len; i++) {
          if (data[i] && data[i][oid] === id) {
            return data[i];
          }
        }
        return {};
      }));

      return rows || [];
    },

    setSelectedNumber: function() {
      if (this.selectedRowsLabel && this.grid) {
        var _ids = this.getSelectedRows();
        var currentViewCount = 0;
        // selected number should change with current records in table.
        if (this.grid.store instanceof Memory) {
          var oids = array.map(this.grid.store.data, function(d) {
            return d[this.layer.objectIdField];
          }, this);
          array.forEach(_ids, function(id) {
            if (oids.indexOf(id) > -1) {
              currentViewCount++;
            }
          });
        } else {// query store
          currentViewCount = _ids.length;
        }
        this.selectedRowsLabel.innerHTML = "  " +
          currentViewCount + " " + this.nls.selected + "  ";
      }
    },

    _setSelection: function(result) {
      result = result || [];
      this._selectionResults = result;
      this.selectionManager.setSelection(this.layer, result);
    },

    _zoomToExtentByFeatures: function(result) {
      return this.getExtent(result).then(lang.hitch(this, function(gExtent) {
        if (gExtent && this.domNode) {
          var def = null;
          if (gExtent.type === "point") {
            // var levelOrFactor = 15;
            // levelOrFactor = this.map.getMaxZoom() > -1 ? this.map.getMaxZoom() : 0.1;
            // // def = this.map.centerAndZoom(gExtent, levelOrFactor);

            var numLevels = this.map.getNumLevels(),
              currentLevel = this.map.getLevel(),
              last = this.map.getMaxZoom(),
              factor = 4;

            if (numLevels > 0) { // tiled base layer
              var targetLevel;
              if (currentLevel === last) {// if currentLevel is maxZoomLevel
                targetLevel = currentLevel;
              }else{
                targetLevel = currentLevel + factor;
                if (targetLevel > last) {
                  targetLevel = last;
                }
              }
              def = this.map.centerAndZoom(gExtent, targetLevel);
            } else { // dynamic base layer
              def = this.map.centerAndZoom(gExtent, (1 / Math.pow(2, factor)) * 2);
            }
          } else {
            def = this.map.setExtent(gExtent.expand(2));
          }

          return def.then((function() {
            return gExtent;
          }));
        }
      }));
    },

    _showMapInfoWindowByFeatures: function(gExtent, result) {
      if (!result || result.length === 0 || !this.domNode) {
        return;
      }
      var popup = this.map.infoWindow;
      var layerInfoTemplate = this.layerInfo.isPopupEnabled() &&
        this.layerInfo.getInfoTemplate();
      if (popup && popup.setFeatures && result.length === 1 && layerInfoTemplate) {
        array.forEach(result, lang.hitch(this, function(item) {
          // sometimes result come from querytask not local feature
          // so we need to bind the _layer and infoTemplate to the result
          item._layer = this.layerInfo.layerObject;
          item.setInfoTemplate(layerInfoTemplate);
        }));
        popup.setFeatures(result);

        var location = null;
        if (gExtent.type === "point") {
          location = result[0].geometry;
        } else {
          location = gExtent.getCenter();
        }
        popup.show(location, {
          closetFirst: true
        });

        this._dblClickResult = result[0];
        this.syncWithMapInfowindow(this._dblClickResult);
      }
    },

    // differect with layer.selectFeatures
    selectFeatures: function(method, result) {
      if (result && result.length > 0) {
        if (method === "rowclick" || method === "selectall") {// ignore selectall
          this._setSelection(result); // call selectionManager
        } else if (method === "zoom" || method === "row-dblclick") {
          this._zoomToExtentByFeatures(result).then(lang.hitch(this, function(gExtent) {
            if (method !== "row-dblclick" || !this.domNode) {
              return;
            }
            this._showMapInfoWindowByFeatures(gExtent, result);
          }), lang.hitch(this, function(err) {
            if (err && err.message !== 'Request canceled') {
              console.error(err);
            }
          }));
        }
        this.setSelectedNumber();
      } else {
        this._popupMessage(this.nls.dataNotAvailable);
      }
    },

    // monitor: feature or [feature]
    syncWithMapInfowindow: function(monitor) {
      var popup = this.map.infoWindow;
      var result = lang.isArray(monitor) ? monitor : [monitor];
      // var sg = monitor;
      on.once(popup, 'hide', lang.hitch(this, function() {
        if (h && h.remove) {
          h.remove();
          h = null;
        }
      }));
      var h = aspect.after(popup, 'show', lang.hitch(this, function() {
        var sg = popup.getSelectedFeature();
        var selectedSg = sg && result[0] === sg;
        var index = -1;
        if(popup.features){
          index = popup.features.indexOf(result[0]);
        }
        if (!selectedSg && index > -1) {
          popup.select(index);
        } else if (this.domNode && !selectedSg) {
          if (h && h.remove) {
            h.remove();
            h = null;
          }
        }
      }));
    },

    getGraphicsFromLocalFeatureLayer: function(ids) {
      var gs = [],
        id,
        idsi;
      var len = ids.length;
      var n = this.layer.graphics.length;
      var objectid = this.layer.objectIdField;
      for (var i = 0; i < len; i++) {
        for (var m = 0; m < n; m++) {
          id = this.layer.graphics[m].attributes[objectid] + "";
          idsi = ids[i] + "";
          if (id === idsi) {
            gs.push(this.layer.graphics[m]);
            break;
          }
        }
      }
      return gs;
    },

    // addGraphics: function(result) {
    //   var symbol, graphic;
    //   var len = result.length;
    //   var outlineSymbol = new SimpleLineSymbol(
    //     SimpleLineSymbol.STYLE_SOLID,
    //     new Color([0, 255, 255]),
    //     2
    //   );
    //   // this.graphicsLayer.clear();

    //   for (var i = 0; i < len; i++) {
    //     var geometry = null;
    //     if (!result.geometry) {
    //       console.error('unable to get geometry of the reocord: ', result);
    //       continue;
    //     } else if (!result.geometry.spatialReference.equals(this.map.spatialReference)) {
    //       console.warn('unable to draw graphic result in different wkid from map');
    //     }
    //     if (result.geometry.type === "point") {
    //       geometry = new Point(result.geometry.toJson());
    //       symbol = lang.clone(this.map.infoWindow.markerSymbol);
    //     } else if (result.geometry.type === "multipoint") {
    //       geometry = new Multipoint(result.geometry.toJson());
    //       symbol = lang.clone(this.map.infoWindow.markerSymbol);
    //     } else if (result.geometry.type === "polyline") {
    //       geometry = new Polyline(result.geometry.toJson());
    //       symbol = outlineSymbol;
    //     } else if (result.geometry.type === "polygon") {
    //       geometry = new Polygon(result.geometry.toJson());
    //       symbol = new SimpleFillSymbol(
    //         SimpleFillSymbol.STYLE_SOLID,
    //         outlineSymbol,
    //         new Color([255, 255, 255, 0.25])
    //       );
    //     }
    //     graphic = new Graphic(geometry, symbol, result.attributes, result.infoTemplate);
    //     // this.graphicsLayer.add(graphic);
    //   }
    // },

    getExtent: function(result) {
      var def = new Deferred();

      var extent, points;
      var len = result.length;
      if (len === 1 && result[0].geometry && result[0].geometry.type === "point") {
        extent = result[0].geometry;
      } else if (len === 1 && !result[0].geometry) {
        def.reject(new Error('AttributeTable.getExtent:: extent was not projected.'));
        return def;
      } else {
        for (var i = 0; i < len; i++) {
          if (!result[i].geometry) {
            console.error('unable to get geometry of the reocord: ', result[i]);
            continue;
          }
          if (result[i].geometry.type === "point") {
            if (!points) {
              points = new Multipoint(result[i].geometry.spatialReference);
              points.addPoint(result[i].geometry);
            } else {
              points.addPoint(result[i].geometry);
            }
            if (i === (len - 1)) {
              extent = points.getExtent();
            }
          } else {
            if (!extent) {
              extent = result[i].geometry.getExtent();
            } else {
              extent = extent.union(result[i].geometry.getExtent());
            }
          }
        }
      }

      if (!extent || !extent.spatialReference) {
        def.reject(new Error("AttributeTable.getExtent:: extent was not projected."));
        return def;
      }

      // convert to map sr
      var sr = this.map.spatialReference;
      if (extent.spatialReference.equals(sr)) {
        def.resolve(extent);
      } else {
        var parameter = new ProjectParameters();
        parameter.geometries = [extent];
        parameter.outSR = sr;

        esriConfig.defaults.geometryService.project(
          parameter,
          lang.hitch(this, function(geometries) {
            if (!this.domNode) {
              return;
            }
            if (geometries && geometries.length) {
              def.resolve(geometries[0]);
            } else {
              def.reject(new Error("AttributeTable.getExtent:: extent was not projected."));
            }
          }), lang.hitch(this, function(err) {
            // projection error
            if (!err) {
              err = new Error("AttributeTable.getExtent:: extent was not projected.");
            }
            def.reject(err);
          }));
      }
      return def;
    },

    // scroll records to view when click feature in map if possible
    // 1. get selected feature objectId
    // 2. calculate row position based on objectId if sorted by objectId ASC:_getIndexOfIdInGrid
    // 3. scroll to the position
    _onFeaturelayerClick: function(evt, fromPopup) {
      if (!this.actived) {
        return;
      }
      var graphic = lang.getObject('graphic', false, evt);
      var attributes = lang.getObject('graphic.attributes', false, evt);
      if (!attributes || !graphic || graphic._layer !== this.layer) {
        return;
      }
      var sg = this.map.infoWindow.getSelectedFeature();
      if (sg && sg._layer === this.layer && !fromPopup) {
        this.map.infoWindow.hide();
      }

      var id = attributes[this.layer.objectIdField];
      if (this.showSelected) {
        this.toggleSelectedRecords();
      }
      // this.clearSelection(false);
      // for now doesn't get index correctly if other fields be sorted
      this._getIndexOfIdInGrid(id).then(lang.hitch(this, function(dataIndex) {
        if (dataIndex === -1) {
          return;
        }

        this.grid.scrollTo({
          x: 0,
          y: this.grid.rowHeight * dataIndex
        });

        if (this.map.infoWindow.features) {
          var index = this.map.infoWindow.features.indexOf(evt.graphic);
          this.map.infoWindow.select(index);
        }

        on.once(this.map.infoWindow, 'selection-change', lang.hitch(this, function() {
          var graphic = this.map.infoWindow.getSelectedFeature();
          var _layer = lang.getObject('_layer', false, graphic);
          if (graphic === evt.graphic || _layer !== this.layer) {
            return;
          }
          this._onFeaturelayerClick({
            graphic: graphic
          }, true);
        }));
        this.syncWithMapInfowindow(evt.graphic);
      }));
    },

    _getIndexOfIdInGrid: function(id) {
      var def = new Deferred();
      var dataIndex = -1;
      var objectIds = lang.getObject('store.objectIds', false, this.grid);

      var compare = null;
      var sort = this.grid.get('sort')[0];

      if (this._relatedQuery) {
        def.resolve(-1);
        return def;
      }

      if (this.grid.store instanceof Memory) {
        var obj = this.grid.store.get(id);
        var data = this.grid.store.data;
        if (!obj) {
          dataIndex = -1;
        }else if (sort && sort.attribute && esriLang.isDefined(sort.descending)) {
          data = lang.clone(data);
          compare = (function(attr, des) {
            return function(a, b) {
              if (a[attr] === b[attr]) {
                return 0;
              }

              return a[attr] < b[attr] ? (des ? 1 : -1) : (des ? -1 : 1);
            };
          })(sort.attribute, sort.descending);
          data.sort(compare);
          data = array.map(data, function(d) {
            return d[this.layer.objectIdField];
          }, this);
          dataIndex = data.indexOf(obj[this.layer.objectIdField]);
        } else {
          dataIndex = data.indexOf(obj);
        }
        def.resolve(dataIndex);
      } else if (objectIds && objectIds.length > 0) {// in this case does't support sort
        dataIndex = objectIds.indexOf(id);
        def.resolve(dataIndex);
      } else {
        var query = new Query();
        query.returnGeometry = false;
        query.where = this._getLayerFilterExpression() + " AND " +
          this.layer.objectIdField + " < " + id;
        // query count cann't get right postion if users ordered by other fields
        if (sort && !(sort.attribute === this.layer.objectIdField && !sort.descending)) {
          def.resolve(-1);
          return def;
        }
        // if (sort && sort.attribute && esriLang.isDefined(sort.descending)) {
        //   query.orderByFields = [sort.attribute + (sort.descending ? " DESC" : " ASC")];
        // }

        // query.outFields = [this.layer.objectIdField, sort.attribute];

        if (this.matchingMap && !this._relatedQuery && this._currentExtent) {
          query.geometry = this._currentExtent;
        }

        var countDef = this._currentDef = this.layer.queryCount(query);
        // var countDef = this._currentDef = this.layer.queryFeatures(query);
        countDef.then(lang.hitch(this, function(count) {
          def.resolve(count);
        }), lang.hitch(this, function(err) {
          def.reject(err);
        }));
      }

      return def;
    },

    // _onRefreshComplete: function(evt) {
    //   if (evt.grid._clickShowSelectedRecords) {
    //     var selectedIds = this.getSelectedRows();
    //     array.forEach(selectedIds, lang.hitch(this, function(id) {
    //       evt.grid.select(id);
    //     }));

    //     evt.grid._clickShowSelectedRecords = false;
    //   }
    // },

    _zoomToSelected: function() {
      if (!this.configedInfo || !this.tableCreated) {
        return;
      }
      var ids = this.getSelectedRows();
      this._goToFeatures(ids, 'zoom');
    },

    _goToFeatures: function(ids, method) {
      if (ids.length === 0) {
        return;
      } else {
        var localGraphics = this.getGraphicsFromLocalFeatureLayer(ids);
        if (this.isSupportQueryToServer() && ids.length !== localGraphics.length) {
          this._queryFeaturesByIds(ids, method);
          return;
        } else {
          //this.isSupportQueryOnClient()
          // or supportQueryToServer but can get graphics in client directly
          this.selectFeatures(
            method,
            localGraphics
          );
        }
      }
    },

    _onDblclickRow: function(evt) {
      if (this.layerInfo &&
        this.layerInfo.isShowInMap()) {
        // this._zoomToSelected();
        var row = this.grid.row(evt);
        var ids = [row.id];
        this._goToFeatures(ids, 'row-dblclick');
      }
    },

    _queryFeaturesByIds: function(ids, mode) {
      var query = new Query();
      query.objectIds = ids;
      query.returnGeometry = true;
      query.outSpatialReference = lang.clone(this.map.spatialReference);
      query.outFields = ['*'];
      var _layer = this.layer;
      var isCSVLayer = _layer.declaredClass === "esri.layers.CSVLayer";
      if (_layer.url && !isCSVLayer) {
        //we always select feature from server if layer has url
        var queryTask = new QueryTask(_layer.url);
        queryTask.execute(
          query,
          lang.hitch(this, function(fset) {
            this.selectFeatures(mode, fset.features);
          }),
          lang.hitch(this, this._errorSelectFeatures)
        );
      } else { // FeatureCollection do query on client
        _layer.selectFeatures(
          query,
          FeatureLayer.SELECTION_NEW,
          lang.hitch(this, this.selectFeatures, mode),
          lang.hitch(this, this._errorSelectFeatures)
        );
      }
    },

    _onHeaderClick: function(evt) {
      var cell = this.grid.cell(evt);
      var column = cell.column;
      var coords = {
        x: evt.pageX,
        y: evt.pageY
      };
      this._showColumnMenu(column, cell, evt.target, coords);
    },

    _showColumnMenu: function(column, cell, target, coords) {
      var info = lang.getObject("_cache", false, column);
      if (!info) {
        return;
      }
      var cm = new Menu({});
      html.addClass(cm.domNode, 'jimu-widget-attributetable-feature-menu');
      var that = this;
      if (info.sortable) {
        var labelArray = [
          this.nls.sortAsc,
          this.nls.sortDes
        ];
        var iconArray = [
          "iconSortAscending",
          "iconSortDescending"
        ];

        array.forEach(labelArray, function(label, idx) {
          var menuItem = new MenuItem({
            "label": label,
            "iconClass": iconArray[idx],
            "baseClass": "menuItemClass",
            onClick: function() {
              if (0 === idx) {
                that.grid.set('sort', column.field, false);
              } else if (1 === idx) {
                that.grid.set('sort', column.field, true);
              }
            }
          });
          cm.addChild(menuItem);
        });
      }
      if (info.statistics) {
        var menuItem = new MenuItem({
          label: this.nls.statistics,
          iconClass: "iconTableStatistics",
          baseClass: "menuItemClass",
          onClick: lang.hitch(this, function() {
            var statInfo = {
              layer: this.layer,
              filterExpression: this._getLayerFilterExpression(),
              fieldNames: [column.field]
            };

            if (this.matchingMap) {// map extent
              statInfo.geometry = this._currentExtent;
            }

            if (this.showSelected) { // only show Selected records
              var selectedIds = this._getSelectedIds();
              statInfo.filterExpression += ' AND ' + this.layer.objectIdField +
              ' IN (' + selectedIds.join() + ')';
            } else {
              //related mode or featureSet(View In Table Action)
              if (this.queryByStoreObjectIds && this.queryByStoreObjectIds.length > 0) {
                statInfo.filterExpression += ' AND ' + this.layer.objectIdField +
                ' IN (' + this.queryByStoreObjectIds.join() + ')';
              }
            }

            this.fieldStatistics = new FieldStatistics();
            this.fieldStatistics.showContentAsPopup(statInfo);
            // on.once(this.FieldStatistics, 'statistics', lang.hitch(this, function(evt) {
            //   this.emit('show-statistics', evt);
            // }));
          })
        });
        cm.addChild(menuItem);
      }

      var childCount = cm.getChildren();

      cm.startup();
      cm._openMyself({
        "target": target,
        delegatedTarget: cell,
        iframe: null,
        "coords": coords
      });

      this.own(on(cm, 'Close', lang.hitch(this, function() {
        var menu = this.get('columnMenu');
        if (menu) {
          menu.destroyRecursive();
          this.set('columnMenu', null);
        }
      })));
      this.set('columnMenu', cm);

      if (childCount.length < 1) {
        on.emit(cm, 'Close');
      }
    },

    _onColumnStateChange: function(evt) {
      if (evt && evt.grid && evt.grid.columns){
        // var visibleFields = array.filter(evt.grid.columns, lang.hitch(this, function(c) {
        //   return !c.hidden;
        // }));
        // var len = visibleFields.length;
        var len = 0;
        for (var p in evt.grid.columns) {
          if (!evt.grid.columns[p].hidden) {
            len++;
          }
        }

        if ((20 * 1 + 100 * len - 1) < html.getMarginBox(this.domNode).w) {
          html.addClass(this.domNode, 'auto-width');
        } else {
          html.removeClass(this.domNode, 'auto-width');
        }
      }
    },

    _onSelectionHandleClick: function() {
      var ids = this._getSelectedIds();
      this.selectionRows = ids;

      this._closePopup();
      if (!this.layerInfo.isTable) {
        if (ids.length > 0) {
          this._goToFeatures(ids, 'rowclick');
        } else {
          this._setSelection([]);
        }
      }

      this.setSelectedNumber();

      this.emit('row-click', {
        table: this,
        selectedIds: ids
      });
    },

    _onExtentChange: function(params) {
      var x = lang.getObject('delta.x', false, params);
      var y = lang.getObject('delta.y', false, params);
      // sometimes click map will emit extent change,
      //so we need to judge whether delta really changed
      var really = (this._clickMap && !params.levelChange) ?
        isFinite(x) && isFinite(y) && (Math.abs(x) > 0 || Math.abs(y) > 0) : true;
      this._clickMap = false;
      if (!really) {
        return;
      }
      if (this.matchingMap && this.actived && (this.layerInfo && !this.layerInfo.isTable)) {
        this.startQuery(this.queryByStoreObjectIds);
      }
    },

    _getLayerFilterExpression: function() {
      // var expr = (this._filterObj && this._filterObj.expr) || "";

      // var mapFilter = this.layerInfo.getFilterOfWebmap();

      // if (expr) {
      //   if (mapFilter) {
      //     return expr + " AND " + mapFilter;
      //   } else {
      //     return expr;
      //   }
      // } else if (mapFilter) {
      //   return mapFilter;
      // }

      var layerFilter = this.layerInfo.getFilter();
      if (layerFilter) {
        return layerFilter;
      }

      return "1=1";
    },

    _getOutFieldsFromLayerInfos: function(pk) {
      var fields = this.configedInfo.layer.fields;
      var oFields = [];
      if (fields) {
        array.forEach(fields, lang.hitch(this, function(field) {
          if (!esriLang.isDefined(field.show)) { // first open
            field.show = true;
          }
          if (field.name === pk &&
            // Fields come from layer.fields
            ((field.type === 'esriFieldTypeOID' || field.type === 'esriFieldTypeInteger') ||
              // Fields come from config.layer.fileds
              !field.type)
          ) {
            field._pk = true;
          }
          if (field.show || field._pk) {
            oFields.push(field);
          }
        }));
      }
      return oFields;
    },

    _processExecuteFields: function(rFields, oFields) {
      if (rFields && rFields.length > 0) {
        var outFields = [];
        if (!oFields.length) {
          return rFields;
        }
        for (var i = 0, len = oFields.length; i < len; i++) {
          for (var j = 0; j < rFields.length; j++) {
            if (oFields[i].name === rFields[j].name &&
              (oFields[i].type === rFields[j].type || // oFields come from layer.fields
                !oFields[i].type) // oFields come from config.layer.fileds
            ) {
              rFields[j] = lang.mixin(rFields[j], oFields[i]);
              outFields.push(rFields[j]);
            }
          }
        }
        return outFields;
      } else {
        return oFields;
      }

      return rFields;
    },

    _getSelectedIds: function() {
      var ids = [];
      var selection = this.grid.selection;
      for (var id in selection) {
        if (selection[id]) {
          if (isFinite(id)) {
            ids.push(parseInt(id, 10));
          } else {
            ids.push(id);
          }
        }
      }

      return ids;
    },

    _errorSelectFeatures: function(params) {
      console.error(params);
    },

    _popupMessage: function(message) {
      var popup = new Message({
        message: message,
        buttons: [{
          label: this.nls.ok,
          onClick: lang.hitch(this, function() {
            popup.close();
          })
        }]
      });

      this.loading.hide();
    }
  });
});
0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Zlatina,

   Sorry I did my testing in 2.7 not 2.6.

In your code above comment out line 877 thru 928. Your line numbers are a little different then mine in WAB 2.6 though mine are lines 945 - 999 (could be that you skipped some lines when you pasted here).

Don't forget to mark this question as answered by clicking on the "Mark Correct" link on the reply that answered your question.

ZlatinaGounev
New Contributor II

Thank you Robert,

Commenting out lines 945-999 did the trick.

Thanks

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Great. Don't forget to mark this question as answered by clicking on the "Mark Correct" link on the reply that answered your question.

0 Kudos
ZlatinaGounev
New Contributor II

I don't see "Mark Correct" option in my view only Helpful (Yes/No), Like, or Report Abuse. 

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

Zlatina,

   You will not see the mark correct option in your inbox you have to open the thread fully to see that button.

0 Kudos