/**
 * The class implements the display of data on a modular CSS grid.
 */
Ext.define("Terrasoft.controls.Grid", {

	alternateClassName: "Terrasoft.Grid",

	extend: "Terrasoft.Component",

	mixins: {
		draggable: "Terrasoft.DraggableGridMixin",
		hierarchical: "Terrasoft.HierarchicalGridMixin"
	},

	/**
  * Indicates the type of layout of the grid.
  * @cfg {String} [type="tiled" "listed"]
  */
	type: null,

	/**
  * The parameter indicates the possibility of multiple sampling.
  * @cfg {Boolean} [multiSelect="false" "true"]
  */
	multiSelect: false,

	/**
  * Indicates the need to use a "zebra" type design, i.e. alternating allocation of rows in color,
  * in the list form of the list.
  * @cfg {Boolean} [listedZebra="false"]
  */
	listedZebra: false,

	/**
  * A parameter that stores data in the form of a flat array of model objects.
  * Filled and used when initializing.
  *
  *      An example of data for a tiled hierarchical structure
  *      data: [
  *          {
  *              id: 1,
  *              title: "Production Department",
  *              personName: "Olga Ravenskaya",
  *              department: "Production"
  *          },
  *          {
  *              id: 2,
  *              title: "Logistics",
  *              personName: "Vyacheslav Pinkov",
  *              department: "Production",
  *              parent: 1
  *          },
  *          {
  *              id: 3,
  *              title: "Forwarding",
  *              personName: "Constantin Constantinovich Constantinovsky",
  *              department: "Escort of goods",
  *              parent: 2
  *          },
  *          {
  *              id: 4,
  *              title: "Forwarding",
  *              personName: "Kola Peninsula",
  *              department: "Escort of goods",
  *              parent: 2
  *          },
  *          {
  *              id: 7,
  *              title: "Forwarding",
  *              personName: "Kola Peninsula",
  *              department: "Escort of goods",
  *              parent: 2
  *          },
  *          {
  *              id: 5,
  *              title: "Production",
  *              personName: "Sergey Filin",
  *              department: "Production",
  *              parent: 1
  *          },
  *          {
  *              id: 6,
  *              title: "Production Department",
  *              personName: "Constantine Constantinovskiy",
  *              department: "Production"
  *          }
  *      ]
  *
  *      Example data for a tiled data structure
  *      data: [
  *          {
  *              icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
  *              title: "Control call to the client, find out the situation about the financing of the department",
  *              responsibleName: "Evgeniy Mirnyi",
  *              responsibleIcon: "data:image/png;base64,iVBORw0KGgo...",
  *              startDate: "26.09.2012 19:30",
  *              priority: "Normal",
  *              status: "In progress"
  *          },
  *          {
  *              icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
  *              title: "Offer a client a reference meeting",
  *              responsibleName: "Svetlana Filimonova",
  *              responsibleIcon: "data:image/png;base64,iVBORw0KGgo...",
  *              startDate: "25.09.2012 12:30",
  *              priority: "Normal",
  *              status: "Canceled",
  *              result: "Canceled",
  *              resultDetailed: "Canceled due to lack of necessity." The client made a decision in our favor",
  *              counteragent: "Alphabusiness",
  *              contact: "Vyacheslav Nosov",
  *              countryIcon: "data:image/png;base64,iVBORw0KGgoAAAA...",
  *              country: "Spain",
  *              influence: "Exhibition",
  *              sale: "101/Alphabusiness/Complex sale"
  *          },
  *          {
  *              icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
  *              title: "Offer a reference meeting to the client",
  *              responsibleName: "Svetlana Filimonova",
  *              responsibleIcon: "data:image/png;base64,iVBORw0KGgo...",
  *              startDate: "28.09.2012 12:30",
  *              priority: "Normal",
  *              status: "Completed",
  *                result: "Meeteing agreed",
  *              resultDetailed: "We agreed to hold a meeting next week"
  *          }
  *      ]
  * @protected
  * @type {Object[]}
  */
	rows: null,

	/**
  * A parameter that stores styles for data records where the key is the identifier of the collection item,
  * and the value is the styles in the form of an object that can process Ext.DomHelper.generateStyles.
  * @type {Object}
  */
	rowsStyles: null,

	/**
  * A parameter that stores classes for certain cells.
  * @type {Object}
  */
	cellsClasses: null,

	/**
  * Link to the collection.
  * @type {Terrasoft.Collection}
  */
	collection: null,

	/**
  * Column configuration.
  * Each array in the configuration is a string in the markup.
  * Each object in the config is a parameterized cell for markup.
  *
  * The cell object has following configuration values ​​and properties:
  *      "cols": can take the values ​​"grid-cols-1", "grid-cols-2", "grid-cols-3" ... "grid-cols-24";
  *      the number of columns in the cell; maximum width of 24 columns, minimum 1 column;
  *      column sizes in fractions of the line width;
  *
  *      "key": a data key object or an array of data key objects; the data key points to the name of the property
  *      from the data set by where you can get the actual data
  *
  *      "key": {
  *          the name of the key itself or the value for the "self-serving" column, for example "caption"; in order to
  *              the corresponding property of the model could be tracked using
  *          "type": can take following values
  *              "text" (can be omitted, used by default),
  *              "title" to use the heading style,
  *              "* icon *" to indicate the icons; the type of icon is also the class of the way icons are displayed;
  *              for example, specifying the type "grid-flag-icon-16x16"
  *              the data source of the column will be substituted into the value
  *              of the link to the image, and the type itself, as the CSS class
  *              will be registered for the container of the displayed icon
  *              "caption" caption to column
  *              data type; is taken into account when the data is directly inserted into the cell;
  *      }
  *
  * One line without content
  *      columnsConfig: [
  *          []
  *      ]
  *
  * Two lines without content
  *      columnsConfig: [
  *          []
  *          []
  *      ]
  *
  * In the first line, two columns occupy 50% of the width.
  *      columnsConfig: [
  *          [
  *              {
  *                  cols: 12
  *              },
  *              {
  *                  cols: 12
  *              }
  *          ],
  *          []
  *      ]
  *
  * Example configuration for hierarchical mapping
  *      columnsConfig: [
  *          [
  *              {
  *                  cols: 24,
  *                  key: [
  *                      {
  *                          name: "title",
  *                          type: "title"
  *                      }
  *                  ]
  *              },
  *          ],
  *          [
  *              {
  *                  cols: 12,
  *                  key: [
  *                      {
  *                          name: "Manager",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "personName"
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 12,
  *                  key: [
  *                      {
  *                          name: "Parent Department",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "department"
  *                      }
  *                  ]
  *              }
  *          ]
  *      ]
  *
  * Example of setting for a tiled display
  *      columnsConfig: [
  *          [
  *              {
  *                  cols: 24,
  *                  key: [
  *                      {
  *                          name: "icon",
  *                          type: "grid-header-icon-22x22"
  *                      },
  *                      {
  *                          name: "title",
  *                          type: "title"
  *                      }
  *                  ]
  *              }
  *          ],
  *          [
  *              {
  *                  cols: 9,
  *                  key: [
  *                      {
  *                          name: "responsibleIcon",
  *                          type: "grid-icon-32x32"
  *                      },
  *                      {
  *                              name: "Responsible",
  *                              type: "caption"
  *                      },
  *                      {
  *                              name: "responsibleName"
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 5,
  *                  key: [
  *                      {
  *                          name: "Start date",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "startDate"
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 5,
  *                  key: [
  *                      {
  *                          name: "Priority",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "priority",
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 5,
  *                  key: [
  *                      {
  *                          name: "Status",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "status"
  *                      }
  *                  ]
  *              }
  *          ],
  *          [
  *              {
  *                  cols: 9,
  *                  key: [
  *                      {
  *                          name: "Result",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "result"
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 15,
  *                  key: [
  *                      {
  *                          name: "Detailed result",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "resultDetailed"
  *                      }
  *                  ]
  *              }
  *          ],
  *          [
  *              {
  *                  cols: 6,
  *                  key: [
  *                      {
  *                          name: "Account",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "counteragent"
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 6,
  *                  key:[
  *                      {
  *                          name: "Contact",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "contact",
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 6,
  *                  key: [
  *                      {
  *                          name: "Country",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "countryIcon",
  *                          type: "grid-flag-icon-16x16"
  *                      },
  *                      {
  *                          name: "country",
  *                      }
  *                  ]
  *              },
  *              {
  *                  cols: 6,
  *                  key: [
  *                      {
  *                          name: "Influence",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "influence"
  *                      }
  *                  ]
  *              }
  *          ],
  *          [
  *              {
  *                  cols: 24,
  *                  key: [
  *                      {
  *                          name: "Sale",
  *                          type: "caption"
  *                      },
  *                      {
  *                          name: "sale"
  *                      }
  *                  ]
  *              }
  *          ]
  *      ]
  * @protected
  * @type {Array}
  */
	columnsConfig: null,

	/**
  * Configure signatures to the columns in list mode.
  * Represents an array of objects, indicating the width of the column and the signature in the created cell.
  * Configuration example
  *      captionsConfig: [
  *          {
  *              cols: 10,
  *              name: "Name",
  *          },
  *          {
  *              cols: 8,
  *            name: "Primary contact",
  *          },
  *          {
  *              cols: 6,
  *              name: "Owner"
  *          }
  *      ]
  * @type {Object}
  */
	captionsConfig: null,

	/**
  * Parameter that stores the configuration of columns and headers.
  * @type {Object}
  */
	listedConfig: null,

	/**
  * A parameter that stores the configuration of the columns.
  * @type {Object}
  */
	tiledConfig: null,

	/**
  * The configuration of the "actions" for the active record
  *      activeRowActions: [
  *          {
  *              className: "Terrasoft.Button",
  *              style: Terrasoft.controls.ButtonEnums.style.BLUE,
  *              caption: "Edit",
  *              tag: "edit"
  *          },
  *          {
  *              className: "Terrasoft.Button",
  *              style: Terrasoft.controls.ButtonEnums.style.GREY,
  *              caption: "Copy",
  *              tag: "copy"
  *          },
  *          {
  *              className: "Terrasoft.Button",
  *              style: Terrasoft.controls.ButtonEnums.style.GREY,
  *              caption: "Delete",
  *              tag: "delete"
  *          }
  *      ]
  * @cfg {Object}
  */
	activeRowActions: null,

	/**
  * A parameter indicating the permission to display actions for the row.
  */
	isRowActionsVisible: true,

	/**
  * The name of the column to determine the value of the data-item-marker attribute,
  * if not specified, primaryDisplayColumn is used.
  * @type {String}
  */
	rowDataItemMarkerColumnName: null,

	/**
  * The prefix of the DOM element identifier used to sign the columns of the list.
  * @protected
  * @property {String} captionPrefix
  */
	captionPrefix: "-caption-",

	/**
  * The CSS class used to highlight the row of the list.
  * @protected
  * @property {String} selectedRowCss
  */
	selectedRowCss: "grid-row-selected",

	/**
  * The markup for the sort identifier is up.
  * @protected
  * @property {String} sortIndicatorUp
  */
	// jscs:disable
	/*jshint quotmark: false */
	sortIndicatorUp: '<div class="grid-sort-arrow grid-sort-arrow-up"></div>',
	/*jshint quotmark: true */
	// jscs:enable

	/**
  * The markup for the sort identifier down.
  * @property {String} sortIndicatorDown
  */
	// jscs:disable
	/*jshint quotmark: false */
	sortIndicatorDown: '<div class="grid-sort-arrow grid-sort-arrow-down"></div>',
	/*jshint quotmark: true */
	// jscs:enable

	/**
  * @type {Number}
  */
	sortColumnIndex: null,

	sortColumnDirection: Terrasoft.core.enums.OrderDirection.ASC,

	/**
  * The "primaryColumnName" value from the collection. It comes from the first model in the collection.
  * @protected
  * @property {String} primaryColumnName
  */
	primaryColumnName: "Id",

	/**
  * The "primaryDisplayColumnName" value from the collection. It comes from the first model in the collection.
  * @protected
  */
	primaryDisplayColumnName: null,

	/**
  * The prefix of the DOM element identifier corresponding to one model from the collection.
  * @protected
  * @property {String} collectionItemPrefix
  */
	collectionItemPrefix: "-item-",

	/**
  * List of #type describing "self-serving" (not subject to external processing,
  * contain everything necessary for its full operation in the configuration object itself)
  * types in the configuration of the #columnsConfig columns.
  * @protected
  * @property {String[]} internalColumns
  */
	internalColumns: ["caption", "label"],

	/**
  * A set of Terrasoft.CheckBoxEdit elements.
  * @protected
  * @type {Array}
  */
	checkboxes: null,

	/**
  * Progress spinner instance.
  * @protected
  * @type {Terrasoft.ProgressSpinner}
  */
	progressSpinner: null,

	/**
  * List of the identifiers of the elements of the collection that are selected.
  * @type {String[]}
  */
	selectedRows: null,

	/**
  * CSS class indicating that the DOM element can be selected.
  * @protected
  * @property {String} theoreticallyActiveRowCss
  */
	theoreticallyActiveRowCss: "grid-active-selectable",

	/**
  * Parameter that stores the identifier of the active record from the received data.
  * @protected
  */
	activeRow: null,

	/**
  * Css class for the active row of the list.
  * @protected
  * @property {String} activeRowCss
  */
	activeRowCss: "grid-row-selected",

	/**
  * The prefix of the DOM element of the action set for the active record.
  * @protected
  * @property {String} actionsRowPrefix
  */
	actionsRowPrefix: "-actions-item-",

	/**
  * Element of the DOM element template of the action set for the active record.
  * @protected
  * @property {Ext.Template} actionsRowTpl
  */
	// jscs:disable
	/*jshint quotmark: false */
	actionsRowTpl: new Ext.Template('<div id="{id}" class="grid-row-actions"></div>'),
	/*jshint quotmark: true */
	// jscs:enable

	/**
  * CSS class for hiding elements.
  * @protected
  * @property {String} hiddenCss
  */
	hiddenCss: "grid-row-hidden",

	/**
  * A set of "action" elements for the active registry entry.
  * @protected
  * @type {Array}
  */
	actionItems: null,

	/**
  * A set of storing links to the rows of the registry.
  * @protected
  * @type {Array}
  */
	theoreticallyActiveRows: null,

	/**
  * The ordinal number of the row from the end of the list which appearance in the visible part of the browser to track.
  * @property {Number} watchRowInViewport
  */
	watchRowInViewport: 0,

	/**
  * @type {Array}
  */
	watchRowHistory: null,

	translate: Terrasoft.Resources.Grid,

	isEmpty: false,

	isEmptyCaption: null,

	isEmptyHtmlConfig: null,

	isEmptyCss: "grid-empty",

	isLoading: false,

	isLoadingHtmlConfig: null,

	/**
  * Indicates the need to add external rows to the registry.
  * @cfg {Boolean} [useRowActionsExternal=false]
  */
	useRowActionsExternal: false,

	/**
  * The prefix for the DOM of the Container ID of the external row action handlers.
  * @protected
  * @property {String} rowActionsExternalPrefix
  */
	rowActionsExternalPrefix: "-external-actions-item-",

	/**
  * CSS class to denote the container element of the external processing of row actions in the DOM.
  * @protected
  * @property {String} rowActionsExternalCss
  */
	rowActionsExternalCss: "grid-row-actions-external",

	/**
  * Indicates the need to add icons for the help fields in the list registry.
  * @cfg {Boolean} [useListedLookupImages=false]
  */
	useListedLookupImages: false,

	// jscs:disable
	/*jshint quotmark: false */
	bottomSpinnerSpaceTpl: new Ext.Template('<span class="grid-bottom-spinner-space"></span>'),
	/*jshint quotmark: true */
	// jscs:enable

	isLoadingSpinners: null,

	hasNestingColumnName: "",

	spinnerRowPrefix: "-spinner-row-",

	/**
  * Listed grid captions row css class.
  * @protected
  */
	captionsCss: "grid-captions",

	/**
  * Grid listed row css class prefix.
  * @protected
  */
	listedRowsCss: "grid-listed-row",

	/**
  * Grid column element css class prefix.
  * @protected
  */
	colsCss: "grid-cols",

	/**
  * If there is a row in the {@link Terrasoft.Grid # columnsConfig columnsConfig} markup row where not all cells
  * are filled with data and this flag is set to false, then the row does not fit into the list markup.
  * @property {Boolean} isEmptyRowVisible
  */
	isEmptyRowVisible: true,

	disabledClass: "grid-disabled",

	/**
  * The object of the custom message about the empty registry.
  * @protected
  * @type {Object}
  */
	emptyMessageControl: null,

	/**
  * Scroll element.
  * @protected
  * @type {Object}
  */
	scrollEl: null,

	/**
  * Scroll element identifier.
  * @type {String}
  */
	scrollElId: null,

	/**
  * Allow scrolling to row that is activated.
  * @type {Boolean}
  */
	allowScrollToActiveRow: false,

	/**
  * Use parent element for positioning.
  * @type {Boolean}
  */
	scrollParent: false,

	/**
  * Default image column name.
  * @type {String}
  */
	defaultImageColumnName: null,

	/**
  * Template of the span cell.
  * @private
  * @type {Array}
  */
	// jscs:disable
	/*jshint quotmark: false */
	_spanCellTemplate: ['<span grid-data-type="{type}"', '<tpl if="direction"> dir="{direction}"</tpl>', '<tpl if="title"> title="{title}"</tpl>', '>{text}</span>'],
	/*jshint quotmark: true */
	// jscs:enable

	/**
  * Compiled template of the span cell.
  * @private
  * @type {Ext.XTemplate}
  */
	_spanCellXTemplate: null,

	/**
  * @inheritdoc Terrasoft.controls.Component
  * @override
  */
	reRender: function (config) {
		if (config && config.byCollection && this.rows) {
			var rowsId = this.rows.map(function (item) {
				return item.Id;
			});
			var collection = this.collection;
			collection = collection && collection.filter(function (item, key) {
				return !Ext.Array.contains(rowsId, key);
			});
			this.initDefaultCollectionItemPrefix();
			this.callParent(arguments);
			this.onCollectionDataLoaded(null, collection, null);
		} else {
			this.callParent(arguments);
		}
	},

	/**
  * @inheritdoc Terrasoft.Component#constructor
  * @protected
  */
	constructor: function (config) {
		this.rows = config.rows || [];
		this.rowsStyles = config.rowsStyles || {};
		this.cellsClasses = config.cellsClasses || {};
		this.listedConfig = config.listedConfig || null;
		this.tiledConfig = config.tiledConfig || null;
		this.columnsConfig = this.initColumnsConfig(config);
		this.captionsConfig = this.initCaptionsConfig(config);
		this.activeRowActions = config.activeRowActions || [];
		this.watchRowInViewport = this.watchRowInViewport || Math.abs(this.watchRowInViewport);
		this.watchRowHistory = [];
		this.primaryColumnName = config.primaryColumnName || this.primaryColumnName;
		this.hierarchicalColumnName = config.hierarchicalColumnName || this.hierarchicalColumnName;
		this.isEmptyCaption = config.isEmptyCaption || this.translate.isEmptyCaption;
		this.isEmptyHtmlConfig = {
			tag: "div",
			cls: "grid-status-message-empty",
			html: this.isEmptyCaption
		};
		this.isLoadingHtmlConfig = {
			tag: "div",
			cls: "grid-status-message-loading"
		};
		this.hasNestingColumnName = config.hasNestingColumnName || this.hasNestingColumnName;
		this.callParent(arguments);
	},

	/**
  * Extends the configuration of the columns to display the pictures of lookup fields.
  * @private
  * @param {Object} row Data to display the registry string.
  * @param {Terrasoft.BaseViewModel} item Collection item.
  * @param {String} columnName Model column.
  */
	addLookupImageInfo: function (row, item, columnName) {
		if (this.type === "listed" && !this.useListedLookupImages) {
			return;
		}
		row.keyImgExtension = row.keyImgExtension || {};
		var entitySchema = item.entitySchema;
		var column = item.columns[columnName];
		var columnPath = column ? column.columnPath : columnName;
		if (entitySchema && columnPath === entitySchema.primaryDisplayColumnName) {
			entitySchema.primaryImageColumnName = entitySchema.primaryImageColumnName || this.defaultImageColumnName;
			var primaryImageColumnName = entitySchema.primaryImageColumnName;
			if (!primaryImageColumnName) {
				return;
			}
			var primaryImageColumnValue = item.get(primaryImageColumnName);
			if (primaryImageColumnValue) {
				row.keyImgExtension[columnName] = item.getSchemaImageUrl(primaryImageColumnName, Terrasoft.ImageSize.IMAGE32X32);
			}
			return;
		}
		column = item.columns[columnName];
		var lookupValue = item.get(columnName);
		var hasImage = column && column.isLookup && lookupValue ? true : false;
		if (hasImage) {
			var modelMethodName = item.defGetLookupImageUrlMethod;
			var modelMethod = item[modelMethodName];
			row.keyImgExtension[columnName] = modelMethod.call(item, columnName, Terrasoft.ImageSize.IMAGE32X32);
		}
	},

	/**
  * Add information about the presence of nested records.
  * @param {Object} row The data to display the list string.
  * @param {Terrasoft.BaseViewModel} item A collection item.
  */
	addHasNestingInfo: function (row, item) {
		this.hasNestingColumnName = item.defHasNestingColumnName || this.hasNestingColumnName;
		var hasNestingColumnName = this.hasNestingColumnName;
		var hasNesting = item.get(hasNestingColumnName);
		row[hasNestingColumnName] = parseInt(hasNesting, 10);
	},

	/**
  * Gets the image size by the predefined data key type.
  * @private
  * @param {Terrasoft.GridKeyType} keyType Data key type.
  * @return {Object} The object with the width and height of the required image.
  */
	getIconSize: function (keyType) {
		var result = Terrasoft.ImageSize.DEFAULT;
		switch (keyType) {
			case Terrasoft.GridKeyType.ICON16:
			case Terrasoft.GridKeyType.ICON16LISTED:
				result = Terrasoft.ImageSize.IMAGE16X16;
				break;
			case Terrasoft.GridKeyType.ICON22LISTED:
			case Terrasoft.GridKeyType.ICON22:
				result = Terrasoft.ImageSize.IMAGE22X22;
				break;
			case Terrasoft.GridKeyType.ICON32LISTED:
			case Terrasoft.GridKeyType.ICON32:
				result = Terrasoft.ImageSize.IMAGE32X32;
				break;
		}
		return result;
	},

	/**
  * Inserts item to DOM.
  * @private
  * @param {Terrasoft.BaseViewModel} item Collection item.
  * @param {Number} [index] Item index.
  */
	insertItem: function (item, index) {
		this.theoreticallyActiveRows = null;
		var row = this.getRow(item);
		var rows = [row];
		var result = [];
		this.renderGrid(result, { rows: rows });
		var resultHtml = "";
		for (var i = 0, c = result.length; i < c; i += 1) {
			resultHtml += Ext.DomHelper.createHtml(result[i]);
		}
		var options = { mode: "bottom" };
		if (index === 0) {
			options.mode = "top";
		} else if (index && Number(index) > 0) {
			var itemToInsertAfter = this.collection.getByIndex(index - 1);
			options.target = itemToInsertAfter.get(this.primaryColumnName);
			options.mode = "after";
		}
		this.addRows(resultHtml, options);
	},

	/**
  * @inheritdoc Terrasoft.Component#init
  * @protected
  */
	init: function () {
		this.callParent(arguments);

		this.addEvents(
		/**
   * @event
   * The row is selected.
   */
		"selectRow",
		/**
   * @event
   * Unselect a row.
   */
		"unSelectRow",
		/**
   * @event
   * Record opening event.
   */
		"openRecord",
		/**
   * @event
   * Sort by column event.
   */
		"sortColumn",
		/**
   * @event
   * Any "action event" with an active list entry.
   */
		"activeRowAction",
		/**
   * @event
   * Event of appearance of the monitored row in the visible part.
   */
		"watchedRowInViewport",
		/**
   * @event
   * Event after row rendering.
   */
		"afterRowRender",
		/**
   * @event
   * Event of opening or closing a branch of the hierarchy.
   * @param {String} levelId
   * @param {Boolean} state
   */
		"updateExpandHierarchyLevels",
		/**
   * @event
   * Click event by reference.
   */
		"linkClick",
		/**
   * @event
   * Middle button click.
   */
		"linkMiddleClick",
		/**
   * @event
   * Событие наведение курсора на ссылку.
   */
		"linkMouseOver",
		/**
   * @event
   * Событие проверки наличия конфигурации для пользовательского сообщения о пустом реестре.
   */
		"getEmptyMessageConfig",
		/**
   * @event
   * The event is called when the drag element crosses the drop-zone.
   */
		"dragOver",
		/**
   * @event
   * The event is called when the drag element is dropped above the drop zone.
   */
		"dragDrop",
		/**
   * @event
   * The event is called when the drag element is released not over the drop zone.
   */
		"invalidDrop");

		this.selectors = {
			wrapEl: ""
		};
		this.classes = {
			wrapEl: ["grid"]
		};
		this.actionItems = [];
		this.checkboxes = [];
		this.selectedRows = [];
		this.expandHierarchyLevels = [];
		this.initDefaultCollectionItemPrefix();
	},

	/**
  * Initializing prefix of the DOM element identifier corresponding to one model from the collection.
  * @protected
  */
	initDefaultCollectionItemPrefix: function () {
		this.collectionItemPrefix = "-item-";
	},

	/**
  * The "dataLoaded" event handler for the Terrasoft.Collection collection.
  * @protected
  * @param {Terrasoft.Collection} collection Collection.
  * @param {Terrasoft.Collection} newItems New items.
  * @param {Object} settings Settings.
  */
	onCollectionDataLoaded: function (collection, newItems, settings) {
		this.theoreticallyActiveRows = null;
		if (!this.rows.length) {
			this.collection = this.collection || collection;
			this.prepareCollectionData();
			this.safeRerender();
			return;
		}
		if (Ext.Object.isEmpty(newItems) || !this.rendered) {
			return;
		}
		var rows = [];
		var options = {
			rows: rows
		};
		newItems.each(function (item) {
			this.prepareCollectionItem(item);
			var row = this.getRow(item);
			rows.push(row);
			if (settings) {
				if (settings.mode !== "top") {
					this.rows.push(row);
				} else {
					this.rows.splice(0, 0, row);
				}
			}
		}, this);
		if (this.hierarchical && !newItems.isEmpty()) {
			var firstItem = newItems.getByIndex(0);
			var firstItemParent = firstItem.get(this.hierarchicalColumnName);
			options[this.hierarchicalColumnName] = firstItemParent;
			if (this.type === "listed" && firstItemParent) {
				var firstItemParentDom = this.getDomRow(firstItemParent);
				var parentLevel = parseInt(firstItemParentDom.getAttribute("level"), 10);
				options.rowLevel = parentLevel + 1;
			}
		}
		var result = [];
		this.renderGrid(result, options);
		var resultHtml = "";
		for (var i = 0, c = result.length; i < c; i += 1) {
			resultHtml += Ext.DomHelper.createHtml(result[i]);
		}
		if (Ext.Object.isEmpty(settings)) {
			settings = {
				mode: "bottom"
			};
		}
		this.addRows(resultHtml, settings);
		this.checkNeedLoadData();
	},

	/**
  * Event handler for "add" event of {@link Terrasoft.Collection}.
  * @protected
  * @param {Terrasoft.BaseViewModel} item Collection item.
  */
	onAddItem: function (item) {
		this.insertItem(item);
	},

	/**
  * Event handler for "replace" event of {@link Terrasoft.Collection}.
  * @protected
  * @param {Object} config Replace information.
  * @param {Terrasoft.BaseViewModel} config.removedItem Removed item.
  * @param {String} config.removedItemKey Removed item key.
  * @param {Terrasoft.BaseViewModel} config.insertedItem Inserted item.
  * @param {String} config.insertedItemKey Inserted item key.
  * @param {Number} config.index Item index.
  */
	onReplaceItem: function (config) {
		this.onDeleteItem(config.removedItem);
		this.insertItem(config.insertedItem, config.index);
		return false;
	},

	/**
  * The event handler for the record update.
  * @protected
  * @param {Terrasoft.BaseViewModel} item Collection item.
  */
	onUpdateItem: function (item) {
		if (!this.rendered) {
			return;
		}
		this.theoreticallyActiveRows = null;
		var row = this.getRow(item);
		this.updateRow(row);
		var rows = [];
		rows.push(row);
		var options = {
			rows: rows
		};
		if (this.hierarchical) {
			this.addAllItemChildren(item, rows);
			this.addHierarchicalOptions(item, options);
		}
		var result = [];
		this.renderGrid(result, options);
		var resultHtml = "";
		for (var i = 0, c = result.length; i < c; i += 1) {
			resultHtml += Ext.DomHelper.createHtml(result[i]);
		}
		var id = item.get(this.primaryColumnName);
		var oldRow = this.getDomRow(id);
		var newRow = oldRow.insertSibling(resultHtml, "after");
		oldRow.replaceWith(newRow);
		this.initCheckboxesEvents();
		this.initActionItems();
		this.initLinkEvents();
	},

	/**
  * Updates row.
  * @protected
  * @param {Object} newRow New row.
  */
	updateRow: function (newRow) {
		var primaryColumnName = this.primaryColumnName;
		var newRowPrimaryColumnValue = newRow[primaryColumnName];
		var rows = this.rows;
		for (var i = 0; i < rows.length; i++) {
			if (rows[i][primaryColumnName] === newRowPrimaryColumnValue) {
				rows[i] = newRow;
				return;
			}
		}
	},

	/**
  * Removes the child elements of the parent from the DOM and adds them to an array of rows.
  * @private
  * @param {BaseViewModel} item A collection item.
  * @param {Array} rows An array of strings.
  */
	addAllItemChildren: function (item, rows) {
		var children = [];
		this.getAllItemChildren(children, item.get(this.primaryColumnName));
		Ext.each(children, function (child) {
			var id = child.get(this.primaryColumnName);
			var rowId = this.id + this.collectionItemPrefix + id;
			this.deleteItemRowActions(id);
			this.deleteItemCheckbox(id);
			this.deleteRow(rowId);
			this.deleteItemHierarchicalToggle(item);
			rows.push(this.getRow(child));
		}, this);
	},

	/**
  * Returns all children elements.
  * @param {Array} result The result that contains the children elements.
  * @param {String} id Identifier.
  * @return {Boolean} Child elements
  */
	getAllItemChildren: function (result, id) {
		var collection = this.collection;
		collection.each(function (item) {
			var parent = item.get(this.hierarchicalColumnName);
			if (id === parent) {
				var itemId = item.get(this.primaryColumnName);
				result.push(item);
				this.getAllItemChildren(result, itemId);
			}
		}, this);
	},

	/**
  * @deprecated
  */
	getAllItemChilds: function (result, id) {
		this.log(Ext.String.format(Terrasoft.Resources.ObsoleteMessages.ObsoleteMethodMessage, "getAllItemChilds", "getAllItemChildren"));
		this.getAllItemChildren(result, id);
	},

	/**
  * The "remove" event handler of the Terrasoft.Collection collection.
  * @protected
  * @param {Terrasoft.BaseViewModel} item A collection item.
  */
	onDeleteItem: function (item) {
		if (!this.rendered) {
			return;
		}
		this.theoreticallyActiveRows = null;
		var id = item.get(this.primaryColumnName);
		var rowId = this.id + this.collectionItemPrefix + id;
		if (this.multiSelect) {
			var currentSelectedRows = Ext.Array.clone(this.selectedRows);
			var selectedRows = Terrasoft.without(currentSelectedRows, id);
			this.setSelectedRows(selectedRows);
		} else {
			if (id === this.activeRow) {
				this.setActiveRow(null);
			}
		}
		this.deleteItemRowActions(id);
		this.deleteItemCheckbox(id);
		this.deleteRow(rowId);
		this.deleteItemFromRows(id);
		this.deleteItemHierarchicalToggle(item);
	},

	/**
  * Deletes the entry with the specified identifier from the array of model objects.
  * @private
  * @param {String} id The unique identifier of the record.
  */
	deleteItemFromRows: function (id) {
		this.rows = Ext.Array.filter(this.rows, function (row) {
			return row.Id !== id;
		}, this);
	},

	deleteItemRowActions: function (id) {
		var actionsRowId = this.id + this.actionsRowPrefix + id;
		this.actionItems.forEach(function (item) {
			if (actionsRowId === item.renderTo.id) {
				item.destroy();
			}
		}, this);
	},

	deleteItemCheckbox: function (id) {
		this.checkboxes.some(function (item) {
			if (item.value === id) {
				item.destroy();
				return true;
			}
		}, this);
	},

	deleteRow: function (id) {
		this.theoreticallyActiveRows = null;
		var element = Ext.get(id);
		if (element) {
			Ext.removeNode(element.dom);
		}
	},

	addRows: function (rows, options) {
		if (!this.rendered) {
			return;
		}
		var mode = options.mode;
		switch (mode) {
			case "top":
				this.addRowsTop(rows);
				break;
			case "after":
				this.addRowsAfter(rows, options);
				break;
			case "child":
				this.addRowsChild(rows, options);
				break;
			case "bottom":
				this.addRowsBottom(rows, options);
				break;
		}
		this.initCheckboxesEvents();
		this.initActionItems();
		this.initDraggable();
		this.initLinkEvents();
	},

	initLinkEvents: function () {
		var wrapEl = this.getWrapEl();
		var gridLinks = wrapEl.select("a:not([id])");
		gridLinks.addListener("mouseover", this.onLinkMouseOver, this);
		gridLinks.each(function (el) {
			el.dom.id = el.id = Terrasoft.Component.generateId();
		}, this);
	},

	onLinkMouseOver: function (event, target) {
		var targetEl = Ext.get(target);
		var root = this.getWrapEl().dom;
		var rowId = this.getRowId(target);
		var link = targetEl.findParent("a", root, true);
		if (link) {
			if (rowId) {
				var options = {
					rowId: rowId,
					columnName: link.getAttribute("data-column"),
					targetId: link.getAttribute("id")
				};
				this.fireEvent("linkMouseOver", options);
			}
		}
	},

	addRowsTop: function (rows) {
		var insertPlace;
		var insertAfter;
		var type = this.type;
		var wrapEl = this.getWrapEl();
		if (type === "tiled") {
			insertAfter = wrapEl;
			insertPlace = "afterBegin";
		} else if (type === "listed") {
			var insertAfterSelect = wrapEl.select("." + this.captionsCss);
			insertAfter = insertAfterSelect.first();
			insertPlace = "afterEnd";
		}
		if (insertAfter) {
			Ext.DomHelper.insertHtml(insertPlace, insertAfter.dom, rows);
		}
	},

	addRowsAfter: function (rows, options) {
		var wrapEl = this.getWrapEl();
		var target = options.target;
		var where = "afterEnd";
		var element = Ext.get(this.id + this.collectionItemPrefix + target, wrapEl);
		var type = this.type;
		var hierarchical = this.hierarchical;
		if (type === "tiled") {
			if (hierarchical) {
				// jscs:disable
				/*jshint quotmark: false */
				var childrenWrapper = element.parent('[class*="-children-"]');
				/*jshint quotmark: true */
				// jscs:enable
				if (childrenWrapper) {
					element = childrenWrapper;
				}
			}
		}
		if (element) {
			Ext.DomHelper.insertHtml(where, element.dom, rows);
		}
	},

	addRowsChild: function (rows, options) {
		if (!this.hierarchical) {
			return;
		}
		var wrapEl = this.getWrapEl();
		var target = options.target;
		var where = "beforeEnd";
		var element = Ext.get(this.id + this.collectionItemPrefix + target, wrapEl);
		var type = this.type;
		if (type === "tiled" && element) {
			var actionsRow = element.select(".grid-row-actions").first();
			if (actionsRow) {
				element = actionsRow;
				where = "afterEnd";
			}
		} else if (type === "listed") {
			where = "afterEnd";
		}
		if (element) {
			Ext.DomHelper.insertHtml(where, element.dom, rows);
		}
	},

	addRowsBottom: function (rows, options) {
		var wrapEl = this.getWrapEl();
		// jscs:disable
		/*jshint quotmark: false */
		var element = wrapEl.select('[class="grid-bottom-spinner-space"]').item(0);
		/*jshint quotmark: true */
		// jscs:enable
		var where = "beforeBegin";
		if (options.hasOwnProperty("spinner") && options.spinner) {
			where = "afterBegin";
		}
		Ext.DomHelper.insertHtml(where, element.dom, rows);
	},

	/**
  * A method that collects data from a collection into a flat array.
  * @protected
  */
	prepareCollectionData: function () {
		var collection = this.collection;
		collection.each(function (item) {
			this.prepareCollectionItem(item);
			var row = this.getRow(item);
			this.rows.push(row);
		}, this);
	},

	/**
  * Prepares collection elements. Adds styles for new row and classes for grid cells. Sets primaryColumnName and
  * primaryDisplayColumnName if necessary.
  * @param {Object} item New element for processing.
  * @protected
  */
	prepareCollectionItem: function (item) {
		this.primaryColumnName = this.primaryColumnName || item.primaryColumnName;
		this.primaryDisplayColumnName = this.primaryDisplayColumnName || item.primaryDisplayColumnName;
		var style = this.getRowStyle(item);
		var cellsClasses = this.getCellsClasses(item);
		this.rowsStyles[item.get(this.primaryColumnName)] = style;
		Ext.apply(this.cellsClasses, cellsClasses);
	},

	/**
  * List cleaning method. Both DOM elements and data stored in the list are deleted, but not in the collection.
  */
	clear: function () {
		this.onDestroy(true);
		this.rows = [];
		this.rowsStyles = {};
		this.cellsClasses = {};
		this.theoreticallyActiveRows = null;
		this.watchRowHistory = [];
		var wrapEl = this.getWrapEl();
		if (wrapEl && this.rendered) {
			this.bottomSpinnerSpaceTpl.append(wrapEl);
		}
	},

	/**
  * @inheritdoc Terrasoft.Component#initDomEvents
  * @protected
  */
	initDomEvents: function () {
		this.callParent(arguments);
		var wrapEl = this.getWrapEl();
		wrapEl.on("click", this.onGridClick, this);
		wrapEl.on("dblclick", this.onGridDoubleClick, this);
		this.debounceWindowScroll = this.debounceWindowScroll || Terrasoft.debounce(this.onWindowScroll, 500);
		var scrollEl = this.getScrollElement(this.scrollElId);
		Ext.EventManager.addListener(scrollEl, "scroll", this.debounceWindowScroll, this);
		if (Ext.isIE9 || Ext.isIE11 || Ext.isChrome || Ext.isSafari || Ext.isOpera) {
			Ext.EventManager.addListener(scrollEl, "mousewheel", this.debounceWindowScroll, this);
		} else if (Ext.isGecko) {
			Ext.EventManager.addListener(scrollEl, "DOMMouseScroll", this.debounceWindowScroll, this);
		} else {
			Ext.EventManager.addListener(scrollEl, "onmousewheel", this.debounceWindowScroll, this);
		}
	},

	/**
  * The browser's event scrolling event handler.
  * @protected
  */
	onWindowScroll: function () {
		if (this.watchRowInViewport) {
			this.checkWatchedRow();
		}
	},

	/**
  * Check watched row visibility in browser window.
  * @protected
  */
	checkWatchedRow: function () {
		var watchRow = this._getWatchRow();
		if (!watchRow) {
			return;
		}
		var watchRowId = watchRow.dom.id;
		var inViewport = this.elementInViewport(watchRow);
		if (inViewport) {
			if (Ext.Array.indexOf(this.watchRowHistory, watchRowId) >= 0) {
				return;
			}
			this.watchRowHistory.push(watchRowId);
			this.fireEvent("watchedRowInViewport", watchRowId);
		}
	},

	/**
  * Checks need load data.
  * Fire event watchedRowInViewport if last row element in the viewport.
  * @protected
  * @return {Boolean} False if not fired watchedRowInViewport event.
  */
	checkNeedLoadData: function () {
		if (!this.watchRowInViewport) {
			return false;
		}
		var watchRow = this._getWatchRow(function (rows) {
			return rows.length - 1;
		});
		if (!watchRow) {
			return false;
		}
		var watchRowId = watchRow.id;
		var inViewport = this.elementInViewport(watchRow);
		if (!inViewport) {
			return false;
		}
		var result = this.fireEvent("watchedRowInViewport", watchRowId);
		return result;
	},

	/**
  * Gets watching row element.
  * @private
  * @param {Function} [watchRowIndexFn] Function calculate watch row index value.
  * @return {null|Ext.Element} Watching row element.
  */
	_getWatchRow: function (watchRowIndexFn) {
		watchRowIndexFn = watchRowIndexFn || Terrasoft.emptyFn;
		var rows = this.hierarchical ? this.getRootDomRows() : this.getDomRows();
		if (!rows || !this.isVisible()) {
			return null;
		}
		var watchRowIndex = watchRowIndexFn.call(this, rows) || rows.length - this.watchRowInViewport;
		if (!rows.length || watchRowIndex < 0) {
			return null;
		}
		var watchRow = Ext.get(rows[watchRowIndex]);
		return watchRow;
	},

	/**
  * Calculates the intersection of the coordinates of the visible part of the browser screen and the specified element.
  * @param {Ext.dom.Element} el
  * @return {Boolean}
  */
	elementInViewport: function (el) {
		if (!el) {
			return false;
		}
		var body = Ext.getBody();
		var bodyViewRegion = body.getViewRegion();
		var elViewRegion = el.getViewRegion();
		return elViewRegion.bottom < bodyViewRegion.bottom;
	},

	/**
  * Returns target's row identifier.
  * @private
  * @param {HTMLElement} target Target element.
  * @returns {String} Row identifier.
  */
	getRowId: function (target) {
		var targetEl = Ext.get(target);
		var root = this.getWrapEl().dom;
		var row = targetEl.findParent("[class*=\"" + this.theoreticallyActiveRowCss + "\"]", root, true);
		var rowId;
		if (row) {
			rowId = row.id.replace(this.id + this.collectionItemPrefix, "");
		}
		return rowId;
	},

	/**
  * The event handler for the registry click.
  * @param {Ext.EventObject} event
  * @param {HTMLElement} target
  */
	onGridClick: function (event, target) {
		if (!this.enabled) {
			event.stopEvent();
			return;
		}
		var bubble = true;
		var type = this.type;
		var targetEl = Ext.get(target);
		var root = this.getWrapEl().dom;
		var rowId = this.getRowId(target);
		var link = targetEl.findParent("a", root, true);
		if (link) {
			if (rowId) {
				bubble = false;
				this.handleLinkClick(rowId, link, event);
			}
		}
		if (this.hierarchical && bubble) {
			var toggleRows = Ext.dom.Query.select("[id*=\"" + this.hierarchicalTogglePrefix + "\"]", root);
			for (var i = 0, c = toggleRows.length; i < c; i += 1) {
				var toggle = toggleRows[i];
				if (target !== toggle) {
					continue;
				}
				event.stopEvent();
				bubble = false;
				var id = toggle.id.replace(this.id + this.hierarchicalTogglePrefix, "");
				this.toggleHierarchyFolding(id);
				break;
			}
		}
		if (type === "listed" && bubble) {
			var caption = targetEl.findParent("[id*=\"" + this.id + this.captionPrefix + "\"]", root, true);
			if (caption) {
				event.stopEvent();
				bubble = false;
				var index = caption.id.replace(this.id + this.captionPrefix, "");
				this.fireEvent("sortColumn", index);
			}
		}
		if (!this.multiSelect && bubble) {
			if (rowId) {
				event.stopEvent();
				bubble = false;
				this.setActiveRow(rowId);
			}
		}
	},

	/**
  * Handles grid link click.
  * @protected
  * @param {String} rowId Row identifier.
  * @param {HTMLElement} link Link html element.
  * @param {Ext.EventObject} event Click event.
  */
	handleLinkClick: function (rowId, link, event) {
		var linkClickResult;
		var href = link.getAttribute("href");
		var columnName = link.getAttribute("data-column");
		/**
   * @event linkClick
   * @param {String} rowId Clicked row identifier.
   * @param {String} href Link href attribute.
   * @param {String} columnName Column name.
   */
		var mouseButton = Terrasoft.getMouseButton(event);
		if (mouseButton === Terrasoft.MouseButton.LEFT) {
			linkClickResult = this.fireEvent("linkClick", rowId, columnName, href);
			if (linkClickResult === false) {
				event.stopEvent();
			}
		}
		if (mouseButton === Terrasoft.MouseButton.MIDDLE) {
			linkClickResult = this.fireEvent("linkMiddleClick", rowId, columnName, href);
			if (linkClickResult === false) {
				event.stopEvent();
			}
		}
	},

	/**
  * Method for obtaining references to all rows of the list in the DOM.
  * @param {Object} root
  * @return {Array|theoreticallyActiveRows}
  */
	getDomRows: function (root) {
		if (!root) {
			var wrapEl = this.getWrapEl();
			if (wrapEl && wrapEl.dom) {
				root = wrapEl.dom;
			}
		}
		if ((!this.theoreticallyActiveRows || !this.theoreticallyActiveRows.length) && root) {
			this.theoreticallyActiveRows = Ext.dom.Query.select("[class*=\"" + this.theoreticallyActiveRowCss + "\"]", root);
		}
		return this.theoreticallyActiveRows;
	},

	/**
  * Returns array of grid root rows.
  * @param {Object} root Root dom element.
  * @returns {Object[]} Root rows array.
  */
	getRootDomRows: function (root) {
		if (!root) {
			var wrapEl = this.getWrapEl();
			if (wrapEl && wrapEl.dom) {
				root = wrapEl.dom;
			}
		}
		var result = [];
		if (root) {
			var selectorString = Ext.String.format("#{0}> .{1}:not([class*='{2}'])", root.id, this.theoreticallyActiveRowCss, this.hierarchicalChildrenPrefix);
			result = Ext.dom.Query.select(selectorString);
		}
		return result;
	},

	/**
  * Returns grid dom rows array.
  * @param {String} id Row id.
  * @return {Ext.dom.Element} Returns a reference to the list row in the DOM.
  */
	getDomRow: function (id) {
		if (!this.rendered || !id) {
			return;
		}
		var root = this.getWrapEl();
		var result = root.select("[id=\"" + this.id + this.collectionItemPrefix + id + "\"]").item(0);
		return result;
	},

	/**
  * Double event click event handler.
  * @param {Ext.EventObject} event
  */
	onGridDoubleClick: function (event) {
		if (!this.enabled) {
			event.stopEvent();
			return;
		}
		var root = this.getWrapEl().dom;
		var theoreticallyActiveRows = Ext.dom.Query.select("[class*=\"" + this.theoreticallyActiveRowCss + "\"]", root);
		for (var i in theoreticallyActiveRows) {
			var row = theoreticallyActiveRows[i];
			if (!event.within(row)) {
				continue;
			}
			event.stopEvent();
			var id = row.id.replace(this.id + this.collectionItemPrefix, "");
			this.fireEvent("openRecord", id);
			if (Ext.isIE8) {
				document.selection.empty();
			}
			return;
		}
	},

	/**
  * @inheritdoc Terrasoft.Component#onAfterRender
  * @protected
  */
	onAfterRender: function () {
		this.callParent(arguments);
		this.initCheckboxesEvents();
		this.initActionItems();
		this.initLinkEvents();
		this.initRowActionsExternal();
		var emptyMessageConfig = this.loadEmptyMessageConfig();
		if (!Ext.Object.isEmpty(emptyMessageConfig)) {
			this.displayIsEmpty(emptyMessageConfig);
		}
		this.mixins.draggable.onAfterRender.apply(this, arguments);
		this.checkNeedLoadData();
	},

	onAfterReRender: function () {
		this.callParent(arguments);
		this.initCheckboxesEvents();
		this.initActionItems();
		this.initLinkEvents();
		this.initRowActionsExternal();
		var emptyMessageConfig = this.loadEmptyMessageConfig();
		if (!Ext.Object.isEmpty(emptyMessageConfig)) {
			this.displayIsEmpty(emptyMessageConfig);
		}
		this.mixins.draggable.onAfterReRender.apply(this, arguments);
		this.checkNeedLoadData();
	},

	onBeforeReRender: function () {
		this.callParent(arguments);
		this.onDestroy(true);
	},

	/**
  * Assignment of checkbox events.
  * @protected
  */
	initCheckboxesEvents: function () {
		var self = this;
		this.checkboxes.forEach(function (item) {
			if (!item.rendering) {
				return;
			}
			item.init();
			item.onAfterRender();
			item.on("click", self.onCheckboxClick, self);
		}, self);
	},

	initActionItems: function () {
		var self = this;
		var list = this.multiSelect ? this.selectedRows : this.activeRow ? [this.activeRow] : [];
		list.forEach(function (item) {
			this.addRowActions(item);
		}, self);
	},

	initRowActionsExternal: function () {
		if (!this.useRowActionsExternal) {
			return;
		}
		var domRows = this.getDomRows();
		domRows.forEach(function (row) {
			var id = row.id.replace(this.id + this.collectionItemPrefix, "");
			var renderTo = Ext.get(row).select("#" + this.id + this.rowActionsExternalPrefix + id + " div").item(0);
			if (id && renderTo) {
				this.fireEvent("afterRowRender", id, renderTo);
			}
		}, this);
	},

	/**
  * The handler of the click event on the checkbox.
  * @param {Terrasoft.CheckBoxEdit} checkbox
  */
	onCheckboxClick: function (checkbox) {
		var value = checkbox.value;
		var checked = checkbox.checked;
		this.setRowSelected(value, checked);
		if (checked) {
			this.addRowActions(value);
			this.fireEvent("selectRow", value);
		} else {
			this.removeRowActions(value);
			this.fireEvent("unSelectRow", value);
		}
		this.fireEvent("rowsSelectionChanged");
	},

	/**
  * Marks selected entries in the list by the list of identifiers newSelectedIds.
  * With records, identifiers of which are not in this list, the selection is removed if it was.
  * @param {String []} newSelectedIds
  */
	setSelectedRows: function (newSelectedIds) {
		if (!Ext.isArray(newSelectedIds)) {
			return;
		}
		var selectedRows = Ext.Array.clone(this.selectedRows);
		var newSelectedLength = newSelectedIds.length;
		var oldSelectedIds = this.selectedRows;
		var oldSelectedLength = oldSelectedIds.length;
		var iterator = 0;
		for (; iterator < oldSelectedLength; iterator++) {
			var oldId = oldSelectedIds[iterator];
			if (!Terrasoft.contains(newSelectedIds, oldId)) {
				this.unselectRow(oldId);
				selectedRows = Terrasoft.without(selectedRows, oldId);
			}
		}
		iterator = 0;
		for (; iterator < newSelectedLength; iterator++) {
			var newId = newSelectedIds[iterator];
			if (!Terrasoft.contains(oldSelectedIds, newId)) {
				this.selectRow(newId);
				selectedRows.push(newId);
			}
		}
		this.selectedRows = selectedRows;
		this.fireEvent("rowsSelectionChanged");
	},

	/**
  * Selects a line in the multiple selection mode.
  * @protected
  * @param {String} id The identifier of the string.
  */
	selectRow: function (id) {
		if (this.rendered) {
			this.setCheckboxChecked(id, true);
			this.addRowActions(id);
		}
		this.fireEvent("selectRow", id);
	},

	/**
  * Removes selection from a line in the multiple selection mode.
  * @protected
  * @param {String} id The identifier of the string.
  */
	unselectRow: function (id) {
		if (this.rendered) {
			this.setCheckboxChecked(id, false);
			this.removeRowActions(id);
		}
		this.fireEvent("unSelectRow", id);
	},

	/**
  * Specifies or deselects the checkbox.
  * @param {String} id The identifier of the record; If the collection is connected,
  * then this is the ID of the entry in the collection.
  * @param {Boolean} checked
  */
	setCheckboxChecked: function (id, checked) {
		if (!this.multiSelect) {
			return;
		}
		var checkboxes = this.checkboxes;
		for (var i = 0, c = checkboxes.length; i < c; i += 1) {
			var checkbox = checkboxes[i];
			if (checkbox.value !== id) {
				continue;
			}
			checkbox.setChecked(checked);
			this.setRowSelected(id, checked);
		}
	},

	/**
  * Specifies or deselects a row.
  * @param {String} id The identifier of the record; If the collection is connected,
  * then this is the ID of the entry in the collection.
  * @param {Boolean} selected
  */
	setRowSelected: function (id, selected) {
		var selectedRows = Ext.Array.clone(this.selectedRows);
		var root = this.getWrapEl().dom;
		var domId = this.id + this.collectionItemPrefix + id;
		var element = Ext.get(Ext.dom.Query.selectNode("#" + domId, root));
		if (selected) {
			element.addCls(this.selectedRowCss);
			selectedRows.push(id);
		} else {
			element.removeCls(this.selectedRowCss);
			selectedRows = Terrasoft.without(this.selectedRows, id);
		}
		this.selectedRows = selectedRows;
	},

	/**
  * Sets active row.
  * @param {String||Object} newRowConfig Row unique identifier or row config.
  * @param {String} newRowConfig.value Row unique identifier.
  * @param {Boolean} newRowConfig.scrollPageToActiveRow Is need to scroll page to active row.
  */
	setActiveRow: function (newRowConfig) {
		var newId = Ext.isObject(newRowConfig) ? newRowConfig.value : newRowConfig;
		var oldId = this.activeRow;
		if (!oldId && !newId) {
			return;
		}
		if (newId !== oldId) {
			var canExecute = oldId && this.canExecute({
				method: this.setActiveRow,
				args: arguments
			});
			if (canExecute === false) {
				return;
			}
			this.deactivateRow(oldId);
			this.activateRow(newId);
			this.activeRow = newId;
			this.scrollPageToActiveRow(newRowConfig);
			this.fireEvent("unSelectRow", oldId);
			this.fireEvent("selectRow", this.activeRow);
		}
	},

	/**
  * Scroll page to active row.
  * @protected
  * @param {Object} newRowConfig New row config.
  * @param {String} newRowConfig.value Row unique identifier.
  * @param {Boolean} newRowConfig.scrollPageToActiveRow Is need to scroll page to active row.
  */
	scrollPageToActiveRow: function (newRowConfig) {
		var value = newRowConfig && newRowConfig.value;
		if (!this.rendered || !value || !newRowConfig.scrollPageToActiveRow) {
			return;
		}
		var row = this.getDomRow(value);
		if (!row) {
			return;
		}
		Terrasoft.setTopScroll(row.getTop());
	},

	/**
  * Activates row.
  * @param {String|Number} id Row id.
  */
	activateRow: function (id) {
		if (!this.rendered || !id) {
			return;
		}
		var row = this.getDomRow(id);
		if (!row) {
			return;
		}
		if (this.allowScrollToActiveRow) {
			var parentEl = this.getWrapEl();
			Terrasoft.utils.dom.scrollToEl(row, parentEl, this.scrollParent);
		}
		row.addCls(this.activeRowCss);
		this.addRowActions(id);
	},

	/**
  * Deactivates row.
  * @param {String|Number} id Row unique identifier.
  */
	deactivateRow: function (id) {
		if (!this.rendered || !id) {
			return;
		}
		var row = this.getDomRow(id);
		if (!row) {
			return;
		}
		row.removeCls(this.activeRowCss);
		this.removeRowActions(id);
	},

	/**
  * Optionally adds or displays a row with "actions" for the active list entry.
  * Accepts the data record ID as a parameter.
  * @protected
  * @param {String | Number} id The identifier of the record.
  */
	addRowActions: function (id) {
		if (!this.activeRowActions.length || !this.isRowActionsVisible) {
			return;
		}
		var actionsRow = Ext.get(this.id + this.actionsRowPrefix + id);
		if (!actionsRow) {
			var renderTo = this.createActionsRow(id);
			if (renderTo) {
				this.renderRowActions(renderTo, id);
			}
		} else {
			actionsRow.removeCls(this.hiddenCss);
		}
	},

	/**
  * Creates a row with "actions" for the active list entry
  * @param {String | Number} id The identifier of the record.
  * @return {HTMLElement | Ext.Element} A row with "actions".
  */
	createActionsRow: function (id) {
		var item = Ext.get(this.id + this.collectionItemPrefix + id);
		if (!item) {
			return;
		}
		var type = this.type;
		var hierarchical = this.hierarchical;
		var renderTo;
		var where = "beforeEnd";
		var el = item.dom;
		var html = this.actionsRowTpl.apply({
			id: this.id + this.actionsRowPrefix + id
		});
		if (this.useRowActionsExternal) {
			var rowActionsExternal = item.select("#" + this.id + this.rowActionsExternalPrefix + id);
			if (rowActionsExternal.getCount()) {
				where = "beforeBegin";
				el = rowActionsExternal.first().dom;
			}
		}
		if (type === "tiled" && hierarchical) {
			var firstChildren = item.child("[class*=\"" + this.hierarchicalChildrenPrefix + "\"]", true);
			if (firstChildren) {
				where = "beforeBegin";
				el = firstChildren;
			}
		}
		var renderToNode = Ext.DomHelper.insertHtml(where, el, html);
		renderTo = Ext.get(renderToNode);
		return renderTo;
	},

	/**
  * Hides the row of "actions" for the active list entry.
  * Accepts the data record ID as a parameter.
  * @protected
  * @param {String | Number} id
  */
	removeRowActions: function (id) {
		if (!this.activeRowActions.length) {
			return;
		}
		var actionsRow = Ext.get(this.id + this.actionsRowPrefix + id);
		if (actionsRow) {
			actionsRow.addCls(this.hiddenCss);
		}
	},

	/**
  * Visualize the "action" elements for an active list entry.
  * @protected
  * @param {HTMLElement / Ext.Element} renderTo
  * @param {String | Number} id
  */
	renderRowActions: function (renderTo, id) {
		var rowActions = Ext.clone(this.activeRowActions);
		var self = this;
		function itemHandler(menu, item) {
			self.onActionItemClick(item.tag, id);
		}
		function actionHandler() {
			self.onActionItemClick(this.tag, id);
		}
		for (var i = 0, c = rowActions.length; i < c; i += 1) {
			var action = rowActions[i];
			action.renderTo = renderTo;
			if (action.hasOwnProperty("menu") && action.menu.hasOwnProperty("items")) {
				var items = action.menu.items;
				if (items && !items.bindTo) {
					for (var j in items) {
						if (!items[j].hasOwnProperty("handler")) {
							items[j].handler = itemHandler;
						}
					}
				}
			} else {
				Ext.merge(action, { listeners: { click: actionHandler } });
			}
			var actionItem = Ext.create(action.className, action);
			if (this.collection) {
				var selectedViewModel = this.collection.get(id);
				actionItem.bind(selectedViewModel);
			}
			actionItem.setEnabled(this.enabled);
			this.actionItems.push(actionItem);
		}
	},

	/**
  * The click handler for the "action" element for the active list entry.
  * @param {String} tag
  * @param {String} id
  */
	onActionItemClick: function (tag, id) {
		var self = this;
		self.fireEvent("activeRowAction", tag, id);
	},

	/**
  * Method for creating the Terrasoft.CheckBoxEdit element.
  * @protected
  * @param {Object} config
  * @return {Terrasoft.CheckBoxEdit}
  */
	createCheckbox: function (config) {
		return Ext.create("Terrasoft.CheckBoxEdit", config);
	},

	/**
  * Returns the template configuration.
  * @protected
  * @param {Array} wrapElClasses Element classes.
  * @param {String} type Type of layout of the grid.
  * @return {Object} Template configuration.
  */
	generateHtmlTpl: function (wrapElClasses, type) {
		var htmlConfig = {
			tag: "div",
			id: "grid-" + this.id + "-wrap",
			cls: wrapElClasses.join(" "),
			children: []
		};
		if (this.isEmpty) {
			var emptyMessageConfig = this.loadEmptyMessageConfig();
			if (Ext.Object.isEmpty(emptyMessageConfig)) {
				htmlConfig.children.push(this.isEmptyHtmlConfig);
			}
			htmlConfig.cls += " " + this.isEmptyCss;
		} else {
			if (type === "listed") {
				htmlConfig.children.push(this.renderCaptionsRow());
			}
			this.renderGrid(htmlConfig.children, {});
		}
		htmlConfig.children.push(this.bottomSpinnerSpaceTpl.apply());
		return htmlConfig;
	},

	/**
  * Returns a item prefix of the collection.
  * @protected
  * @return {String} A item prefix of the collection.
  */
	generateCollectionItemPrefix: function () {
		if (Terrasoft.Features.getIsEnabled("GenerateGridItemPrefixWithGuid")) {
			return Ext.String.format("-{0}{1}", Terrasoft.generateGUID(), this.collectionItemPrefix);
		}
		return this.collectionItemPrefix;
	},

	/**
  * @inheritdoc Terrasoft.Component#generateHtml
  * @protected
  */
	generateHtml: function () {
		if (this.visible !== true) {
			this.tpl = "";
			return "";
		}
		this.collectionItemPrefix = this.generateCollectionItemPrefix();
		this.selectors.wrapEl = "#grid-" + this.id + "-wrap";
		var hierarchical = this.hierarchical;
		var multiSelect = this.multiSelect;
		var type = this.type;
		var wrapElClasses = this.classes.wrapEl;
		var listedZebra = this.listedZebra;
		Ext.Array.remove(wrapElClasses, "grid-tiled");
		Ext.Array.remove(wrapElClasses, "grid-listed");
		Ext.Array.remove(wrapElClasses, "grid-listed-zebra");
		if (type === "tiled") {
			wrapElClasses.push("grid-tiled");
		} else if (type === "listed") {
			wrapElClasses.push("grid-listed");
			if (listedZebra) {
				wrapElClasses.push("grid-listed-zebra");
			}
		}
		Ext.Array.remove(wrapElClasses, "grid-hierarchical");
		if (hierarchical) {
			wrapElClasses.push("grid-hierarchical");
		}
		Ext.Array.remove(wrapElClasses, "grid-multiselect");
		if (multiSelect) {
			wrapElClasses.push("grid-multiselect");
		}
		Ext.Array.remove(wrapElClasses, this.disabledClass);
		if (!this.enabled) {
			wrapElClasses.push(this.disabledClass);
		}
		var htmlConfig = this.generateHtmlTpl(wrapElClasses, type);
		this.tpl = Ext.DomHelper.markup(htmlConfig);
		return this.callParent(arguments);
	},

	/**
  * Метод выбора способа компоновки в зависимости от выбраного типа способа рендеринга по модульной сетке.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderGrid: function (result, options) {
		var hierarchical = this.hierarchical;
		var type = this.type;
		if (hierarchical) {
			if (type === "listed") {
				this.renderListedHierarchicalGrid(result, options);
			} else if (type === "tiled") {
				this.renderTiledHierarchicalGrid(result, options);
			}
		} else {
			if (type === "listed") {
				this.renderListedGrid(result, options);
			} else if (type === "tiled") {
				this.renderTiledGrid(result, options);
			}
		}
	},

	/**
  * Метод создания последовательной верстки для плиточного способа отображения по модульной сетке.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderTiledGrid: function (result, options) {
		var rows = options.rows || this.rows;
		for (var index = 0, length = rows.length; index < length; index += 1) {
			var row = rows[index];
			options.row = row;
			var id = (row[this.primaryColumnName] || index).toString();
			var rowStyles = this.rowsStyles[id];
			var htmlConfig = this.getDefaultRowHtmlConfig(row);
			Ext.apply(htmlConfig, {
				tag: "div",
				cls: "grid-row grid-pad " + this.theoreticallyActiveRowCss,
				style: Ext.DomHelper.generateStyles(rowStyles),
				id: this.id + this.collectionItemPrefix + id,
				children: []
			});
			if (this.multiSelect) {
				var checkbox = this.createCheckbox({
					value: id
				});
				if (Terrasoft.contains(this.selectedRows, id)) {
					htmlConfig.cls += " " + this.selectedRowCss;
					checkbox.setChecked(true);
				}
				htmlConfig.children.push({
					tag: "div",
					cls: "grid-fixed-col",
					html: checkbox.generateHtml()
				});
				this.checkboxes.push(checkbox);
			} else {
				if (this.activeRow === id) {
					htmlConfig.cls += " " + this.activeRowCss;
				}
			}
			this.renderColumns(htmlConfig.children, options);
			if (this.useRowActionsExternal) {
				htmlConfig.children.push({
					tag: "div",
					id: this.id + this.rowActionsExternalPrefix + id,
					cls: this.rowActionsExternalCss,
					children: [{
						tag: "div",
						cls: this.colsCss + "-24"
					}]
				});
			}
			result.push(htmlConfig);
		}
	},

	getDefaultRowHtmlConfig: function (row) {
		var htmlConfig = {};
		var rowDataItemMarkerColumnName = this.rowDataItemMarkerColumnName || this.primaryDisplayColumnName;
		if (rowDataItemMarkerColumnName) {
			var markerValue = row[rowDataItemMarkerColumnName];
			if (!Ext.isEmpty(markerValue)) {
				Ext.apply(htmlConfig, {
					"data-item-marker": this.encodeHtml(markerValue)
				});
			}
		}
		return htmlConfig;
	},

	/**
  * Метод создания последовательной верстки для списочного способа отображения по модульной сетке.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderListedGrid: function (result, options) {
		var rows = options.rows || this.rows;
		for (var index = 0, length = rows.length; index < length; index += 1) {
			var row = rows[index];
			options.row = row;
			var id = (row[this.primaryColumnName] || index).toString();
			var rowStyles = this.rowsStyles[id];
			var htmlConfig = this.getDefaultRowHtmlConfig(row);
			Ext.apply(htmlConfig, {
				tag: "div",
				cls: this.listedRowsCss + " " + this.theoreticallyActiveRowCss,
				style: Ext.DomHelper.generateStyles(rowStyles),
				id: this.id + this.collectionItemPrefix + id,
				children: []
			});
			this.renderColumns(htmlConfig.children, options);
			if (this.multiSelect) {
				htmlConfig.children.unshift({
					tag: "div",
					cls: "grid-fixed-col",
					html: ""
				});
				var fixedCol = htmlConfig.children[0];
				var checkbox = this.createCheckbox({
					classes: {
						wrapClass: ["grid-listed-row-control"]
					},
					value: id
				});
				if (Terrasoft.contains(this.selectedRows, id)) {
					htmlConfig.cls += " " + this.selectedRowCss;
					checkbox.setChecked(true);
				}
				fixedCol.html = checkbox.generateHtml() + htmlConfig.children[0].html;
				this.checkboxes.push(checkbox);
			} else {
				if (this.activeRow === id) {
					htmlConfig.cls += " " + this.activeRowCss;
				}
			}
			result.push(htmlConfig);
		}
	},

	/**
  * The method for starting the data rendering on the row settings file.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderColumns: function (result, options) {
		var columns = this.getColumnsConfig();
		for (var level = 0, length = columns.length; level < length; level += 1) {
			var column = columns[level];
			if (Ext.isArray(column)) {
				options.column = column;
				this.renderRow(result, options);
			} else if (Ext.isObject(column)) {
				options.cell = column;
				this.renderCell(result, options);
			}
		}
	},

	/**
  * Depending on the type, returns the current configuration of the columns.
  * @protected
  * @return {Array}
  */
	getColumnsConfig: function () {
		var type = this.type;
		var config = this[type + "Config"];
		return config ? config.columnsConfig : this.columnsConfig;
	},

	/**
  * Depending on the type, returns the current header configuration.
  * @return {Array}
  */
	getCaptionsConfig: function () {
		var type = this.type;
		var config = this[type + "Config"];
		return config ? config.captionsConfig : this.captionsConfig;
	},

	/**
  * Initializes the initial value of the column configuration.
  * @param {Object} gridConfig
  * @return {Array}
  */
	initColumnsConfig: function (gridConfig) {
		var type = gridConfig.type;
		var config = this[type + "Config"];
		var columnsConfig = (config ? config.columnsConfig : gridConfig.columnsConfig) || [];
		if (!gridConfig.columnsConfig) {
			gridConfig.columnsConfig = columnsConfig;
		}
		return columnsConfig;
	},

	/**
  * Initializes the initial value of the header configuration.
  * @param {Object} gridConfig
  * @return {Array}
  */
	initCaptionsConfig: function (gridConfig) {
		var type = gridConfig.type;
		var config = this[type + "Config"];
		var captionsConfig = (config ? config.captionsConfig : gridConfig.captionsConfig) || [];
		if (!gridConfig.captionsConfig) {
			gridConfig.captionsConfig = captionsConfig;
		}
		return captionsConfig;
	},

	/**
  * Prepares config and render cell.
  * @private
  * @param {Object} renderRowOptions Render row options.
  * @param {Object} column Profile column.
  * @param {Object} htmlConfig Html config.
  * @return {Number} Cell ready state.
  */
	renderRowCell: function (renderRowOptions, column, htmlConfig) {
		renderRowOptions.cell = column;
		if (htmlConfig.cls.indexOf("grid-module") < 0) {
			htmlConfig.cls += " grid-module";
		}
		return this.renderCell(htmlConfig.children, renderRowOptions);
	},

	/**
  * Renders vertical grid cells.
  * @private
  * @param {Object} column Profile column.
  * @param {Number} rowReadyState Current row state.
  * @param {Object} renderRowOptions Render row options.
  * @param {Object} htmlConfig Html config.
  * @return {Number} Current row state
  */
	renderVerticalGridCells: function (column, rowReadyState, renderRowOptions, htmlConfig) {
		Terrasoft.each(column, function (col) {
			rowReadyState += this.renderRowCell(renderRowOptions, col, htmlConfig);
		}, this);
		return rowReadyState;
	},

	/**
  * Render row.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderRow: function (result, options) {
		var rowReadyState = 0;
		var htmlConfig = {
			tag: "div",
			cls: "grid-row",
			children: []
		};
		var verticalCellElementLength = 0;
		for (var level = 0, length = options.column.length; level < length; level += 1) {
			var column = options.column[level];
			var newRenderOptions = Terrasoft.deepClone(options);
			if (Ext.isArray(column)) {
				if (this.isVertical) {
					verticalCellElementLength = column.length;
					rowReadyState = this.renderVerticalGridCells(column, rowReadyState, newRenderOptions, htmlConfig);
				} else {
					newRenderOptions.column = column;
					this.renderRow(htmlConfig.children, newRenderOptions);
					rowReadyState += 1;
				}
			} else if (Ext.isObject(column)) {
				rowReadyState += this.renderRowCell(newRenderOptions, column, htmlConfig);
			}
		}
		if (this.isEmptyRowVisible === false && (!this.isVertical && length > rowReadyState || this.isVertical && verticalCellElementLength > rowReadyState)) {
			return;
		}
		if (rowReadyState > 0) {
			result.push(htmlConfig);
		}
	},

	/**
  * Рендеринг ячейки.
  * @protected
  * @param {Array} result
  * @param {Object} options
  */
	renderCell: function (result, options) {
		var cellReadyState = 0;
		var cell = options.cell;
		var data = options.row;
		var link = this.getDataKey(options.cell.link);
		var styles = {};
		var cls = this.colsCss + "-" + cell.cols;
		if (cell.classes && Ext.isArray(cell.classes)) {
			cls += " " + cell.classes.join(" ");
		}
		var htmlConfig = {
			tag: "div",
			cls: cls,
			html: "",
			children: [],
			"grid-cell-type": []
		};
		if (cell.minHeight) {
			styles["min-height"] = cell.minHeight;
		}
		if (cell.maxHeight) {
			styles["max-height"] = cell.maxHeight;
			styles.overflow = "hidden";
		}
		htmlConfig.style = Ext.DomHelper.generateStyles(styles);
		var key = cell.key;
		if (Ext.isArray(key)) {
			for (var i = 0, length = key.length; i < length; i += 1) {
				var column = key[i];
				cellReadyState += this.formatCellContent(htmlConfig, data, column, link);
			}
		} else if (Ext.isObject(key)) {
			cellReadyState += this.formatCellContent(htmlConfig, data, key, link);
		}
		htmlConfig["grid-cell-type"] = Ext.Array.intersect(htmlConfig["grid-cell-type"]);
		if (htmlConfig["grid-cell-type"].length > 0) {
			htmlConfig["grid-cell-type"] = htmlConfig["grid-cell-type"].join(" ");
		} else {
			delete htmlConfig["grid-cell-type"];
		}
		if (cellReadyState > 0) {
			result.push(htmlConfig);
			return 1;
		} else {
			htmlConfig.html = "";
			result.push(htmlConfig);
			return 0;
		}
	},

	/**
  * HtmlEncode string and replace symbols "{}".
  * @private
  * @param {String} value Changeable string.
  * @return {String} Replaced string.
  */
	encodeHtml: function (value) {
		value = Terrasoft.encodeHtml(value);
		if (Ext.isString(value)) {
			value = value.replace(/{/g, "&#123;");
			value = value.replace(/}/g, "&#125;");
			value = value.replace(/\\/g, "&#92;");
		}
		return value;
	},

	/**
  * Generates html cell-link.
  * @private
  * @param {Object} linkData Link params.
  * @param {String} dataColumn Links data-column attribute.
  * @param {String} innerHtml Links internal content.
  * @return {String} Cell html.
  */
	formatCellLink: function (linkData, dataColumn, innerHtml) {
		// jscs:disable
		/*jshint quotmark: false */
		var linkTpl = '<a href="{0}" target="{1}" title="{2}" data-column="{3}">{4}</a>';
		/*jshint quotmark: true */
		// jscs:enable
		if (linkData.customUrlsExists) {
			var urlOccurrencePattern = "url_occurrence_{0}";
			linkData.customUrls.forEach(function (url, index) {
				innerHtml = innerHtml.replace(this.encodeHtml(url), Ext.String.format(urlOccurrencePattern, index));
			}, this);
			linkData.customUrls.forEach(function (url, index) {
				innerHtml = innerHtml.replace(Ext.String.format(urlOccurrencePattern, index), Ext.String.format(linkTpl, url, linkData.target, this.encodeHtml(url), "", url));
			}, this);
			return innerHtml;
		}
		return Ext.String.format(linkTpl, linkData.url, linkData.target, this.encodeHtml(linkData.title), dataColumn, innerHtml);
	},

	/**
  * Generates html cell-image.
  * @private
  * @param {String} cellImage Image link.
  * @param {String} type Image type.
  * @return {String} Cell html.
  */
	formatCellImage: function (cellImage, type) {
		// jscs:disable
		/*jshint quotmark: false */
		var imageTpl = '<img src="{0}" class="{1}">';
		/*jshint quotmark: true */
		// jscs:enable
		return Ext.String.format(imageTpl, cellImage, this.encodeHtml(type));
	},

	/**
  * Generates html cell.
  * @private
  * @param {String} dataType Cell type.
  * @param {String} cellData Cell text.
  * @return {String} Cell html.
  */
	formatCellSpan: function (dataType, cellData) {
		var spanCellXTemplate = this._getSpanCellXTemplate();
		var valueWithoutTags = Terrasoft.removeHtmlTags(cellData);
		var text = this.encodeHtml(valueWithoutTags);
		var direction = null;
		if (Terrasoft.getIsRtlMode()) {
			direction = Terrasoft.containsRtlChars(cellData) ? direction : "ltr";
		}
		var html = spanCellXTemplate.apply({
			type: dataType,
			text: text,
			direction: direction
		});
		return html;
	},

	/**
  * Gets compiled template of the span cell.
  * @private
  * @return {Ext.XTemplate} Compiled template of the span cell.
  */
	_getSpanCellXTemplate: function () {
		this._spanCellXTemplate = this._spanCellXTemplate || new Ext.XTemplate(this._spanCellTemplate);
		return this._spanCellXTemplate;
	},

	/**
  * Generates html label.
  * @private
  * @param {String} name Label text.
  * @return {String} Cell html.
  */
	formatCellLabel: function (name) {
		// jscs:disable
		/*jshint quotmark: false */
		var labelTpl = '<span class="grid-label">{0}</span>';
		/*jshint quotmark: true */
		// jscs:enable
		return Ext.String.format(labelTpl, this.encodeHtml(name));
	},

	/**
  * Formats data for a cell.
  * @protected
  * @param {Object} cell Cell.
  * @param {Object} data Data.
  * @param {Object} column Configuration.
  * @param {Object} link
  * @return {Number} The amount of data, that was added during the iteration.
  */
	formatCellContent: function (cell, data, column, link) {
		var cellReadyState = 0;
		var name = this.getDataKey(column.name);
		var type = column.type || "text";
		var gridType = this.type;
		var cellData = data[name];
		var cellImage = "";
		if (data.hasOwnProperty("keyImgExtension") && data.keyImgExtension.hasOwnProperty(name) && type !== "label") {
			cellImage = data.keyImgExtension[name];
		} else if (type.indexOf("icon") > -1 && data.hasOwnProperty(name)) {
			cellImage = data[name];
		}
		var linkData;
		if (link && data.hasOwnProperty(link)) {
			linkData = data[link];
		}
		var internalColumn = Ext.Array.contains(this.internalColumns, type);
		var dimensionsRe = new RegExp("\\d+x\\d+", "i");
		if (!cellData && !cellImage && !internalColumn) {
			return cellReadyState;
		}
		var html;
		var size;
		if (type.indexOf("icon") === -1 && cellImage) {
			var lookupImageType = column.lookupImageType || "grid-icon-fixed-32x32";
			size = dimensionsRe.exec(lookupImageType)[0];
			cell.cls += lookupImageType ? " icon-spacer-" + size : " icon-spacer-32x32";
			html = this.formatCellImage(cellImage, lookupImageType);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
		}
		var cellClass = this.cellsClasses[name];
		var dataType = type;
		if (type === "text") {
			if (cellClass && cellClass.typeClass) {
				dataType = cellClass.typeClass;
				cell["grid-cell-type"].push(cellClass.typeClass);
			}
			if (gridType === "tiled" && cell.html.length === 0 && (this.multiSelect || this.hierarchical)) {
				cell.style = "padding-top: 5px;";
			}
			html = this.formatCellSpan(dataType, cellData);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			cellReadyState += 1;
		} else if (type === "caption") {
			if (gridType === "tiled" && cell.html.length === 0 && (this.multiSelect || this.hierarchical)) {
				cell.style = "padding-top: 6px;";
			}
			cell.html += this.formatCellLabel(name);
		} else if (type === "label") {
			html = this.formatCellLabel(name);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			cellReadyState += 1;
		} else if (type.indexOf("grid-icon") > -1 && cellImage) {
			html = this.formatCellImage(cellImage, type);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			size = dimensionsRe.exec(type)[0];
			cell.cls += " icon-spacer-" + size;
			cellReadyState += 1;
		} else if (type.indexOf("flag-icon") > -1 && cellImage) {
			html = this.formatCellImage(cellImage, type);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			cellReadyState += 1;
		} else if (type.indexOf("listed-icon") > -1 && cellImage) {
			html = this.formatCellImage(cellImage, type);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			cellReadyState += 1;
		} else if (type === "title") {
			if (cellClass && cellClass.typeClass) {
				dataType = cellClass.typeClass;
				cell["grid-cell-type"].push(cellClass.typeClass);
			}
			html = this.formatCellSpan(dataType, cellData);
			cell.html += linkData ? this.formatCellLink(linkData, this.encodeHtml(name), html) : html;
			cell.cls += " grid-header";
			cellReadyState += 1;
		} else if (type === "link") {
			var linkHtmlConfig = Ext.DomHelper.createHtml({
				tag: "a",
				target: cellData.target || "_blank",
				html: this.encodeHtml(cellData.caption),
				href: cellData.url,
				"data-column": this.encodeHtml(name)
			});
			cell.html += linkHtmlConfig;
			cellReadyState += 1;
		}
		if (name === this.primaryDisplayColumnName) {
			cell.cls += " grid-primary-column";
		}
		return cellReadyState;
	},

	/**
  * A method for retrieving data from a collection.
  * @protected
  * @param {Object} name
  * @return {*}
  */
	getDataKey: function (name) {
		if (Ext.isObject(name) && name.bindTo) {
			var binding = this.columnBindings[name.bindTo];
			if (binding) {
				name = binding.modelItem;
			}
		}
		return name;
	},

	/**
  * Create an object for rendering the signatures of the columns of the list.
  * @private
  * @return {Object}
  */
	renderCaptionsRow: function () {
		var captions = this.getCaptionsConfig();
		var columns = this.getColumnsConfig();
		var orderDirectionAsc = Terrasoft.core.enums.OrderDirection.ASC;
		var orderDirectionDesc = Terrasoft.core.enums.OrderDirection.DESC;
		var htmlConfig = {
			tag: "div",
			cls: this.captionsCss,
			children: []
		};
		for (var i = 0, c = captions.length; i < c; i += 1) {
			var captionConfig = captions[i];
			var sortIndicator = "";
			if (this.sortColumnIndex === i || captionConfig.sortColumnDirection) {
				if (this.captionsConfig[i].sortColumnDirection) {
					this.sortColumnIndex = i;
					this.sortColumnDirection = captionConfig.sortColumnDirection;
					delete this.captionsConfig[i].sortColumnDirection;
				}
				if (this.sortColumnDirection === orderDirectionAsc) {
					sortIndicator = this.sortIndicatorUp;
				} else if (this.sortColumnDirection === orderDirectionDesc) {
					sortIndicator = this.sortIndicatorDown;
				}
			}
			var caption = {
				tag: "div",
				cls: this.colsCss + "-" + captionConfig.cols,
				html: "<label>" + captionConfig.name + "</label>" + sortIndicator,
				id: this.id + this.captionPrefix + i,
				"data-item-marker": captionConfig.name
			};
			var column = columns[i];
			if (column) {
				var key = column.key;
				var columnName;
				if (Ext.isArray(key)) {
					columnName = this.getDataKey(key[0].name);
				} else if (Ext.isObject(key)) {
					columnName = this.getDataKey(key.name);
				}
				var cellClass = this.cellsClasses[columnName];
				if (cellClass && cellClass.typeClass) {
					caption["grid-caption-type"] = cellClass.typeClass;
				}
			}
			htmlConfig.children.push(caption);
		}
		return htmlConfig;
	},

	/**
  t * Assign a sortable column and a visual update of the sort indicator. The parameter is the column number.
  t * The column count starts from zero.
  * @param {Number} index
  */
	setSortColumnIndex: function (index) {
		if (this.sortColumnIndex === index || index == null) {
			return;
		}
		this.updateSortColumn(index, this.sortColumnDirection);
		this.sortColumnIndex = index;
	},

	/**
  * Assign a sorting direction and visual update of the sort indicator.
  * The index of the sorted column should already be assigned.
  * @param {Terrasoft.core.enums.OrderDirection} direction Sort direction.
  */
	setSortColumnDirection: function (direction) {
		if (this.sortColumnDirection === direction || direction == null) {
			return;
		}
		this.updateSortColumn(this.sortColumnIndex, direction);
		this.sortColumnDirection = direction;
	},

	/**
  * Visual removal of column sort indicator.
  * @private
  */
	removeSortColumnIndicator: function () {
		if (!this.rendered) {
			return;
		}
		var sortColumnIndex = this.sortColumnIndex;
		var root = this.getWrapEl();
		if (sortColumnIndex != null) {
			var captionElSelect = root.select("[id=\"" + this.id + this.captionPrefix + sortColumnIndex + "\"]");
			var captionEl = captionElSelect.first();
			if (captionEl) {
				var captionHtml = captionEl.getHTML();
				var regExpSortIndicatorDown = new RegExp(this.sortIndicatorDown, "igm");
				var regExpSortIndicatorUp = new RegExp(this.sortIndicatorUp, "igm");
				var newHtml = captionHtml.replace(regExpSortIndicatorDown, "");
				newHtml = newHtml.replace(regExpSortIndicatorUp, "");
				captionEl.setHTML(newHtml);
			}
		}
	},

	/**
  * Arrange the sort indicator to a specific column.
  * @param {Number} index
  * @param {Terrasoft.core.enums.OrderDirection} direction Sorting direction.
  */
	addSortColumnIndicator: function (index, direction) {
		if (!this.rendered) {
			return;
		}
		var root = this.getWrapEl();
		var captionEl = root.select("[id=\"" + this.id + this.captionPrefix + index + "\"]").item(0);
		if (captionEl) {
			var captionHtml = captionEl.getHTML();
			var sortIndicator = "";
			if (direction === Terrasoft.core.enums.OrderDirection.ASC) {
				sortIndicator = this.sortIndicatorUp;
			} else if (direction === Terrasoft.core.enums.OrderDirection.DESC) {
				sortIndicator = this.sortIndicatorDown;
			}
			var newHtml = captionHtml + sortIndicator;
			captionEl.setHTML(newHtml);
		}
	},

	/**
  * Consistently: delete the "old" sort indicator and add a new one according to the incoming parameters.
  * @param {Number} index
  * @param {Terrasoft.core.enums.OrderDirection} direction Sorting direction.
  */
	updateSortColumn: function (index, direction) {
		if (!this.rendered) {
			return;
		}
		this.removeSortColumnIndicator();
		this.addSortColumnIndicator(index, direction);
	},

	/**
  * A method that modifies the registry status attribute: empty, not empty.
  * @param {Boolean} newState
  */
	setIsEmpty: function (newState) {
		if (this.isEmpty === newState) {
			return;
		}
		this.isEmpty = newState;
		this.displayIsEmpty();
	},

	/**
  * A method that synchronizes the state attribute and its external view.
  * @protected
  */
	displayIsEmpty: function (emptyMessageConfig) {
		if (!this.rendered) {
			return;
		}
		var wrapEl = this.getWrapEl();
		if (this.isEmpty) {
			this.clear();
			wrapEl.addCls(this.isEmptyCss);
			if (Ext.Object.isEmpty(emptyMessageConfig)) {
				emptyMessageConfig = this.loadEmptyMessageConfig();
			}
			if (!Ext.Object.isEmpty(emptyMessageConfig)) {
				this.emptyMessageControl = Ext.create(emptyMessageConfig.className, Ext.apply({}, emptyMessageConfig, {
					renderTo: wrapEl
				}));
			} else {
				// jscs:disable
				/*jshint quotmark: false */
				var element = wrapEl.select('[class="grid-bottom-spinner-space"]').item(0);
				/*jshint quotmark: true */
				// jscs:enable
				var html = Ext.DomHelper.createHtml(this.isEmptyHtmlConfig);
				Ext.DomHelper.insertHtml("beforeBegin", element.dom, html);
			}
		} else {
			// jscs:disable
			/*jshint quotmark: false */
			var statusEl = wrapEl.select('[class*="grid-status-message"]').item(0);
			/*jshint quotmark: true */
			// jscs:enable
			if (statusEl) {
				wrapEl.removeCls(this.isEmptyCss);
				statusEl.destroy();
			}
		}
	},

	/**
  * Checks for the presence of a configuration for a custom message about an empty list.
  * If there is an external configuration, it returns it, otherwise it returns an empty object.
  * @private
  * @return {Object}
  */
	loadEmptyMessageConfig: function () {
		var emptyMessageConfig = {};
		this.fireEvent("getEmptyMessageConfig", emptyMessageConfig);
		if (emptyMessageConfig && emptyMessageConfig.className) {
			return Ext.clone(emptyMessageConfig);
		}
		return {};
	},

	/**
  * A method that modifies the list state attribute: data is loaded\data is not loaded.
  * @param {Boolean} newState
  */
	setIsLoading: function (newState) {
		var state;
		var options;
		if (Ext.isObject(newState)) {
			state = newState.state;
			options = newState.options;
		} else {
			state = newState;
			options = {
				mode: "bottom"
			};
			if (this.isLoading === state) {
				return;
			}
		}
		this.isLoading = state;
		this.displayIsLoading(options);
	},

	/**
  * A method that synchronizes the state attribute and its external view.
  * @protected
  */
	displayIsLoading: function (options) {
		if (!this.rendered) {
			return;
		}
		if (this.isLoading) {
			var spinnerRow = this.createSpinnerRow(options);
			if (!spinnerRow) {
				return;
			}
			options.spinner = true;
			this.addRows(spinnerRow, options);
		} else {
			var spinnerId = this.createProgressSpinnerId(options);
			this.deleteRow(spinnerId);
		}
	},

	createSpinnerRow: function (options) {
		var wrapEl = this.getWrapEl();
		var domId = this.createProgressSpinnerId(options);
		var rowAlreadyPresent = wrapEl.select("#" + domId);
		if (rowAlreadyPresent.getCount()) {
			return;
		}
		var type = this.type;
		var htmlConfig;
		var mode = options.mode;
		if (type === "tiled" && mode !== "bottom") {
			htmlConfig = {
				tag: "div",
				cls: "grid-row grid-pad",
				id: domId,
				children: [{
					tag: "div",
					cls: "grid-row grid-module"
				}]
			};
			htmlConfig.children[0].html = this.createProgressSpinner();
		} else if (type === "listed" && mode !== "bottom") {
			htmlConfig = {
				tag: "div",
				cls: this.listedRowsCss,
				id: domId
			};
			htmlConfig.html = this.createProgressSpinner();
		} else {
			htmlConfig = this.isLoadingHtmlConfig;
			htmlConfig.id = domId;
			htmlConfig.html = this.createProgressSpinner();
		}
		return Ext.DomHelper.markup(htmlConfig);
	},

	/**
  * Creates progress spinner html.
  * @return {String} Progress spinner html.
  */
	createProgressSpinner: function () {
		this.progressSpinner = this.progressSpinner || Ext.create("Terrasoft.ProgressSpinner");
		return this.progressSpinner.generateHtml();
	},

	createProgressSpinnerId: function (options) {
		var keyParts = Terrasoft.compact([options.mode, options.target]);
		var key = keyParts.join("-");
		return [this.id, this.spinnerRowPrefix, key].join("");
	},

	/**
  * Delete the list and its components.
  * @protected
  * @param {Boolean} clear
  */
	onDestroy: function (clear) {
		if (this.progressSpinner && !this.progressSpinner.destroyed) {
			this.progressSpinner.destroy();
		}
		if (this.dragDropGroupName) {
			this.mixins.draggable.onDestroy.apply(this, arguments);
		}
		if (this.checkboxes && this.checkboxes.length > 0) {
			this.checkboxes.forEach(function (element) {
				element.destroy();
			}, this);
			this.checkboxes = [];
		}
		if (this.actionItems && this.actionItems.length > 0) {
			this.actionItems.forEach(function (element) {
				if (element.destroyed !== true) {
					element.destroy();
				}
			}, this);
			this.actionItems = [];
		}
		if (this.emptyMessageControl) {
			this.emptyMessageControl.destroy();
			this.emptyMessageControl = null;
		}
		var wrapEl = this.getWrapEl();
		if (!clear) {
			if (wrapEl) {
				wrapEl.un("click", this.onGridClick, this);
				wrapEl.un("dblclick", this.onGridDoubleClick, this);
			}
			if (this.debounceWindowScroll) {
				var scrollEl = this.getScrollElement();
				Ext.EventManager.removeListener(scrollEl, "scroll", this.debounceWindowScroll, this);
				if (Ext.isIE9 || Ext.isIE11 || Ext.isChrome || Ext.isSafari || Ext.isOpera) {
					Ext.EventManager.removeListener(scrollEl, "mousewheel", this.debounceWindowScroll, this);
				} else if (Ext.isGecko) {
					Ext.EventManager.removeListener(scrollEl, "DOMMouseScroll", this.debounceWindowScroll, this);
				} else {
					Ext.EventManager.removeListener(scrollEl, "onmousewheel", this.debounceWindowScroll, this);
				}
			}
		}
		if (wrapEl) {
			wrapEl.setHTML("");
		}
		if (!clear) {
			this.callParent(arguments);
		}
	},

	/**
  * An object that describes the relationship of the columns of the list with the properties or methods of the model.
  * @protected
  * @type {Object}
  */
	columnBindings: null,

	/**
  * Returns grid row data by bindings.
  * @protected
  * @param {Terrasoft.BaseViewModel} item Collection item.
  * @return {Object} Grid row.
  */
	getRow: function (item) {
		var bindings = this.columnBindings;
		var row = {};
		var primaryColumnName = this.primaryColumnName;
		var hierarchicalColumnName = this.hierarchicalColumnName;
		row[primaryColumnName] = item.get(primaryColumnName);
		var primaryDisplayColumnName = this.primaryDisplayColumnName;
		if (primaryDisplayColumnName) {
			row[primaryDisplayColumnName] = item.get(primaryDisplayColumnName);
		}
		if (this.hierarchical) {
			var hierarchicalColumn = item.get(hierarchicalColumnName);
			if (hierarchicalColumn) {
				if (Ext.isObject(hierarchicalColumn) && hierarchicalColumn.hasOwnProperty("value")) {
					row[hierarchicalColumnName] = hierarchicalColumn.value;
				} else if (Ext.isString(hierarchicalColumn)) {
					row[hierarchicalColumnName] = hierarchicalColumn;
				}
			}
		}
		for (var property in bindings) {
			var binding = bindings[property];
			if (!binding.bindingType) {
				var bindingType = this.getBindingType(property, binding, item);
				binding.bindingType = bindingType;
			}
			var value = this.getBindingValue(binding, item);
			var columnName = binding.modelItem;
			var type = item.getColumnDataType(columnName);
			var displayValue = value;
			if (type !== Terrasoft.DataValueType.CUSTOM_OBJECT && binding.bindingType === Terrasoft.BindingType.PROPERTY) {
				this.addLookupImageInfo(row, item, columnName);
				this.addHasNestingInfo(row, item, columnName);
				var column = item.columns[columnName];
				if (binding.imageSize || type === Terrasoft.DataValueType.IMAGELOOKUP) {
					var modelMethodName = item.defGetLookupImageUrlMethod;
					var modelMethod = item[modelMethodName];
					displayValue = modelMethod.call(item, columnName, binding.imageSize);
				} else if (column && column.isNotFound) {
					displayValue = this.translate.columnNotFound;
				} else {
					var precision = this.getColumnPrecision(item, columnName);
					displayValue = Terrasoft.getTypedStringValue(value, type, { decimalPrecision: precision });
				}
			}
			row[columnName] = displayValue;
		}
		return row;
	},

	/**
  * Returns column decimal precision.
  * @private
  * @param {Terrasoft.BaseViewModel} item Collection item.
  * @param {String} columnName Item column name.
  * @return {Null|Number} Column decimal precision.
  */
	getColumnPrecision: function (item, columnName) {
		var column = item.getColumnByName(columnName);
		return Ext.isEmpty(column) ? null : column.precision;
	},

	/**
  * A method for retrieving the styles from a collection item.
  * @protected
  * @param {Object} item Collection item.
  * @return {{}}
  */
	getRowStyle: function (item) {
		var customStyle = {};
		if (item.hasOwnProperty("customStyle")) {
			customStyle = item.customStyle;
		}
		return customStyle;
	},

	/**
  * Method for obtaining classes of cells based on data types.
  * @protected
  * @param {Object} item Cell configuration.
  * @return {Object} Additional classes for the cell.
  */
	getCellsClasses: function (item) {
		var cellsStyles = {};
		var bindings = this.columnBindings;
		Terrasoft.each(bindings, function (binding) {
			var columnName = binding.modelItem;
			var column = item.columns[columnName];
			if (column && column.isNotFound) {
				cellsStyles[columnName] = {
					typeClass: "columnNotFound"
				};
				return;
			}
			var type = item.getColumnDataType(columnName);
			switch (type) {
				case Terrasoft.DataValueType.INTEGER:
				case Terrasoft.DataValueType.FLOAT:
				case Terrasoft.DataValueType.MONEY:
					cellsStyles[columnName] = {
						typeClass: "number"
					};
					break;
			}
		}, this);
		return cellsStyles;
	},

	/**
  * Initializes the binding of the columns of the list based on the configuration object.
  * @protected
  * @param {Object} columnsConfig The configuration object of the column binding.
  */
	initColumnBindings: function (columnsConfig) {
		var bindings = this.columnBindings;
		for (var key in columnsConfig) {
			var configItem = columnsConfig[key];
			if (Ext.isArray(configItem)) {
				this.initColumnBindings(configItem);
			} else {
				for (var propertyName in configItem) {
					var item = configItem[propertyName];
					if (Ext.isArray(item)) {
						this.initColumnBindings(item);
					} else if (Ext.isObject(item) && item.bindTo) {
						var binding = this.initBinding(propertyName, item);
						if (binding) {
							binding.config.isColumnConfig = true;
							var GridKeyType = Terrasoft.GridKeyType;
							if (propertyName.type === GridKeyType.ICON16 || propertyName.type === GridKeyType.ICON22 || propertyName.type === GridKeyType.ICON32 || propertyName.type === GridKeyType.ICON16LISTED || propertyName.type === GridKeyType.ICON22LISTED || propertyName.type === GridKeyType.ICON32LISTED) {
								binding.imageSize = this.getIconSize(propertyName.type);
							}
							bindings[item.bindTo] = binding;
						}
					}
				}
			}
		}
	},

	/**
  * @inheritdoc Terrasoft.Bindable#initBinding
  * @protected
  */
	initBindings: function (config) {
		this.callParent(arguments);
		this.columnBindings = {};
		var columnsConfig = config.columnsConfig;
		this.initColumnBindings(columnsConfig);
	},

	/**
  * Returns the configuration of the binding to the model. Implements the {@link Terrasoft.Bindable} mixin interface.
  * @protected
  */
	getBindConfig: function () {
		var bindConfig = this.callParent(arguments);
		var gridBindConfig = {
			collection: {
				changeMethod: "onCollectionDataLoaded"
			},
			selectedRows: {
				changeEvent: "rowsSelectionChanged",
				changeMethod: "setSelectedRows"
			},
			activeRow: {
				changeEvent: "selectRow",
				changeMethod: "setActiveRow"
			},
			isRowActionsVisible: {
				changeMethod: "setIsRowActionsVisible"
			},
			isEmpty: {
				changeMethod: "setIsEmpty"
			},
			isLoading: {
				changeMethod: "setIsLoading"
			},
			multiSelect: {
				changeMethod: "setMultiSelect"
			},
			sortColumnIndex: {
				changeEvent: "sortColumn",
				changeMethod: "setSortColumnIndex"
			},
			sortColumnDirection: {
				changeMethod: "setSortColumnDirection"
			},
			type: {
				changeMethod: "setType"
			},
			expandHierarchyLevels: {
				changeEvent: "updateExpandHierarchyLevels",
				changeMethod: "setExpandHierarchyLevel"
			},
			isEmptyRowVisible: {
				changeMethod: "setIsEmptyRowVisible"
			},
			listedZebra: {
				changeMethod: "setListedZebra"
			}
		};
		Ext.apply(gridBindConfig, bindConfig);
		return gridBindConfig;
	},

	/**
  * Assigns the {@link Terrasoft.Grid # listedZebra listedZebra} value to the parameter.
  * @param {Boolean} newState
  */
	setListedZebra: function (newState) {
		if (this.listedZebra === newState) {
			return;
		}
		this.listedZebra = newState;
		this.activeRow = null;
		this.selectedRows = [];
		this.init();
		this.safeRerender();
	},

	/**
  * Assigns a value to the {@link Terrasoft.Grid # is # isEmptyRowVisible is Empty Row Visible} parameter.
  * @param {Boolean} newState
  */
	setIsEmptyRowVisible: function (newState) {
		if (this.isEmptyRowVisible === newState) {
			return;
		}
		this.isEmptyRowVisible = newState;
	},

	/**
  * Assigns a value to the {@link Terrasoft.Grid # is # isRowActionsVisible isRowActionsVisible} parameter.
  * @param {Boolean} newState new value
  */
	setIsRowActionsVisible: function (newState) {
		if (this.isRowActionsVisible === newState) {
			return;
		}
		this.isRowActionsVisible = newState;
	},

	setMultiSelect: function (newState) {
		if (this.multiSelect === newState) {
			return;
		}
		this.multiSelect = newState;
		this.activeRow = null;
		this.selectedRows = [];
		this.init();
		this.safeRerender({ byCollection: true });
	},

	setType: function (newType) {
		if (this.type === newType) {
			return;
		}
		this.type = newType;
		this.activeRow = null;
		this.selectedRows = [];
		this.init();
		this.safeRerender();
	},

	/**
  * @inheritdoc Terrasoft.Bindable#setControlPropertyValue
  * @protected
  */
	setControlPropertyValue: function (binding, value, model) {
		if (binding.config.isColumnConfig) {
			this.onUpdateRow(model);
		} else {
			this.callParent(arguments);
		}
	},

	/**
  * @inheritdoc Terrasoft.Bindable#subscribeForCollectionEvents
  * @protected
  */
	subscribeForCollectionEvents: function (binding, property, model) {
		this.callParent(arguments);
		var collection = model.get(binding.modelItem);
		collection.on("dataLoaded", this.onCollectionDataLoaded, this);
		collection.on("add", this.onAddItem, this);
		collection.on("remove", this.onDeleteItem, this);
		collection.on("itemChanged", this.onUpdateItem, this);
		collection.on("clear", this.clear, this);
		collection.on("replace", this.onReplaceItem, this);
	},

	unSubscribeForCollectionEvents: function (binding, property, model) {
		if (!model) {
			// TODO: 193528
			return;
		}
		this.callParent(arguments);
		var collection = model.get(binding.modelItem);
		collection.un("dataLoaded", this.onCollectionDataLoaded, this);
		collection.un("add", this.onAddItem, this);
		collection.un("remove", this.onDeleteItem, this);
		collection.un("itemChanged", this.onUpdateItem, this);
		collection.un("clear", this.clear, this);
	},

	/**
  * @inheritdoc Terrasoft.Component#setEnabled
  * @override
  */
	setEnabled: function (enabled) {
		if (this.enabled === enabled) {
			return;
		}
		this.callParent(arguments);
		this.checkboxes.forEach(function (element) {
			element.setEnabled(enabled);
		}, this);
		this.actionItems.forEach(function (element) {
			element.setEnabled(enabled);
		}, this);
	},

	/**
  * Returns scroll element.
  * @protected
  * @param {String} id Scroll element identifier.
  * @return {Object} Scroll element.
  */
	getScrollElement: function (id) {
		if (id && Ext.isString(id)) {
			this.scrollEl = Ext.get(id);
		}
		var scrollEl = this.scrollEl = this.scrollEl || window;
		return scrollEl;
	}
});