/**
 * Class "Drop-down list"
 */
Ext.define("Terrasoft.controls.ListView", {

	extend: "Terrasoft.Component",

	alternateClassName: "Terrasoft.ListView",

	/**
  * Data to display in the menu.
  * @protected
  */
	listItems: [],

	/**
  * Elements of DOM list of menu elements Ext.dom.CompositeElement
  * @protected
  */
	listEls: [],

	/**
  * Link to a focused list item.
  * @type {Ext.CompositeElementLite/Ext.CompositeElement}
  * @protected
  */
	selectedItem: null,

	/**
  * CSS style class of the selected menu item.
  * @protected
  * @property {String} selectedCssClass
  */
	selectedCssClass: "listview-selected",

	/**
  * Download display control.
  * @protected
  * @type {Terrasoft.ProgressSpinner}
  */
	progressSpinner: null,

	/**
  * A parameter indicating the need to display the download indicator.
  * @property {Boolean} selectedCssClass
  */
	showProgressSpinner: false,

	/**
  * Text signature to the download indicator.
  * @property {String} progressSpinnerCaption
  */
	progressSpinnerCaption: Terrasoft.Resources.Controls.ProgressSpinner.Caption,

	/**
  * Parameter indicating that the list will be displayed with icons.
  * @protected
  * @property {Boolean} hasIcons
  */
	hasIcons: false,

	/**
  * The height of one menu item from the list.
  * @protected
  * @property {String} rowHeight
  */
	rowHeight: "40px",

	/**
  * The value of adjusting the height of the list display relative to the parent container.
  * @protected
  * @property {Number} correctTopPosition
  */
	correctTopPosition: 1,

	/**
  * Index of las item in the list before request to get new data.
  * @private
  * @property {Number} indexOfLastLoadedItem
  */
	indexOfLastLoadedItem: null,

	/**
  * Schema of the ratio of data keys between external and internal formats.
  * We assume that external data will be submitted in a certain format, and the configuration of the structure bypass will be made through the correspondence schema.
  *
  * If the external data schema has following a structure..
  *     var externalData = [
  *         {
  *             Id: 1,
  *             City: "Washington"
  *         },
  *         {
  *             Id: 2,
  *             City: "Paris",
  *             IconSprite: "od-icon-16х16"
  *         },
  *         {
  *             Id: 3,
  *             City: "London",
  *             imageConfig: {
  *                 source: Terrasoft.ImageSources.URL,
  *                 url: "http://goo.gl/776gtdOur"
  *             }
  *         }
  *     ];
  * then the relationship schema will look as follows:
  *     listview.map = {
  *         // specify the key, in external data, which stores data for the value of the drop-down list item
  *         value: "Id",
  *         // specify the key, in external data, which stores data for display in the drop-down list and correlates with its value
  *         displayValue: "City",
  *         // key, in external data, indicating the stored value of the CSS class
  *         imageClass: "IconSprite",
  *         // key, in external data indicating the stored value of the image configuration
  *         imageConfig: "imageConfig"
  *     };
  *
  * @cfg map
  * @cfg map.value Value key.
  * @cfg map.displayValue Display value key.
  * @cfg map.imageClass Image CSS class value key.
  * @cfg map.imageConfig Image configuration information key.
  * @cfg map.markerValue Data item marker key.
  * @cfg map.isSeparatorItem Separator item flag.
  */
	map: {
		value: "value",
		displayValue: "displayValue",
		imageClass: "imageClass",
		imageConfig: "imageConfig",
		customHtml: "customHtml",
		markerValue: "markerValue",
		isSeparatorItem: "isSeparatorItem"
	},

	/**
  * The reference to the Ext.dom.Element element relative to which the visual positioning will occur
  * @type {Ext.dom.Element}
  */
	alignEl: null,

	/**
  * Offset from the {@link #alignEl} element.
  * @private
  * @type {Array}
  */
	offset: null,

	/**
  * A flag of automatic selection of the first element of the drop-down list.
  * @type {Boolean}
  */
	useDefSelection: false,

	/**
  * The offset from the {@link #alignEl} element by default.
  * @private
  * @type {Array}
  */
	defaultOffset: null,

	/**
  * Parameter indicating the maximum visible number of menu items in the list
  * @property {Number} maxItems
  */
	maxItems: 15,

	/**
  * Parameter indicating the minimum visible number of menu items in the list
  * @property {Number} minItems
  */
	minItems: 3,

	/**
  * The minimum width of the drop-down list
  * @property {String} minWidth
  */
	minWidth: "0px",

	/**
  * Maximum width of the drop-down list
  * @property {String} maxWidth
  */
	maxWidth: "0px",

	/**
  * @inheritdoc Terrasoft.Component#styles
  * @protected
  * @override
  */
	styles: {
		wrapStyle: {}
	},

	/**
  * Is images from PrimaryImageColumn visible.
  * @protected
  */
	iconsVisible: false,

	/**
  * Indicates that the body scroll must be disabled.
  * @type {Boolean}
  */
	bodyScrollLock: false,

	/**
  * Body scroll top value.
  * @type {Number}
  */
	bodyScrollTop: 0,

	/**
  * @inheritdoc Terrasoft.Component#constructor
  * @protected
  * @override
  */
	constructor: function () {
		this.callParent(arguments);
		this.addEvents(
		/**
   * @event listPressDown
   * The expected event on the drop-down list that indicates that you want to select the next item in the list relative to the current one.
   * The handler of the {@link #onPressDown} event
   */
		"listPressDown",

		/**
   * @event listPressUp
   * The expected event on the drop-down list that indicates that you want to select the previous list item relative to the current item.
   */
		"listPressUp",

		/**
   * @event listPressEnter
   * The expected event on the drop-down list that indicates that the "Enter" key was pressed.
   */
		"listPressEnter",

		/**
   * @event listViewItemRender
   * Element Generation Event.
   */
		"listViewItemRender",

		/**
   * @event loadNextPage
   * Event that fires when new group of list items need to be loaded.
   */
		"loadNextPage");
	},

	/**
  * @inheritdoc Terrasoft.Component#init
  * @override
  */
	init: function () {
		this.callParent(arguments);
		this.defaultOffset = [1, 1];
	},

	/**
  * @inheritdoc Terrasoft.Component#initDomEvents
  * @override
  */
	initDomEvents: function () {
		if (Ext.isEmpty(this.listItems)) {
			return;
		}
		var wrapEl = this.getWrapEl();
		var listview = Ext.get(this.id);
		var list = this.listEls = listview.select("li, div");
		wrapEl.on("mousedown", this.onWrapElMouseDown, this);
		wrapEl.on("scroll", this.onWrapElScroll, this);
		list.on("click", this.onSelected, this);
		list.on("mouseover", this.onMouseOver, this);
		list.on("mouseout", this.onMouseOut, this);
		this.on("listPressDown", this.onPressDown, this);
		this.on("listPressUp", this.onPressUp, this);
		this.on("listPressEnter", this.onPressEnter, this);
		var document = Ext.getDoc();
		document.on("scroll", this.onDocumentScroll, this);
		var body = Ext.getBody();
		body.on("wheel", this.onBodyMouseWheel, this);
		this.bodyScrollTop = body.getScrollTop();
	},

	/**
  * Clear DOM events listeners.
  * @inheritdoc Terrasoft.controls.component#clearDomListeners
  * @override
  */
	clearDomListeners: function () {
		this.callParent(arguments);
		this.unSubscribeDocEvents();
	},

	/**
  * Clears document DOM events listeners.
  * @private
  */
	unSubscribeDocEvents: function () {
		var doc = Ext.getDoc();
		doc.un("scroll", this.onDocumentScroll, this);
		var body = Ext.getBody();
		body.un("wheel", this.onBodyMouseWheel, this);
	},

	/**
  * Handler for document mouse wheel event. Hides listview.
  * @protected
  * @param {Ext.EventObjectImpl} event Event object.
  */
	onBodyMouseWheel: function (event) {
		if (!this.isEventWithin(event)) {
			this.hide();
			this.unSubscribeDocEvents();
		}
	},

	/**
  * Handler for scroll event on document. Set body scroll.
  * @protected
  */
	onDocumentScroll: function () {
		var body = Ext.getBody();
		if (this.bodyScrollLock) {
			body.setScrollTop(this.bodyScrollTop);
		}
		this.bodyScrollTop = body.getScrollTop();
	},

	/**
  * Processes list scrolling.
  * @param {Ext.EventObject} event Event object.
  */
	onWrapElScroll: function (event) {
		var element = event.currentTarget;
		if (this.getIsScrolledToBottom(element)) {
			this.loadMoreElements();
		}
	},

	/**
  * Checks if list scrolled to the bottom.
  * @protected
  * @param {Ext.dom.Element} element Scrolled element.
  * @return {Boolean} Returns true if element scrolled to the bottom, otherwise - false.
  */
	getIsScrolledToBottom: function (element) {
		return element.clientHeight + element.scrollTop >= element.scrollHeight;
	},

	/**
  * Fires event to load more elements to the list with reqired amount of elements.
  * @protected
  */
	loadMoreElements: function () {
		this.indexOfLastLoadedItem = this.listEls.elements.length - 1;
		this.fireEvent("loadNextPage");
	},

	/**
  * Processes click on the main list element.
  * @protected
  * @param {Object} event Event object.
  */
	onWrapElMouseDown: function (event) {
		event.stopEvent();
	},

	/**
  * Returns listview LI element.
  * @protected
  * @param {Object} element
  */
	getTargetElement: function (element) {
		return element.tagName === "LI" ? element : element.parentNode;
	},

	/**
  * Fires selection event with the value of selected item.
  * @protected
  * @param  {Ext.EventObject} event Event object.
  * @param  {Ext.dom.Element} target Link to the element of event.
  */
	onSelected: function (event, target) {
		event.stopEvent();
		var element = this.getTargetElement(target);
		var value = element.getAttribute("data-value");
		var listItems = this.listItems;
		var listValue;
		for (var i = 0, length = listItems.length; i < length; i++) {
			listValue = listItems[i];
			/*jshint eqeqeq:false */
			if (listValue.value == value) {
				/*jshint eqeqeq:true */
				this.fireEvent("select", listValue);
				break;
			}
		}
	},

	/**
  * Processes focused list item selection.
  * @protected
  * @param {Ext.EventObject} event Event object.
  * @param {Ext.dom.Element} target Link to the element of event.
  */
	onMouseOver: function (event, target) {
		event.stopEvent();
		var selected = this.selectedItem;
		var item = Ext.get(this.getTargetElement(target));
		if (selected) {
			selected.removeCls("listview-selected");
		}
		item.addCls("listview-selected");
		this.selectedItem = item;
	},

	/**
  * The event handler for canceling the focus of a list item.
  * @protected
  * @param {Ext.EventObject} event Event object
  */
	onMouseOut: function (event) {
		event.stopEvent();
		if (this.selectedItem && this.selectedItem.hasCls("listview-selected")) {
			this.selectedItem.removeCls("listview-selected");
		}
		this.selectedItem = null;
	},

	/**
  * The {@link #listPressDown} event handler.
  * @protected
  */
	onPressDown: function () {
		this.changeSelected("down");
	},

	/**
  * The {@link #listPressUp} event handler.
  * @protected
  */
	onPressUp: function () {
		this.changeSelected("up");
	},

	/**
  * Event Handler of list item selection.
  * @protected
  */
	onPressEnter: function () {
		var selected = Ext.getDom(this.selectedItem);
		var result = null;
		if (selected) {
			var value = selected.getAttribute("data-value");
			var listValue;
			var listItems = this.listItems;
			for (var i = 0, length = listItems.length; i < length; i++) {
				listValue = listItems[i];
				/*jshint eqeqeq:false */
				if (listValue.value == value) {
					/*jshint eqeqeq:true */
					result = listValue;
					break;
				}
			}
		}
		this.fireEvent("select", result);
		return true;
	},

	/**
  * @inheritdoc Terrasoft.Component#onAfterRender
  * @protected
  * @override
  */
	onAfterRender: function () {
		this.callParent(arguments);
		this.adjustMaxHeight();
	},

	/**
  * @inheritdoc Terrasoft.Component#onAfterReRender
  * @protected
  * @override
  */
	onAfterReRender: function () {
		this.callParent(arguments);
		this.adjustMaxHeight();
	},

	/**
  * @inheritdoc Terrasoft.Component#tpl
  * @protected
  * @override
  * @type {String[]}
  */
	/* jshint quotmark:false */
	// jscs:disable validateQuoteMarks
	tpl: ['<div id="{id}" class="{wrapClass}" style="{wrapStyle}">', '<tpl if="listItems">', '<ul>', '<tpl for="listItems">', '<tpl if="values.isSeparatorItem">', '<div data-value="{value}" data-item-marker="{markerValue}">{customHtml}</div>', '<tpl elseif="values.primaryImageVisible">', '<li data-value="{value}" data-item-marker="{markerValue}">', '<div class="listview-left-icon-container">', '<tpl if="values.primaryImage">', '<img class="listview-left-icon" src={imageUrl}>', '</tpl>', '</div>', '<div class="listview-text-container">{customHtml}</div>', '</li>', '<tpl else>', '<li', '<tpl if="values.imageConfig">', ' class="listview-icon"', '</tpl>', '<tpl if="values.imageConfig">', ' style="background-image: url(\'', '{[Terrasoft.ImageUrlBuilder.getUrl(values.imageConfig)]}', '\'); "', '</tpl>', ' data-value="{value}" data-item-marker="{markerValue}"', '>{customHtml}</li>', '</tpl>', '</tpl>', '</ul>', '</tpl>', '<tpl if="showProgressSpinner">', '<div class="listview-progress">', '<div class="listview-progress-spinner">{pregressSpinner}</div>', '<div class="listview-progress-caption">{progressSpinnerCaption}</div>', '</div>', '</tpl>', '</div>'],
	/* jshint quotmark:true */

	/**
  * @inheritdoc Terrasoft.Component#getTplData
  * @protected
  * @override
  * @return {Object}
  */
	getTplData: function () {
		var tplData = this.callParent(arguments);
		this.combineCssStyles();
		var listviewTplData = {
			listItems: this.getListItems(),
			showProgressSpinner: this.showProgressSpinner,
			pregressSpinner: this.getProgressSpinner(),
			progressSpinnerCaption: Terrasoft.encodeHtml(this.progressSpinnerCaption),
			wrapClass: this.combineCssClasses()
		};
		Ext.apply(listviewTplData, tplData, {});
		this.getSelectors();
		this.selectedItem = null;
		return listviewTplData;
	},

	/**
  * Collect and configure CSS classes for the template.
  * @protected
  */
	combineCssClasses: function () {
		var classes = ["listview", "listview-scroll"];
		if (this.hasIcons) {
			classes.push("listview-with-icons");
		}
		return classes;
	},

	/**
  * Collect and configure CSS styles for the template.
  * @protected
  */
	combineCssStyles: function () {
		var wrapStyle = this.styles.wrapStyle;
		var minWidth = parseInt(this.minWidth, 10) - 2;
		var maxWidth = parseInt(this.maxWidth, 10) - 2;
		var wrapBox = this.alignEl ? this.alignEl.getBox() : {};
		if (minWidth > 0) {
			wrapStyle["min-width"] = this.minWidth + "px";
		} else if (wrapBox.width) {
			wrapStyle["min-width"] = wrapBox.width - 2 + "px";
		}
		if (maxWidth > 0) {
			wrapStyle["max-width"] = this.maxWidth - 2 + "px";
		}
		if (this.maxItems) {
			wrapStyle["max-height"] = this.adjustHeight() + "px";
		}
		this.styles.wrapStyle = wrapStyle;
	},

	/**
  * Creating selectors for Terrasoft.Component
  * @protected
  * @return {Object}
  */
	getSelectors: function () {
		this.selectors = {
			wrapEl: "#" + this.id,
			el: "#" + this.id
		};
		return this.selectors;
	},

	/**
  * Getting list items.
  * @protected
  * @return {Object}
  */
	getListItems: function () {
		var items = this.listItems;
		Terrasoft.each(this.listItems, function (item) {
			this.fireEvent("listViewItemRender", item);
			var customHtml = item.customHtml;
			item.customHtml = customHtml ? customHtml : Terrasoft.encodeHtml(item.displayValue);
			if (item.imageConfig) {
				this.hasIcons = true;
			}
			if (Ext.isEmpty(item.markerValue)) {
				item.markerValue = item.displayValue;
			}
		}, this);
		return items;
	},

	/**
  * Generate a link to the item object of the download indicator.
  * @protected
  * @return {Terrasoft.ProgressSpinner}
  */
	getProgressSpinner: function () {
		var progressSpinner = this.progressSpinner;
		if (!progressSpinner) {
			progressSpinner = this.progressSpinner = Ext.create("Terrasoft.ProgressSpinner", {
				extraComponentClasses: "listview-progress-container"
			});
		}
		return progressSpinner.generateHtml();
	},

	/**
  * Positioning the menu relative to the parent element.
  * @protected
  */
	adjustPosition: function () {
		var alignEl = this.alignEl;
		var wrapEl = this.getWrapEl();
		if (!alignEl || !wrapEl || !alignEl.dom || !wrapEl.dom) {
			return;
		}
		var self = this;
		var correctFn = function () {
			self.correctListTopPosition.call(self);
		};
		var offset = this.offset || this.defaultOffset;
		wrapEl.anchorTo(alignEl, "tl-bl?", offset, false, false, correctFn);
		wrapEl.removeAnchor();
		this.scrollToSelectedItem();
	},

	/**
  * Adjusts the position of the sheet to 1px up / down relative to alignEl
  * @private
  */
	correctListTopPosition: function () {
		var wrap = this.getWrapEl();
		var wrapDom = wrap.dom;
		var listTop = wrapDom.offsetTop;
		var listHeight = wrapDom.offsetHeight;
		var elDom = this.alignEl.dom;
		var elTop = elDom.offsetTop;
		var elHeight = elDom.offsetHeight;
		var wrapTopStyle = wrap.getStyle("top");
		var listBox = Ext.util.Format.parseBox(wrapTopStyle);
		var top = listBox.top;
		if (listTop === elTop + elHeight) {
			top += this.correctTopPosition;
			wrap.setStyle("top", top + "px");
		} else if (listTop + listHeight === elTop) {
			top -= this.correctTopPosition;
			wrap.setStyle("top", top + "px");
		}
	},

	/**
  * Set the maximum number of visible elements relative to the set value and other parameters of the drop-down list.
  * @protected
  * @return {Number} The adjusted maximum number of visible list items.
  */
	adjustMaxItems: function () {
		var listItemsLength = this.listItems.length;
		var maxItems = this.maxItems - 1;
		var max = listItemsLength < maxItems ? listItemsLength : maxItems;
		if (this.showProgressSpinner) {
			maxItems += max < maxItems ? 1 : -1;
		}
		return maxItems;
	},

	/**
  * Adjust the maximum height of the menu after rendering or re-rendering
  * @protected
  */
	adjustMaxHeight: function () {
		var alignEl = this.alignEl;
		if (!alignEl) {
			return;
		}
		this.adjustPosition();
		var alignRegion = alignEl.getViewRegion();
		var wrapRegion = this.getWrapEl().getViewRegion();
		var viewRegion = Ext.getBody().getViewRegion();
		if (wrapRegion.top > viewRegion.top && wrapRegion.bottom < viewRegion.bottom && wrapRegion.left > viewRegion.left && wrapRegion.right < viewRegion.right) {
			this.adjustPosition();
			return;
		}
		var listHeight = wrapRegion.bottom - wrapRegion.top;
		var spaceTop = alignRegion.top - viewRegion.top || 0;
		var spaceBottom = viewRegion.bottom - alignRegion.bottom || 0;
		var space = spaceTop > spaceBottom ? spaceTop : spaceBottom;
		var rowHeight = parseInt(this.rowHeight, 10);
		if (listHeight < space) {
			space = listHeight;
		}
		var newRows = Math.floor(space / rowHeight);
		if (listHeight > space) {
			newRows -= 1;
		}
		var newHeight = newRows * rowHeight;
		this.wrapEl.setHeight(newHeight);
		this.adjustPosition();
	},

	/**
  * Calculate the total height of the menu, taking into account the height of each element and their number
  * @protected
  * @return {Number} Menu height
  */
	adjustHeight: function () {
		var maxItems = this.adjustMaxItems();
		var rowHeight = parseInt(this.rowHeight, 10);
		return maxItems * rowHeight;
	},

	/**
  * Select and scroll to list element by selected value.
  */
	selectElement: function () {
		var selectedValue = this.selectedValue;
		if (!selectedValue) {
			this.changeSelected("down");
			return;
		}
		var wrapEl = this.getWrapEl();
		var list = Ext.select("li", true, wrapEl.dom);
		var listElements = list.elements;
		var listLength = listElements.length;
		var elementValue;
		for (var i = 0; i < listLength; i++) {
			elementValue = listElements[i].dom.getAttribute("data-value");
			/*jshint eqeqeq:false */
			if (elementValue == selectedValue.value) {
				/*jshint eqeqeq:true */
				listElements[i].addCls("listview-selected");
				this.selectedItem = listElements[i];
				break;
			}
		}
		this.scrollToSelectedItem();
		return;
	},

	/**
  * Focus function of the list item
  * @protected
  * @param  {String} direction = ["up"|"down"] direction indicator of the focus shift
  */
	changeSelected: function (direction) {
		if (this.listItems.length < 1) {
			return;
		}
		var wrapEl = this.getWrapEl();
		var list = Ext.select("li", false, wrapEl.dom);
		var listLength = list.getCount();
		var selectedItem = Ext.select("." + this.selectedCssClass, false, wrapEl.dom).first();
		if (!selectedItem) {
			if (direction === "up") {
				selectedItem = list.last();
			} else if (direction === "down") {
				selectedItem = list.first();
			}
			selectedItem.addCls(this.selectedCssClass);
			this.selectedItem = selectedItem;
			this.scrollToSelectedItem();
			return;
		}
		selectedItem.removeCls(this.selectedCssClass);
		var selectedItemIndex = list.indexOf(selectedItem);
		selectedItemIndex += direction === "down" ? 1 : -1;
		selectedItemIndex = (selectedItemIndex + listLength) % listLength;
		selectedItem = list.item(selectedItemIndex);
		selectedItem.addCls(this.selectedCssClass);
		this.selectedItem = selectedItem;
		this.scrollToSelectedItem();
	},

	/**
  * Scrolling to a focused item in the list
  * @protected
  */
	scrollToSelectedItem: function () {
		var indexOfLastLoadedItem = this.listEls.item && this.listEls.item(this.indexOfLastLoadedItem);
		var selectedItem = this.selectedItem || indexOfLastLoadedItem;
		this.indexOfLastLoadedItem = null;
		if (!selectedItem) {
			return;
		}
		var wrapEl = this.getWrapEl();
		var scrollVector = wrapEl.getConstrainVector(selectedItem);
		var scrollHeight = 0;
		if (scrollVector) {
			scrollHeight = scrollVector[1];
		}
		wrapEl.scroll("bottom", scrollHeight, false);
	},

	/**
  * Load a collection for list data
  * @param  {Terrasoft.Collection} collection Data Collection
  */
	loadList: function (collection) {
		if (Ext.isEmpty(this.map)) {
			return;
		}
		this.collection = collection;
		var list = collection.getItems();
		var listItems = [];
		for (var row in list) {
			var item = this.convertItemToMapConfig(list[row]);
			listItems.push(item);
		}
		this.listItems = listItems;
	},

	convertItemToMapConfig: function (item) {
		var newItem = {};
		var map = this.map;
		var key, mapKey;
		for (key in item) {
			if (!item.hasOwnProperty(key)) {
				continue;
			}
			for (mapKey in map) {
				if (!map.hasOwnProperty(mapKey)) {
					continue;
				}
				if (key === map[mapKey]) {
					newItem[mapKey] = item[key];
					break;
				}
			}
			if (key !== map[mapKey]) {
				newItem[key] = item[key];
			}
			this.convertImageConfig(item, newItem);
		}
		return newItem;
	},

	/**
  * Converts image config from query to listview image config.
  * @protected
  * @param  {Object} item Config from query
  * @param  {Object} newItem Config for listview
  */
	convertImageConfig: function (item, newItem) {
		if (item.imageConfig) {
			return;
		}
		newItem.primaryImageVisible = this.iconsVisible && !item.customHtml;
		if (!newItem.primaryImageVisible) {
			return;
		}
		newItem.primaryImage = item.Image && item.Image.value;
		newItem.imageConfig = {
			"source": Terrasoft.ImageSources.SYS_IMAGE,
			"params": {
				"primaryColumnValue": item.Image && item.Image.value ? item.Image.value : ""
			}
		};
		newItem.imageUrl = Terrasoft.ImageUrlBuilder.getUrl(newItem.imageConfig);
	},

	convertItemToInitialConfig: function (item) {
		var newItem = {};
		var map = this.map;
		var key, mapKey;
		for (key in item) {
			if (!item.hasOwnProperty(key)) {
				continue;
			}
			for (mapKey in map) {
				if (!map.hasOwnProperty(mapKey)) {
					continue;
				}
				if (key === mapKey) {
					newItem[map[mapKey]] = item[key];
					break;
				}
			}
			if (key !== mapKey) {
				newItem[key] = item[key];
			}
		}
		return newItem;
	},

	/**
  * Displaying a drop-down list
  * @param {Object} options Enumeration of public parameters of this class with the required values
  */
	show: function (options) {
		for (var propertyName in options) {
			if (!options.hasOwnProperty(propertyName) || typeof this[propertyName] === undefined) {
				continue;
			}
			this[propertyName] = options[propertyName];
		}
		if (this.listItems.length < 1 && this.showProgressSpinner === false) {
			return;
		}
		this.selectedItem = null;
		this.visible = true;
		if (!this.rendered) {
			this.render(this.renderTo || Ext.getBody());
		} else {
			this.reRender();
		}
		if (this.useDefSelection) {
			this.selectElement();
		}
		this.bodyScrollLock = true;
		this.fireEvent("show");
	},

	/**
  * Hides dropdown list.
  */
	hide: function () {
		this.indexOfLastLoadedItem = null;
		this.selectedItem = null;
		this.selectedValue = null;
		this.bodyScrollLock = false;
		this.setVisible(false);
		this.fireEvent("hide");
	},

	/**
  * @inheritdoc Terrasoft.Component#destroy
  * @protected
  * @override
  */
	onDestroy: function () {
		if (this.pregressSpinner) {
			this.progressSpinner.destroy();
			delete this.progressSpinner;
		}
		if (!Ext.isEmpty(this.listEls)) {
			this.listEls.each(function (el) {
				el.removeAllListeners();
				el.destroy();
			}, this);
		}
		this.callParent(arguments);
	}
});