Your IP : 216.73.216.86


Current Path : /var/www/homesaver/www/bitrix/components/bitrix/main.interface.buttons/templates/.default/
Upload File :
Current File : /var/www/homesaver/www/bitrix/components/bitrix/main.interface.buttons/templates/.default/script.js

BX.namespace('BX.Main');

if (typeof(BX.Main.interfaceButtons) === 'undefined')
{
	/**
	 * @param {object} params parameters
	 * @property {string} containerId @required
	 * @property {string} theme
	 * @property {object} classes
	 * @property {string} classes.item Class for list item
	 * @property {string} classes.itemSublink Class for sublink (ex. Add link)
	 * @property {string} classes.itemText Class for item text and submenu item text
	 * @property {string} classes.itemCounter Class for list item counter and submenu item counter
	 * @property {string} classes.itemIcon Class for list item icon and submenu item icon
	 * @property {string} classes.itemMore Class for more button
	 * @property {string} classes.itemOver Class for hovered item
	 * @property {string} classes.itemActive Class for active item
	 * @property {string} classes.itemDisabled Class for disabled elements
	 * @property {string} classes.itemLocked Class for locked item. Added for list and submenu item
	 * @property {string} classes.menuShown Class for open menu
	 * @property {string} classes.onDrag Class added for container on dragstart event and removed on drag end event
	 * @property {string} classes.dropzone Class for dropzone in submenu
	 * @property {string} classes.separator Class for submenu separator before disabled items
	 * @property {string} classes.submenuItem Class for submenu item
	 * @property {string} classes.submenu Class for submenu container
	 * @property {string} classes.secret Class for hidden alias items (set display: none; for items)
	 * @property {object} messages Messages object. Contains localization strings
	 * @property {string} messages.MIB_DROPZONE_TEXT Dropzone text
	 * @property {string} messages.MIB_LICENSE_BUY_BUTTON License window Buy button text
	 * @property {string} messages.MIB_LICENSE_TRIAL_BUTTON License window Trial button text
	 * @property {string} messages.MIB_LICENSE_WINDOW_HEADER_TEXT License window header text
	 * @property {string} messages.MIB_LICENSE_WINDOW_TEXT License window content text
	 * @property {string} messages.MIB_LICENSE_WINDOW_TRIAL_SUCCESS_TEXT Trial success text
	 * @property {object} licenseWindow Settings for license window
	 * @property {string} licenseWindow.isFullDemoExists Y|N
	 * @property {string} licenseWindow.hostname Hostname for license window ajax calls
	 * @property {string} licenseWindow.ajaxUrl Ajax handler url
	 * @property {string} licenseWindow.licenseAllPath
	 * @property {string} licenseWindow.licenseDemoPath
	 * @property {string} licenseWindow.featureGroupName
	 * @property {string} licenseWindow.ajaxActionsUrl
	 * @param {HTMLElement} container
	 */
	BX.Main.interfaceButtons = function(container, params)
	{
		/**
		 * Sets default values
		 */
		this.classItem = 'main-buttons-item';
		this.classItemSublink = 'main-buttons-item-sublink';
		this.classItemText = 'main-buttons-item-text';
		this.classItemCounter = 'main-buttons-item-counter';
		this.classItemIcon = 'main-buttons-item-icon';
		this.classItemMore = 'main-buttons-item-more';
		this.classOnDrag = 'main-buttons-drag';
		this.classDropzone = 'main-buttons-submenu-dropzone';
		this.classSeparator = 'main-buttons-submenu-delimiter';
		this.classHiddenLabel = 'main-buttons-hidden-label';
		this.classSubmenuItem = 'main-buttons-submenu-item';
		this.classItemDisabled = 'main-buttons-disabled';
		this.classItemOver = '--over';
		this.classMenuShown = '--menu-shown';
		this.classItemActive = 'main-buttons-item-active';
		this.classSubmenu = 'main-buttons-submenu';
		this.classSecret = 'secret';
		this.classItemLocked = '--locked';
		this.classExtraItemLink = '';
		this.classExtraItemText = '';
		this.classExtraItemIcon = '';
		this.classExtraItemCounter = '';
		this.submenuIdPrefix = 'main_buttons_popup_';
		this.childMenuIdPrefix = 'main_buttons_popup_child_';
		this.submenuWindowIdPrefix = 'menu-popup-';
		this.classSettingMenuItem = 'main-buttons-submenu-setting';
		this.classEditState = 'main-buttons-edit';
		this.classEditItemButton = 'main-buttons-item-edit-button';
		this.classDragItemButton = 'main-buttons-item-drag-button';
		this.classSettingsApplyButton = 'main-buttons-submenu-settings-apply';
		this.classSettingsResetButton = 'main-buttons-submenu-settings-reset';
		this.classSetHome = 'main-buttons-set-home';
		this.classSetHide = 'main-buttons-set-hide';
		this.classManage = 'main-buttons-manage';
		this.classContainer = 'main-buttons';
		this.classSubmenuNoHiddenItem = 'main-buttons-submenu-item-no-hidden';
		this.classDefaultSubmenuItem = 'menu-popup-item';
		this.classDefaultSubmenuDelimimeter = 'popup-window-delimiter-section';
		this.classInner = 'main-buttons-inner-container';
		this.listContainer = null;
		this.dragItem = null;
		this.overItem = null;
		this.moreButton = null;
		this.messages = null;
		this.licenseParams = null;
		this.ajaxSettings = null;
		this.enableItemMouseEnter = true;
		this.menuShowTimeout = null;
		this.isMoreMenuShown = false;
		this.onDragStarted = false;
		this.isSettingsEnabled = true;
		this.containerId = params.containerId;
		this.isEditEnabledState = false;
		this.theme = BX.Type.isStringFilled(params.theme) ? params.theme : 'default';
		this.maxItemLength =
			BX.Type.isNumber(params.maxItemLength) && params.maxItemLength > 6 ? params.maxItemLength : 20
		;
		this.tmp = {};
		this.itemData = new WeakMap();

		this.handleMoreMenuItemMouseEnter = this.handleMoreMenuItemMouseEnter.bind(this);

		this.init(container, params);

		/**
		 * Public methods and properties
		 */
		return {
			addMenuItem: this.addMenuItem.bind(this),
			deleteMenuItem: this.deleteMenuItem.bind(this),
			updateMenuItemText: this.updateMenuItemText.bind(this),

			getItemById: this.getItemById.bind(this),
			getAllItems: this.getAllItems.bind(this),
			getHiddenItems: this.getHiddenItems.bind(this),
			getVisibleItems: this.getVisibleItems.bind(this),
			getDisabledItems: this.getDisabledItems.bind(this),
			getMoreButton: this.getMoreButton.bind(this),
			adjustMoreButtonPosition: this.adjustMoreButtonPosition.bind(this),
			getItemData: this.getItemData.bind(this),

			// Compatible methods
			getSubmenu: this.getMoreMenu.bind(this),
			showSubmenu: this.showMoreMenu.bind(this),
			closeSubmenu: this.closeMoreMenu.bind(this),
			refreshSubmenu: this.refreshMoreMenu.bind(this),

			getMoreMenu: this.getMoreMenu.bind(this),
			showMoreMenu: this.showMoreMenu.bind(this),
			closeMoreMenu: this.closeMoreMenu.bind(this),
			refreshMoreMenu: this.refreshMoreMenu.bind(this),

			getCurrentSettings: this.getCurrentSettings.bind(this),
			saveSettings: this.saveSettings.bind(this),
			updateCounter: this.updateCounter.bind(this),
			getActive: this.getActive.bind(this),
			isDisabled: this.isDisabled.bind(this),
			isVisibleItem: this.isVisibleItem.bind(this),
			isEditEnabled: this.isEditEnabled.bind(this),
			isActiveInMoreMenu: this.isActiveInMoreMenu.bind(this),
			isSettingsEnabled: this.isSettingsEnabled,
			classes:
			{
				item: this.classItem,
				itemText: this.classItemText,
				itemCounter: this.classItemCounter,
				itemIcon: this.classItemIcon,
				itemDisabled: this.classItemDisabled,
				itemOver: this.classItemOver,
				itemActive: this.classItemActive,
				itemLocked: this.classItemLocked,
				menuShown: this.classMenuShown,
				submenu: this.classSubmenu,
				submenuItem: this.classSubmenuItem,
				containerOnDrag: this.classOnDrag,
				classSettingMenuItem: this.classSettingMenuItem
			},
			itemsContainer: this.listContainer,
			itemsContainerId: this.listContainer.id
		};
	};

	//noinspection JSUnusedGlobalSymbols,JSUnusedGlobalSymbols
	BX.Main.interfaceButtons.prototype =
	{
		init: function(container, params)
		{
			this.listContainer = BX(this.getId());

			if (!BX.Type.isPlainObject(params))
			{
				throw 'BX.MainButtons: params is not Object';
			}

			if (!('containerId' in params) || !BX.Type.isStringFilled(params.containerId))
			{
				throw 'BX.MainButtons: containerId not set in params';
			}

			if (!BX.Type.isDomNode(this.listContainer))
			{
				throw 'BX.MainButtons: #' + params.containerId + ' is not dom node';
			}

			if (('classes' in params) && BX.Type.isPlainObject(params.classes))
			{
				this.setCustomClasses(params.classes);
			}

			if (('messages' in params) && BX.Type.isPlainObject(params.messages))
			{
				this.setMessages(params.messages);
			}

			if (('licenseWindow' in params) && BX.Type.isPlainObject(params.licenseWindow))
			{
				this.setLicenseWindowParams(params.licenseWindow);
			}

			if ('disableSettings' in params && params.disableSettings === "true")
			{
				this.isSettingsEnabled = false;
				this.visibleControlMoreButton();
			}

			this.initSaving(params.ajaxSettings);

			this.moreButton = this.getMoreButton();

			this.listChildItems = {};

			this.initItems();

			this.adjustMoreButtonPosition();
			this.bindEventsOnMoreButton();
			this.bindOnResizeFrame();

			BX.Event.bind(this.getContainer(), 'click', BX.delegate(this._onDocumentClick, this));
			BX.addCustomEvent("onPullEvent-main", BX.delegate(this._onPush, this));

			this.updateMoreButtonCounter();

			if (this.isActiveInMoreMenu())
			{
				this.activateItem(this.moreButton);
			}

			const homeItem = this.getHomeItem();
			if (homeItem)
			{
				const { url: firstPageLink } = homeItem;
				this.lastHomeLink = firstPageLink;
			}

			const showChildButtons = Array.from(this.container.querySelectorAll('.main-buttons-item-child-button'));
			showChildButtons.forEach(function(button) {
				const realChildButton = button.closest('.main-buttons-item-child');
				if (realChildButton.dataset.isOpened)
				{
					this.realChildButton = realChildButton;
					const clonedChildButton = realChildButton.closest('.main-buttons-item-child-button-cloned')
					if (clonedChildButton)
					{
						this.clonedChildButton = clonedChildButton;
					}
				}

				BX.Event.bind(button, 'click', this.onShowChildButtonClick.bind(this));

			}, this);
		},

		calculateChildListWidth: function()
		{
			if (this.realChildButton)
			{
				const buttons = this.realChildButton
					.querySelectorAll('.main-buttons-item-child-list-inner .main-buttons-item');

				const offset = 10;
				return Array.from(buttons).reduce(function(acc, button) {
					const width = BX.Text.toNumber(BX.Dom.style(button, 'width'));
					const marginLeft = BX.Text.toNumber(BX.Dom.style(button, 'margin-left'));
					const marginRight = BX.Text.toNumber(BX.Dom.style(button, 'margin-right'));

					return acc + width + marginLeft + marginRight;
				}, offset);
			}
			return 0;
		},

		onShowChildButtonClick: function(event)
		{
			event.preventDefault();

			if (!this.realChildButton)
			{
				this.realChildButton = event.currentTarget.closest('.main-buttons-item-child');
			}

			const childListContainer = this.realChildButton.querySelector('.main-buttons-item-child-list');

			this.enableItemMouseEnter = false;
			setTimeout(() => {
				this.enableItemMouseEnter = true;
			}, 200);

			const childIds = BX.Dom.attr(this.realChildButton, 'data-child-items');
			const isOpened = BX.Dom.attr(this.realChildButton, 'data-is-opened');
			let expandedParentIds = {};
			if (isOpened)
			{
				BX.Dom.attr(this.realChildButton, 'data-is-opened', null);

				childIds.forEach(function(childId) {
					const button = this.getContainer().querySelector('[data-id="'+childId+'"]');
					BX.Dom.style(button, 'display', null);
					if (childId.hasOwnProperty('PARENT_ITEM_ID'))
					{
						expandedParentIds[childId['PARENT_ITEM_ID']] = 'N';
					}
				}, this);

				if (this.clonedChildButton)
				{
					BX.Dom.remove(this.clonedChildButton);
				}

				BX.Dom.style(childListContainer, {
					overflow: null,
					'max-width': null,
				});

				expandedParentIds = JSON.stringify(expandedParentIds);
				this.saveOptions('expanded_lists', expandedParentIds);
			}
			else
			{
				BX.Dom.attr(this.realChildButton, 'data-is-opened', true);
				BX.Dom.style(childListContainer, 'max-width', this.calculateChildListWidth() + 'px');

				this.cloneChildButton(this.realChildButton);

				childIds.forEach((childId) => {
					const button = this.getContainer().querySelector('[data-id="'+childId+'"]');
					BX.Dom.insertBefore(button, this.realChildButton);
					BX.Dom.style(button, 'display', 'inline-block');
					if (childId.hasOwnProperty('PARENT_ITEM_ID'))
					{
						expandedParentIds[childId['PARENT_ITEM_ID']] = 'Y';
					}
				});

				setTimeout(() => {
					BX.Dom.style(childListContainer, 'overflow', 'unset');
				}, 200);

				expandedParentIds = JSON.stringify(expandedParentIds);
				this.saveOptions('expanded_lists', expandedParentIds);
			}

			setTimeout(() => {
				this._onResizeHandler();
			}, 200);
		},

		cloneChildButton: function(realChildButton)
		{
			this.clonedChildButton = BX.Runtime.clone(realChildButton);

			const childList = this.clonedChildButton.querySelector('.main-buttons-item-child-list');
			if (childList)
			{
				BX.Dom.remove(childList);
			}

			BX.Dom.addClass(this.clonedChildButton, 'main-buttons-item-child-button-cloned');
			BX.Dom.style(this.clonedChildButton, 'transition', 'none');
			BX.Dom.insertBefore(this.clonedChildButton, realChildButton);
			BX.Event.bind(this.clonedChildButton, 'click', this.onShowChildButtonClick.bind(this));

			setTimeout(() => {
				BX.Dom.style(this.clonedChildButton, 'transition', null);
			}, 0);
		},

		_onDocumentClick: function(event)
		{
			if (this.isDragButton(event.target))
			{
				event.preventDefault();
				event.stopPropagation();
			}

			let item = this.getItem(event);
			if (BX.Type.isDomNode(item))
			{
				if (this.isSettings(item))
				{
					this.enableEdit();
					return false;
				}

				if (this.isApplySettingsButton(item))
				{
					event.preventDefault();
					event.stopPropagation();
					this.disableEdit();

					return false;
				}

				if (this.isResetSettingsButton(item))
				{
					this.resetSettings();
					return false;
				}

				if (this.isEditButton(event.target))
				{
					this.handleEditButtonClick(event);

					return false;
				}

				if (this.isSetHide(item))
				{
					const visibleItems = this.getVisibleItems();
					const visibleItemsLength = BX.Type.isArray(visibleItems) ? visibleItems.length : null;
					const id = this.editItemData.ID.replace(this.listContainer.id + '_', '');
					let currentItem = this.getItemById(id);
					const currentAlias = this.getItemAlias(currentItem);

					currentItem = this.isVisibleItem(currentItem) ? currentItem : currentAlias;

					if (this.isDisabled(currentAlias))
					{
						this.enableItem(currentAlias);

					}
					else if (!this.isDisabled(currentAlias) && visibleItemsLength > 2)
					{
						this.disableItem(currentAlias);
					}

					if (visibleItemsLength === 1)
					{
						BX.onCustomEvent(window, 'BX.Main.InterfaceButtons:onHideLastVisibleItem', [currentItem, this]);
					}

					this.refreshMoreMenu();
					this.saveSettings();

					this.adjustMoreButtonPosition();

					if (this.isEditEnabled())
					{
						this.enableEdit();
					}

					this.editMenu.popupWindow.close();

					return false;
				}

				if (this.isSetHome(item))
				{
					const id = this.editItemData.ID.replace(this.listContainer.id + '_', '');
					const currentItem = this.getItemById(id);
					const currentAlias = this.getItemAlias(currentItem);

					if (this.isDisabled(currentAlias))
					{
						this.enableItem(currentAlias);
					}

					this.listContainer.insertBefore(currentItem, BX.firstChild(this.listContainer));

					this.adjustMoreButtonPosition();
					this.refreshMoreMenu();
					this.saveSettings();

					if (this.isEditEnabled())
					{
						this.enableEdit();
					}

					this.editMenu.popupWindow.close();

					return false;
				}

				if (!this.isDragButton(event.target) && !this.isEditButton(event.target))
				{
					const itemData = this.getItemData(item);
					let dataOnClick = itemData['ON_CLICK']

					if (this.isSublink(event.target))
					{
						dataOnClick = BX.Type.isPlainObject(itemData['SUB_LINK']) ? itemData['SUB_LINK']['ON_CLICK'] : '';
					}

					if (BX.Type.isStringFilled(dataOnClick))
					{
						event.preventDefault();
						this.execScript(dataOnClick, event);
					}
				}
			}

			if (this.isEditEnabled() && this.getMoreMenu())
			{
				this.getMoreMenu().getPopupWindow().setAutoHide(false);
			}
		},

		/**
		 * @return {boolean}
		 */
		isActiveInMoreMenu: function()
		{
			const hiddenItems = this.getHiddenItems();
			const disabledItems = this.getDisabledItems();
			const items = hiddenItems.concat(disabledItems);

			return items.some(function(current) {
				const itemData = this.getItemData(current);

				return itemData['IS_ACTIVE'] === true;
			}, this);
		},

		_onPush: function (command, params)
		{
			if (command === "user_counter" && params && BX.message("SITE_ID") in params)
			{
				const counters = params[BX.message("SITE_ID")];
				for (const counterId in counters)
				{
					if (counters.hasOwnProperty(counterId))
					{
						this.updateCounter(counterId, counters[counterId]);
					}
				}
			}
		},

		/**
		 * Gets active element
		 * @return {?HTMLElement}
		 */
		getActive: function()
		{
			let items = this.getAllItemsData();
			let result = null;
			let rootActiveItem = null;

			while (BX.Type.isArrayFilled(items))
			{
				const item = items.shift();
				if (item['IS_ACTIVE'] === true)
				{
					if (rootActiveItem === null)
					{
						rootActiveItem = item;
					}

					result = item;
					items = BX.Type.isArrayFilled(item['ITEMS']) ? [...item['ITEMS']] : null;
				}
			}

			if (result !== null && rootActiveItem !== null)
			{
				const node = BX(rootActiveItem.ID);
				if (BX.Type.isDomNode(node))
				{
					result.NODE = node;
				}
				else
				{
					result.NODE = null;
				}
			}

			return result;
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isSetHome: function(item)
		{
			return BX.Dom.hasClass(item, this.classSetHome);
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isSetHide: function(item)
		{
			return BX.Dom.hasClass(item, this.classSetHide);
		},

		/**
		 * @return {?HTMLElement}
		 */
		getSettingsButton: function()
		{
			return BX.Buttons.Utils.getByClass(this.getMoreMenuContainer(), this.classSettingMenuItem);
		},

		/**
		 * @return {?HTMLElement}
		 */
		getSettingsApplyButton: function()
		{
			return BX.Buttons.Utils.getByClass(this.getMoreMenuContainer(), this.classSettingsApplyButton);
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isApplySettingsButton: function(item)
		{
			return BX.Dom.hasClass(item, this.classSettingsApplyButton);
		},

		enableEdit: function()
		{
			const menu = this.getMoreMenu();
			if (menu)
			{
				const popup = menu.getPopupWindow();
				popup.setAutoHide(false);

				BX.Dom.addClass(popup.getPopupContainer(), this.classEditState);
			}

			BX.Dom.addClass(this.listContainer, this.classEditState);

			this.isEditEnabledState = true;
		},

		disableEdit: function()
		{
			const menu = this.getMoreMenu();
			if (menu)
			{
				const popup = menu.getPopupWindow();
				popup.setAutoHide(true);
				BX.Dom.removeClass(popup.getPopupContainer(), this.classEditState);
			}

			BX.Dom.removeClass(this.listContainer, this.classEditState);

			this.isEditEnabledState = false;

			this.destroyItemEditMenu();
		},

		/**
		 * @return {boolean}
		 */
		isEditEnabled: function()
		{
			return this.isEditEnabledState;
		},

		/**
		 * @param {object} itemData
		 * @param {HTMLElement} node
		 */
		showItemEditMenu: function(itemData, node)
		{
			if (BX.Type.isPlainObject(itemData) && 'ID' in itemData)
			{
				const menuId = [this.listContainer.id, '_edit_item'].join('');
				let menu = BX.Main.MenuManager.getMenuById(menuId);
				if (menu)
				{
					BX.Main.MenuManager.destroy(menuId);
				}

				menu = this.createItemEditMenu(itemData, menuId, node);
				menu.popupWindow.show();
			}
		},

		destroyItemEditMenu: function()
		{
			const menuId = [this.listContainer.id, '_edit_item'].join('');
			const menu = BX.Main.MenuManager.getMenuById(menuId);
			if (menu)
			{
				BX.Main.MenuManager.destroy(menuId);
			}
		},

		/**
		 * @return {?HTMLElement}
		 */
		getContainer: function()
		{
			if (!BX.Type.isDomNode(this.container))
			{
				this.container = BX(this.containerId).parentNode.parentNode;
			}

			return this.container;
		},

		/**
		 * @return {?BX.PopupMenu}
		 */
		getItemEditMenu: function()
		{
			return BX.Main.MenuManager.getMenuById([this.listContainer.id, '_edit_item'].join(''));
		},

		/**
		 * @param {object} itemData
		 * @param {string} menuId
		 * @param {HTMLElement} node BX.PopupMenu bindElement
		 * @return {?BX.PopupMenu}
		 */
		createItemEditMenu: function(itemData, menuId, node)
		{
			const menuItems = [
				{
					text: this.message('MIB_SET_HOME'),
					className: 'main-buttons-set-home menu-popup-no-icon'
				}
			];

			const id = itemData['ID'].replace(this.listContainer.id + '_', '');
			const currentItem = this.getItemById(id);

			if (this.isDisabled(currentItem))
			{
				menuItems.push({
					text: this.message('MIB_SET_SHOW'),
					className: 'main-buttons-set-hide menu-popup-no-icon'
				});
			}
			else
			{
				menuItems.push({
					text: this.message('MIB_SET_HIDE'),
					className: 'main-buttons-set-hide menu-popup-no-icon'
				});
			}

			if (itemData['IS_PINNED'])
			{
				const parentItem = this.getParentItem(itemData['ID']);
				menuItems.push({
					text: this.message('MIB_UNPIN_ITEM').replace('#NAME#', parentItem ? parentItem['TEXT'] : ''),
					onclick: (event, menuItem) => {
						this.handleItemUnpin(itemData, currentItem);
						menuItem.getMenuWindow().close();
					}
				});
			}

			const nodeRect = BX.pos(node);
			const menuParams = {
				menuId: menuId,
				anchor: node,
				menuItems: menuItems,
				settings: {
					autoHide: true,
					offsetTop: 0,
					offsetLeft: (nodeRect.width / 2),
					zIndex: 20,
					angle: {
						position: 'top',
						offset: (nodeRect.width / 2)
					}
				}
			};

			const menu = BX.Main.MenuManager.create(
				menuParams.menuId,
				menuParams.anchor,
				menuParams.menuItems,
				menuParams.settings
			);

			if (this.isVisibleItem(currentItem))
			{
				itemData.NODE = currentItem;
			}
			else
			{
				itemData.NODE = this.getItemAlias(currentItem);
			}

			this.editItemData = itemData;

			if ('menuItems' in menu && BX.Type.isArray(menu.menuItems))
			{
				menu.menuItems.forEach(function(current) {
					BX.Event.bind(current.layout.item, 'click', BX.delegate(this._onDocumentClick, this));
				}, this);
			}

			BX.onCustomEvent(window, 'BX.Main.InterfaceButtons:onBeforeCreateEditMenu', [menu, itemData, this]);

			this.editMenu = menu;

			return menu;
		},

		prepareMenuItemData: function(data)
		{
			const itemMenuData = {
				CLASS: "",
				CLASS_SUBMENU_ITEM: "",
				COUNTER: 0,
				COUNTER_ID: data.counterId,
				DATA_ID: data.dataId,
				HAS_CHILD: false,
				HAS_MENU: false,
				HTML: "",
				ID: data.id,
				IS_ACTIVE: false,
				IS_DISABLED: "false",
				IS_LOCKED: false,
				IS_PASSIVE: false,
				MAX_COUNTER_SIZE: 99,
				NODE: BX.Tag.render`<div id="${data.id}" class="main-buttons-item"></div>`,
				ON_CLICK: data.onClick,
				SUB_LINK: false,
				SUPER_TITLE: false,
				TEXT: data.text,
				TITLE: "",
				URL: data.url,
			};

			return itemMenuData;
		},

		addMenuItem: function(itemData)
		{
			const settings = this.getCurrentSettings();
			const settingsKeys = Object.keys(settings);
			const menuItemData = this.prepareMenuItemData(itemData);
			const item = this.createRootItem(menuItemData);

			const afterNode = this.getItemById(settingsKeys[settingsKeys.length - 1]);
			BX.Dom.insertAfter(item, afterNode);
			this.initItems();
		},

		deleteMenuItem: function(itemElement)
		{
			this.itemData.delete(itemElement);
			BX.Dom.remove(itemElement);
		},

		updateMenuItemText: function(itemElement, itemText)
		{
			if (!itemElement || !itemText)
			{
				return;
			}

			const itemData = this.getItemData(itemElement);
			itemData.TEXT = itemText;
			const item = this.getItemById(itemData.ID);
			const classItemText = 'main-buttons-item-text-box';
			const elementText = BX.Buttons.Utils.getByClass(item, classItemText);
			elementText.innerText = itemText;
		},

		getHomeItem: function()
		{
			const visibleItems = this.getVisibleItems();
			const firstVisibleItem = BX.Type.isArray(visibleItems) && visibleItems.length > 0 ? visibleItems[0] : null;
			if (!firstVisibleItem)
			{
				return null;
			}

			const itemData = this.getItemData(firstVisibleItem);
			const url = this.normalizeUrl(itemData['URL']);

			if (this.canBeHomed(url, itemData))
			{
				return { itemData, url, firstVisibleItem };
			}

			if (BX.Type.isArrayFilled(itemData['ITEMS']))
			{
				for (let i = 0; i < itemData['ITEMS'].length; i++)
				{
					const subItem = itemData['ITEMS'][i];
					if (subItem['IS_PINNED'] || subItem['IS_DISBANDED'] || subItem['IS_DELIMITER'])
					{
						continue;
					}

					const url = this.normalizeUrl(subItem['URL']);
					if (this.canBeHomed(url, subItem))
					{
						return { itemData: subItem, url, firstVisibleItem };
					}
				}
			}

			return null;
		},

		normalizeUrl: function(url)
		{
			if (!BX.Type.isStringFilled(url))
			{
				return '';
			}

			if (url.charAt(0) === '?')
			{
				const a = document.createElement('a');
				a.href = url;
				url = a.pathname + a.search;
			}

			return url;
		},

		canBeHomed: function(itemLink, itemData)
		{
			if (!BX.Type.isStringFilled(itemLink) || BX.Type.isStringFilled(itemData['ON_CLICK']))
			{
				return false;
			}

			if (BX.Reflection.getClass('BX.SidePanel.Instance'))
			{
				const rule = BX.SidePanel.Instance.getUrlRule(itemLink);
				if (rule)
				{
					return false;
				}
			}

			const event = new BX.Event.BaseEvent({ data: { itemLink, itemData }});
			BX.Event.EventEmitter.emit('BX.Main.InterfaceButtons:onBeforeFirstItemChange', event);

			return !event.isDefaultPrevented();
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isEditButton: function(item)
		{
			return BX.Dom.hasClass(item, this.classEditItemButton);
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isDragButton: function(item)
		{
			return BX.Dom.hasClass(item, this.classDragItemButton);
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isResetSettingsButton: function(item)
		{
			return BX.Dom.hasClass(item, this.classSettingsResetButton);
		},

		/**
		 * Calculate container height
		 * @return {number} Container height in pixels
		 */
		getContainerHeight: function()
		{
			const heights = this.getAllItems().map(function(current) {
				const currentStyle = getComputedStyle(current);

				return (
					BX.height(current) +
					parseInt(currentStyle.marginTop) +
					parseInt(currentStyle.marginBottom)
				);
			});

			return Math.max.apply(Math, heights);
		},

		/**
		 * Sets license window params in this.licenseParams
		 * @param {object} params Params object
		 */
		setLicenseWindowParams: function(params)
		{
			this.licenseParams = params || {};
		},

		/**
		 * Gets message by id
		 * @method message
		 * @private
		 * @param  {string} messageId
		 * @return {string}
		 */
		message: function(messageId)
		{
			let result;

			try
			{
				result = this.messages[messageId];
			}
			catch (error)
			{
				result = '';
			}

			return result;
		},

		/**
		 * Sets custom classes
		 * @param  {object} classes
		 * @return {undefined}
		 */
		setCustomClasses: function(classes)
		{
			if (!BX.Type.isPlainObject(classes))
			{
				return;
			}

			this.classItem = (classes.item || this.classItem);
			this.classItemSublink = (classes.itemSublink || this.classItemSublink);
			this.classItemText = (classes.itemText || this.classItemText);
			this.classItemCounter = (classes.itemCounter || this.classItemCounter);
			this.classItemIcon = (classes.itemIcon || this.classItemIcon);
			this.classItemMore = (classes.itemMore || this.classItemMore);
			this.classItemOver = (classes.itemOver || this.classItemOver);
			this.classMenuShown = (classes.menuShown || this.classMenuShown);
			this.classItemActive = (classes.itemActive || this.classItemActive);
			this.classItemDisabled = (classes.itemDisabled || this.classItemDisabled);
			this.classOnDrag = (classes.onDrag || this.classOnDrag);
			this.classDropzone = (classes.dropzone || this.classDropzone);
			this.classSeparator = (classes.separator || this.classSeparator);
			this.classSubmenuItem = (classes.submenuItem || this.classSubmenuItem);
			this.classSubmenu = (classes.submenu || this.classSubmenu);
			this.classSecret = (classes.secret || this.classSecret);
			this.classItemLocked = (classes.itemLocked || this.classItemLocked);
			this.classExtraItemLink = (classes.extraItemLink || this.classExtraItemLink);
			this.classExtraItemText = (classes.extraItemText || this.classExtraItemText);
			this.classExtraItemIcon = (classes.extraItemIcon || this.classExtraItemIcon);
			this.classExtraItemCounter = (classes.extraItemCounter || this.classExtraItemCounter);
		},

		/**
		 * Sets messages
		 * @param {object} messages Messages object
		 */
		setMessages: function(messages)
		{
			if (!BX.Type.isPlainObject(messages))
			{
				return;
			}

			this.messages = messages;
		},

		/**
		 * Makes full item id
		 * @private
		 * @method makeFullItemId
		 * @param  {string} itemId
		 * @return {*}
		 */
		makeFullItemId: function(itemId)
		{
			if (!BX.Type.isStringFilled(itemId))
			{
				return;
			}

			return [this.listContainer.id, itemId.replace('-', '_')].join('_');
		},

		/**
		 * Gets listContainer child by id
		 * @public
		 * @method getItemById
		 * @param  {string} itemId
		 * @return {object} dom node
		 */
		getItemById: function(itemId)
		{
			let resultItem = null;
			if (BX.Type.isStringFilled(itemId))
			{
				const realId = itemId.startsWith(this.listContainer.id) ? itemId : this.makeFullItemId(itemId);
				resultItem = BX.Buttons.Utils.getBySelector(this.listContainer, '#'+realId.replaceAll(':', '\\:'));
			}

			return resultItem;
		},

		/**
		 * Finds counter object
		 * @private
		 * @method getItemCounterObject
		 * @param  {HTMLElement} item
		 * @return {?HTMLElement} Counter dom node
		 */
		getItemCounterObject: function(item)
		{
			let result = null;

			if (BX.Type.isDomNode(item))
			{
				result = BX.Buttons.Utils.getByClass(item, this.classItemCounter);
			}

			return result;
		},

		/**
		 * Updates menu item counter
		 * @param {string} id - menu item id
		 * @param {*} value - counter value
		 */
		updateCounter: function(id, value)
		{
			if (id.indexOf('crm') === 0 && value < 0)
			{
				//HACK: Skip of CRM counter reset
				return;
			}

			this.updateItemsByCounterId(this.getAllItemsData(), id, value);
			this.updateMoreButtonCounter();
		},

		/**
		 * @private
		 * @param subItems
		 * @param counterId
		 * @param counterValue
		 * @param rootPath
		 */
		updateItemsByCounterId: function(subItems, counterId, counterValue, rootPath = [])
		{
			for (let i = 0; i < subItems.length; i++)
			{
				const subItem = subItems[i];
				if (subItem['COUNTER_ID'] === counterId)
				{
					subItem['COUNTER'] = Number(counterValue);
					this.setCounterValueById(counterId, subItem['COUNTER']);

					for (let index = rootPath.length - 1; index >= 0; index--)
					{
						const rootItem = rootPath[index];

						rootItem['COUNTER'] = rootItem['ITEMS'].reduce((currentValue, subItem) => {
							const isPinned = subItem['IS_PINNED'] === true;
							const counter = BX.Type.isNumber(subItem['COUNTER']) && !isPinned ? subItem['COUNTER'] : 0;

							return currentValue + counter;
						}, 0);

						this.setCounterValueById(rootItem['COUNTER_ID'], rootItem['COUNTER']);
					}
				}

				if (subItem['ITEMS'])
				{
					this.updateItemsByCounterId(subItem['ITEMS'], counterId, counterValue, [...rootPath, subItem]);
				}
			}
		},

		recalculateItemsCounters: function(items, rootPath = [])
		{
			let counterValue = 0;

			for (let i = 0; i < items.length; i++)
			{
				const item = items[i];

				if (item['ITEMS'])
				{
					item['COUNTER'] = this.recalculateItemsCounters(item['ITEMS'], [...rootPath, item]);
				}

				const isPinned = item['IS_PINNED'] === true;
				counterValue += BX.Type.isNumber(item['COUNTER']) && !isPinned ? item['COUNTER'] : 0;
			}

			for (let index = rootPath.length - 1; index >= 0; index--)
			{
				const rootItem = rootPath[index];

				rootItem['COUNTER'] = rootItem['ITEMS'].reduce((currentValue, subItem) => {
					const isPinned = subItem['IS_PINNED'] === true;
					const counter = BX.Type.isNumber(subItem['COUNTER']) && !isPinned ? subItem['COUNTER'] : 0;

					return currentValue + counter;
				}, 0);

				this.setCounterValueById(rootItem['COUNTER_ID'], rootItem['COUNTER']);
			}

			return counterValue;
		},

		/**
		 * @private
		 * @param counterId
		 * @param counterValue
		 */
		setCounterValueById: function(counterId, counterValue)
		{
			if (!BX.Type.isStringFilled(counterId))
			{
				return;
			}

			const counterText = counterValue > 99 ? '99+' : (counterValue > 0 ? counterValue : '');
			const elements = document.querySelectorAll(`[data-mib-counter-id="${counterId}"]`);
			Array.from(elements).forEach(element => {
				element.textContent = counterText;
			});
		},

		/**
		 * Sets counter of more button
		 * @param {*} value
		 */
		setMoreButtonCounter: function(value)
		{
			const counter = this.getItemCounterObject(this.moreButton);
			counter.textContent = value > 99 ? '99+' : (value > 0 ? value : '');
		},

		/**
		 * Binds on click on more button
		 * @method bindOnClickOnMoreButton
		 * @private
		 * @return {undefined}
		 */
		bindEventsOnMoreButton: function()
		{
			BX.Event.bind(this.moreButton, 'click', this.handleMoreButtonClick.bind(this));
			BX.Event.bind(this.moreButton, 'mouseenter', this.handleMoreButtonMouseEnter.bind(this));
			BX.Event.bind(this.moreButton, 'mouseleave', this.handleMoreButtonMouseLeave.bind(this));
		},

		/**
		 * Binds on tmp frame resize
		 * @method bindOnResizeFrame
		 * @private
		 */
		bindOnResizeFrame: function()
		{
			window.frames["maininterfacebuttonstmpframe-"+this.getId()].onresize = BX.throttle(this._onResizeHandler, 20, this);
		},

		/**
		 * Gets buttons list container id
		 * @return {string}
		 */
		getId: function()
		{
			return BX.Buttons.Utils.getByClass(this.getContainer(), this.classInner).id;
		},

		/**
		 * Gets all items
		 * @public
		 * @return {HTMLElement[]}
		 */
		getAllItems: function()
		{
			return BX.Buttons.Utils.getByClass(this.listContainer, this.classItem, true);
		},

		getAllItemsData: function()
		{
			return this.getAllItems().map(item => this.getItemData(item));
		},

		/**
		 * Gets only visible items
		 * @public
		 * @return {HTMLElement[]}
		 */
		getVisibleItems: function()
		{
			const allItems = this.getAllItems();
			let visibleItems = [];

			if (allItems && allItems.length)
			{
				visibleItems = allItems.filter((current) => {
					return this.isVisibleItem(current) && !this.isDisabled(current);
				});
			}

			return visibleItems;
		},

		/**
		 * Gets only hidden items
		 * @public
		 * @method getHiddenItems
		 * @return {HTMLElement[]}
		 */
		getHiddenItems: function()
		{
			const allItems = this.getAllItems();
			let hiddenItems = [];

			if (allItems && allItems.length)
			{
				hiddenItems = allItems.filter((current) => {
					return !this.isVisibleItem(current) && !this.isDisabled(current);
				});
			}

			return hiddenItems;
		},

		/**
		 * Gets only disabled items,
		 * as showed after separator in popup menu
		 * @public
		 * @method getDisabledItems
		 * @return {HTMLElement[]}
		 */
		getDisabledItems: function()
		{
			return this.getAllItems().filter((current) => {
				return this.isDisabled(current);
			});
		},

		/**
		 * Gets more button
		 * @public
		 * @returns {?HTMLElement} More button element
		 */
		getMoreButton: function()
		{
			const elements = this.getContainer().getElementsByClassName(this.classItemMore);

			return elements[0] || null;
		},

		/**
		 * Gets last visible item
		 * @private
		 * @method getLastVisibleItem
		 * @return {object} last visible item object
		 */
		getLastVisibleItem: function()
		{
			const visibleItems = this.getVisibleItems();
			let lastVisibleItem = null;

			if (BX.Type.isArray(visibleItems) && visibleItems.length)
			{
				lastVisibleItem = visibleItems[visibleItems.length - 1];
			}

			if (!BX.Type.isDomNode(lastVisibleItem))
			{
				lastVisibleItem = null;
			}

			return lastVisibleItem;
		},

		/**
		 * Gets last disabled item
		 * @private
		 * @method getLastVisibleItem
		 * @return {object} last visible item object
		 */
		getLastDisabledItem: function()
		{
			const visibleItems = this.getDisabledItems();
			let lastDisabledItem = null;

			if (BX.Type.isArray(visibleItems) && visibleItems.length)
			{
				lastDisabledItem = visibleItems[visibleItems.length - 1];
			}

			if (!BX.Type.isDomNode(lastDisabledItem))
			{
				lastDisabledItem = null;
			}

			return lastDisabledItem;
		},

		/**
		 * Moves "more button" in the end of the list
		 * @public
		 * @method adjustMoreButtonPosition
		 * @return {undefined}
		 */
		adjustMoreButtonPosition: function()
		{
			this.updateMoreButtonCounter();

			if (this.getMoreMenu())
			{
				this.getMoreMenu().getPopupWindow().adjustPosition();
			}
		},

		/**
		 * Gets submenu id
		 * @private
		 * @method getSubmenuId
		 * @param  {boolean} [isFull] Set true if your need to get id for popup window
		 * @return {string} id
		 */
		getMoreMenuId: function(isFull)
		{
			let id = '';

			if (BX.Type.isDomNode(this.listContainer) && BX.Type.isStringFilled(this.listContainer.id))
			{
				id = this.submenuIdPrefix + this.listContainer.id;
			}

			if (isFull)
			{
				id = this.submenuWindowIdPrefix + id;
			}

			return id;
		},

		getChildMenuId: function()
		{
			let id = '';

			if (BX.Type.isDomNode(this.listContainer) && BX.Type.isStringFilled(this.listContainer.id))
			{
				id = this.childMenuIdPrefix + this.listContainer.id;
			}

			return id;
		},

		/**
		 * Gets submenu item content
		 * @private
		 * @method getMoreMenuItemText
		 * @param  {HTMLElement} item
		 * @param pinRootItem
		 * @return {?string}
		 */
		getMenuItemText: function(item, pinRootItem = null)
		{
			const itemData = BX.Type.isElementNode(item) ? this.getItemData(item) : item;

			return BX.Tag.render`
				<span class="main-buttons-menu-popup-item">${[
					BX.Tag.render`<span class="${this.classItemIcon}"></span>`,
					this.createItemText(itemData),
					this.createItemCounter(itemData),
					pinRootItem && this.isEditEnabled() ? this.createItemPin(itemData, pinRootItem) : ''
				]}</span>
			`;
		},

		createRootItem: function(options)
		{
			let itemClass = this.classItem;

			itemClass += BX.Type.isStringFilled(options["CLASS"]) ? ' ' + options["CLASS"] : '';
			if (options['IS_PASSIVE'])
			{
				itemClass += ' --passive';
			}
			else if (options['IS_ACTIVE'])
			{
				if (BX.Type.isStringFilled(this.classItemActive))
				{
					itemClass += ' ' + this.classItemActive;
				}
				else
				{
					itemClass += ' main-buttons-item-active';
				}
			}

			if (options['HAS_MENU'])
			{
				itemClass += ' --has-menu';
			}

			if (options['IS_LOCKED'])
			{
				itemClass += ' --locked';
			}

			const div = BX.Tag.render`
				<div
					id="${options['ID']}"
					class="${itemClass}"
					data-disabled="${options['IS_DISABLED']}"
					data-class="${options['CLASS_SUBMENU_ITEM']}"
					data-id="${options['DATA_ID']}"
					data-top-menu-id="${this.getId()}"
					title=""
				>${[
					this.createItemLink(options, true),
					BX.Type.isPlainObject(options['SUB_LINK']) ? this.createItemSubLink(options['SUB_LINK']) : ''
				]}</div>
			`;

			this.setItemData(div, options);

			return div;
		},

		createItemLink: function(options, rootItemContext = false)
		{
			options = BX.Type.isPlainObject(options) ? options : {};

			let container;
			const classes = ['main-buttons-item-link', this.classExtraItemLink].join(' ').trim();
			if (BX.Type.isStringFilled(options['URL']))
			{
				container = BX.Tag.render`<a class="${classes}" href="${BX.Text.encode(options['URL'])}"></a>`;
			}
			else
			{
				container = BX.Tag.render`<span class="${classes}"></span>`;
			}

			BX.Dom.append(this.createItemIcon(options), container);
			BX.Dom.append(this.createItemText(options, rootItemContext), container);
			BX.Dom.append(this.createItemCounter(options), container);

			return container;
		},

		createItemSubLink: function(options)
		{
			options = BX.Type.isPlainObject(options) ? options : {};

			const className = BX.Type.isStringFilled(options['CLASS']) ? ' ' + options['CLASS'] : '';
			const url = BX.Type.isStringFilled(options['URL']) ? BX.Text.encode(options['URL']) : '';

			return BX.Tag.render`
				<a class="${this.classItemSublink}${className}" href="${url}"></a>
			`;
		},

		createItemIcon: function(options)
		{
			const classes = [this.classItemIcon, this.classExtraItemIcon].join(' ').trim();

			return BX.Tag.render`<span class="${classes}"></span>`;
		},

		createItemText: function(options, rootItemContext = false)
		{
			options = BX.Type.isPlainObject(options) ? options : {};

			const classes = [this.classItemText, this.classExtraItemText].join(' ').trim();
			let itemText = BX.Type.isStringFilled(options['TEXT']) ? options['TEXT'] : '';
			if (rootItemContext && itemText.length > this.maxItemLength)
			{
				itemText = itemText.substring(0, this.maxItemLength - 3) + '...';
			}

			let superTitle = '';
			if (BX.Type.isPlainObject(options['SUPER_TITLE']))
			{
				let { 'TEXT': text, 'CLASS': className, 'COLOR': color } = options['SUPER_TITLE'];
				className = BX.Type.isStringFilled(className) ? ` ${className}` : '';
				const style = BX.Type.isStringFilled(color) ? ` style="color:${color}"` : '';

				superTitle = BX.Tag.render`
					<span class="main-buttons-item-super-title${className}"${style}>${text}</span>
				`;
			}

			return BX.Tag.render`
				<span class="${classes}">${[
					BX.Tag.render`<span 
						class="main-buttons-item-drag-button"
						onclick="${this.handleDragButtonClick.bind(this)}" 
						data-slider-ignore-autobinding="true"
					></span>`,
					superTitle,
					BX.Tag.render`
						<span class="main-buttons-item-text-title">
							<span class="main-buttons-item-text-box">${
								BX.Text.encode(itemText)
							}<span class="main-buttons-item-menu-arrow"></span></span>
						</span>
					`,
					BX.Tag.render`<span 
						class="main-buttons-item-edit-button"
						onclick="${this.handleEditButtonClick.bind(this)}" 
						data-slider-ignore-autobinding="true"
					></span>`,
					BX.Tag.render`<span class="main-buttons-item-text-marker"></span>`,
				]}</span>
			`;
		},

		createItemCounter: function(options)
		{
			options = BX.Type.isPlainObject(options) ? options : {};

			const classes = [this.classItemCounter, this.classExtraItemCounter].join(' ').trim();

			let counter = '';
			const maxCounterSize = BX.Type.isNumber(options['MAX_COUNTER_SIZE']) ? options['MAX_COUNTER_SIZE'] : 99;
			if (BX.Type.isNumber(options['COUNTER']) && options['COUNTER'] > 0)
			{
				counter = options['COUNTER'] > maxCounterSize ? `${maxCounterSize}+` : options['COUNTER'];
			}

			const counterId = BX.Type.isStringFilled(options['COUNTER_ID']) ? options['COUNTER_ID'] : '';

			return BX.Tag.render`<span data-mib-counter-id="${counterId}" class="${classes}">${counter}</span>`;
		},

		createItemPin: function(itemData, rootNode)
		{
			return BX.Tag.render`
				<span class="main-buttons-item-pin" 
					data-slider-ignore-autobinding="true"
					onclick="${this.handleItemPin.bind(this, itemData, rootNode)}"
					onmouseenter="${this.handleItemPinEnter.bind(this)}"
					onmouseleave="${this.handleItemPinLeave.bind(this)}"
				></span>
			`;
		},

		/**
		 * @param {HTMLElement} item
		 * @return {string}
		 */
		getLockedClass: function(item)
		{
			let result = '';
			if (BX.Type.isDomNode(item) && this.isLocked(item))
			{
				result = this.classItemLocked;
			}

			return result;
		},

		/**
		 * Gets More Menu items
		 * @private
		 * @method getMoreMenuItems
		 * @return {HTMLElement[]}
		 */
		getMoreMenuItems: function()
		{
			const allItems = this.getAllItems();
			const hiddenItems = this.getHiddenItems();
			const disabledItems = this.getDisabledItems();
			const result = [];

			if (allItems.length)
			{
				allItems.forEach(current => {
					if (hiddenItems.indexOf(current) === -1 && disabledItems.indexOf(current) === -1)
					{
						const itemData = this.getItemData(current);
						result.push({
							id: itemData['DATA_ID'],
							html: this.getMenuItemText(current),
							href: itemData['URL'],
							onclick: itemData['ON_CLICK'],
							title: current.getAttribute('title'),
							className: [
								this.classSubmenuItem,
								this.getIconClass(current),
								this.classSecret,
								this.getAliasLink(current),
								this.getLockedClass(current)
							].join(' '),
							items: this.getMoreMenuSubItems(current),
							events: {
								onMouseEnter: this.handleMoreMenuItemMouseEnter
							}
						});
					}
				});
			}

			if (hiddenItems.length)
			{
				hiddenItems.forEach(current => {
					const itemData = this.getItemData(current);
					const className = [
						this.classSubmenuItem,
						this.getIconClass(current),
						this.getAliasLink(current),
						this.getLockedClass(current)
					];

					if (itemData['IS_ACTIVE'] === true)
					{
						className.push(this.classItemActive);
					}

					result.push({
						id: itemData['DATA_ID'],
						html: this.getMenuItemText(current),
						href: itemData['URL'],
						onclick: itemData['ON_CLICK'],
						title: current.getAttribute('title'),
						className: className.join(' '),
						items: this.getMoreMenuSubItems(current),
						events: {
							onMouseEnter: this.handleMoreMenuItemMouseEnter
						}
					});
				});
			}

			if (this.isSettingsEnabled)
			{
				result.push({
					delimiter: true,
					html: '<span>' + this.message('MIB_MANAGE') + '</span>',
					className: [
						this.classSeparator,
						this.classSubmenuItem,
						this.classManage
					].join(' ')
				});

				result.push({
					html: this.message('MIB_SETTING_MENU_ITEM'),
					className: [
						this.classSettingMenuItem,
						this.classSubmenuItem
					].join(' ')
				});

				const btnClasses = [
					'ui-btn',
					this.theme === 'default' ? 'ui-btn-sm' : 'ui-btn-xs',
					'ui-btn-success-light',
					'ui-btn-no-caps',
					'ui-btn-round',
					'ui-btn-icon-main-buttons-apply',
				];

				result.push({
					html: `
					<span class="${btnClasses.join(' ')}">
						<span class="ui-btn-text">${this.message('MIB_APPLY_SETTING_MENU_ITEM')}</span>
					</span>`,
					className: [
						this.classSettingsApplyButton,
						this.classSubmenuItem
					].join(' ')
				});

				result.push({
					html: this.message('MIB_RESET_SETTINGS'),
					className: [this.classSettingsResetButton, this.classSubmenuItem].join(' ')
				});

				result.push({
					delimiter: true,
					html: '<span>' + this.message('MIB_HIDDEN') + '</span>',
					className: [
						this.classSeparator,
						this.classSubmenuItem,
						this.classHiddenLabel
					].join(' ')
				});

				if (!disabledItems.length)
				{
					result.push({
						html: '<span>'+this.message('MIB_NO_HIDDEN')+'</span>',
						className: [
							this.classSubmenuItem,
							this.classSubmenuNoHiddenItem
						].join(' ')
					});
				}

				if (disabledItems.length)
				{
					disabledItems.forEach(current => {
						const itemData = this.getItemData(current);
						const className = [
							this.classSubmenuItem,
							this.classItemDisabled,
							this.getIconClass(current),
							this.getAliasLink(current),
							this.getLockedClass(current)
						];

						if (itemData['IS_ACTIVE'] === true)
						{
							className.push(this.classItemActive);
						}

						result.push({
							id: itemData['DATA_ID'],
							html: this.getMenuItemText(current),
							href: itemData['URL'],
							onclick: itemData['ON_CLICK'],
							title: current.getAttribute('title'),
							className: className.join(' '),
							items: this.getMoreMenuSubItems(current),
							events: {
								onMouseEnter: this.handleMoreMenuItemMouseEnter
							}
						});
					});
				}
			}

			return result;
		},

		getMenuItems: function(item)
		{
			return this.createMenuItems(this.getItemData(item), item);
		},

		getMoreMenuSubItems: function(item)
		{
			return this.createMenuItems(this.getItemData(item), null);
		},

		createMenuItems: function(itemData, pinRootItem = null)
		{
			if (!BX.Type.isArrayFilled(itemData['ITEMS']))
			{
				return [];
			}

			const items = itemData['ITEMS'];
			const result = [];
			for (let i = 0; i < items.length; i++)
			{
				const item = items[i];
				if (item['IS_PINNED'] || item['IS_DISBANDED'])
				{
					continue;
				}

				const delimiter = item['IS_DELIMITER'] === true;
				if (delimiter)
				{
					const firstItem = result.length === 0;
					const prevItem = result[result.length - 1];
					if (firstItem || (prevItem && prevItem['delimiter'] === true))
					{
						continue;
					}
				}

				const className = ['menu-popup-no-icon', 'main-buttons-menu-item'];
				if (item['IS_ACTIVE'] === true)
				{
					className.push('main-buttons-menu-item-active');
				}

				const locked = BX.Text.toBoolean(item['IS_LOCKED']);
				if (locked)
				{
					className.push(this.classItemLocked);
				}

				if (this.isEditEnabled())
				{
					className.push(this.classEditState);
				}

				let menuItem;
				if (delimiter)
				{
					menuItem = {
						delimiter: true,
						className: className.join(' '),
						text: item['TEXT'],
					};
				}
				else
				{
					menuItem = {
						html: this.getMenuItemText(item, pinRootItem),
						href: item['URL'],
						onclick: item['ON_CLICK'],
						title: item['TITLE'],
						className: className.join(' '),
					};
				}

				const ajaxMode = item.hasOwnProperty("AJAX_OPTIONS");
				if (ajaxMode)
				{
					menuItem.cacheable = true;
					menuItem.events = this._getEvents(item['AJAX_OPTIONS']);
					menuItem.items = [
						{
							id: 'loading',
							text: this.message('MIB_MAIN_BUTTONS_LOADING')
						}
					];
				}
				else if (BX.Type.isArrayFilled(item['ITEMS']) && !this.isEditEnabled())
				{
					const subItems = this.createMenuItems(item, pinRootItem);
					if (subItems.length)
					{
						menuItem.items = subItems;
					}
				}

				result.push(menuItem);
			}

			if (result.length && result[result.length - 1]['delimiter'] === true)
			{
				result.pop();
			}

			return result;
		},

		_setAjaxMode: function(items)
		{
			for (let itemId in items)
			{
				if (!items.hasOwnProperty(itemId))
				{
					continue;
				}

				if (items[itemId].hasOwnProperty("ajaxOptions"))
				{
					items[itemId].cacheable = true;
					items[itemId].events = this._getEvents(items[itemId]["ajaxOptions"]);
					items[itemId].items = [
						{
							id: "loading",
							text: this.message("MIB_MAIN_BUTTONS_LOADING")
						}
					];
				}
			}
		},

		_getEvents: function(ajaxOptions)
		{
			return {
				onSubMenuShow: () => {
					if (this.subMenuLoaded)
					{
						return;
					}

					const submenu = this.getSubMenu();
					submenu.removeMenuItem("loading");
					const loadingItem = submenu.getMenuItem('loading');

					this.getSubItems(ajaxOptions)
						.then(items => {
							this._setAjaxMode(items);
							this.subMenuLoaded = true;
							this.addSubMenu(items);
							this.showSubMenu();
						})
						.catch(text => {
							if (loadingItem)
							{
								loadingItem.getLayout().text.innerText = text;
							}
						})
					;
				}
			};
		},

		getSubItems: function(ajaxOptions)
		{
			return new Promise(function(resolve, reject) {
				if (this.progress)
				{
					reject(this.message("MIB_MAIN_BUTTONS_LOADING"));
					return;
				}

				if (ajaxOptions.mode === "component")
				{
					this.progress = true;
					BX.ajax.runComponentAction(ajaxOptions.component, ajaxOptions.action, {
						mode: ajaxOptions.componentMode,
						signedParameters: (ajaxOptions.signedParameters ? ajaxOptions.signedParameters : {}),
						data: ajaxOptions.data,
					})
					.then(response => {
						this.progress = false;
						resolve(response.data);
					});
				}
				else
				{
					this.progress = true;
					BX.ajax.runAction(ajaxOptions.action, {
						data: ajaxOptions.data,
					})
					.then(response => {
						this.progress = false;
						resolve(response.data);
					});
				}
			});
		},

		/**
		 * Gets BX.PopupMenu.show arguments
		 * @private
		 * @method getMoreMenuArgs
		 * @return {*[]} Arguments
		 */
		getMoreMenuArgs: function()
		{
			const menuId = this.getMoreMenuId();
			const moreButton = this.moreButton;
			const menuItems = this.getMoreMenuItems();

			let params;
			const maxHeight = 800;
			if (this.theme === 'default')
			{
				const maxWidth = 350;
				const activeItemMargin = 25;
				params = {
					autoHide: false,
					compatibleMode: false,
					offsetLeft: -activeItemMargin,
					offsetTop: 4,
					cacheable: false,
					className: 'main-buttons-menu-popup main-buttons-more-menu-popup',
					minWidth: 240,
					maxWidth: maxWidth,
					maxHeight: maxHeight,
					subMenuOptions: {
						className: 'main-buttons-menu-popup main-buttons-more-menu-popup --sub-menu',
						minWidth: 150,
						maxWidth: maxWidth,
						events: {
							onFirstShow: this.handleMoreMenuFirstShow.bind(this),
						}
					},
					bindOptions: {
						position: 'bottom',
						forceTop: true
					},
					events: {
						onClose: this.handleMoreMenuClose.bind(this),
						onDestroy: this.handleMoreMenuClose.bind(this),
						onFirstShow: this.handleMoreMenuFirstShow.bind(this),
						onShow: this.handleMoreMenuShow.bind(this),
						onBeforeAdjustPosition: this.handleAdjustPosition.bind(this, moreButton)
					}
				};
			}
			else
			{
				const moreButtonTitle = this.moreButton.querySelector('.main-buttons-item-text-title');
				const targetNodeWidth = moreButtonTitle.offsetWidth;
				const popupWidth = 250;
				const offsetLeft = (targetNodeWidth / 2) - (popupWidth / 2) + BX.Main.Popup.getOption('angleLeftOffset');
				const angleShift = BX.Main.Popup.getOption('angleLeftOffset') - BX.Main.Popup.getOption('angleMinTop');
				const angleOffset = popupWidth / 2 - angleShift;

				params = {
					autoHide: false,
					compatibleMode: false,
					offsetTop: 4,
					offsetLeft: offsetLeft,
					minWidth: popupWidth,
					maxWidth: popupWidth,
					maxHeight: maxHeight,
					angle: {
						position: 'top',
						offset: angleOffset
					},
					className: 'main-buttons-default-menu-popup main-buttons-more-menu-popup',
					subMenuOptions: {
						className: 'main-buttons-default-menu-popup main-buttons-more-menu-popup --sub-menu',
						minWidth: null,
						events: {
							onFirstShow: this.handleMoreMenuFirstShow.bind(this),
						}
					},
					cacheable: false,
					bindOptions: {
						position: 'bottom',
						forceTop: true
					},
					events: {
						onClose: this.handleMoreMenuClose.bind(this),
						onDestroy: this.handleMoreMenuClose.bind(this),
						onFirstShow: this.handleMoreMenuFirstShow.bind(this),
						onShow: this.handleMoreMenuShow.bind(this)
					}
				};
			}

			if (this.isEditEnabled())
			{
				params.className += ' ' + this.classEditState;
			}

			return [menuId, moreButton, menuItems, params];
		},

		getChildMenuArgs: function(item)
		{
			const maxHeight = 800;
			if (this.theme === 'default')
			{
				const activeItemMargin = 25;
				const maxWidth = 350;

				return {
					autoHide: false,
					compatibleMode: false,
					offsetLeft: -activeItemMargin,
					offsetTop: 4,
					cacheable: false,
					className: 'main-buttons-menu-popup',
					maxWidth: maxWidth,
					minWidth: item.offsetWidth + activeItemMargin * 2 + 30,
					maxHeight: maxHeight,
					subMenuOptions: {
						className: 'main-buttons-menu-popup --sub-menu',
						minWidth: null,
						events: {
							onFirstShow: this._onChildMenuFirstShow.bind(this)
						}
					},
					bindOptions: {
						position: 'bottom',
						forceTop: true
					},
					events: {
						onFirstShow: this._onChildMenuFirstShow.bind(this),
						onShow: this._onChildMenuShow.bind(this, item),
						onClose: this._onChildMenuClose.bind(this, item),
						onDestroy: this._onChildMenuClose.bind(this, item),
						onBeforeAdjustPosition: this.handleAdjustPosition.bind(this, item)
					}
				};
			}
			else
			{
				const maxWidth = 250;
				return {
					autoHide: false,
					compatibleMode: false,
					offsetTop: 4,
					cacheable: false,
					className: 'main-buttons-default-menu-popup',
					minWidth: Math.min(item.offsetWidth + 25 * 2 + 30, maxWidth),
					maxWidth: maxWidth,
					maxHeight: maxHeight,
					bindOptions: {
						position: 'bottom',
						forceTop: true
					},
					subMenuOptions: {
						className: 'main-buttons-default-menu-popup --sub-menu',
						minWidth: null,
						events: {
							onFirstShow: this._onChildMenuFirstShow.bind(this)
						}
					},
					events: {
						onFirstShow: this._onChildMenuFirstShow.bind(this),
						onShow: this._onChildMenuShow.bind(this, item),
						onClose: this._onChildMenuClose.bind(this, item),
						onDestroy: this._onChildMenuClose.bind(this, item)
					}
				};
			}
		},

		centerPopupArrow(popup, item)
		{
			const targetNodeWidth = item.offsetWidth;
			const popupWidth = popup.getPopupContainer().offsetWidth;
			const offsetLeft = (targetNodeWidth / 2) - (popupWidth / 2);
			const angleShift = BX.Main.Popup.getOption('angleLeftOffset') - BX.Main.Popup.getOption('angleMinTop');

			popup.setAngle({ offset: popupWidth / 2 - angleShift });
			popup.setOffset({ offsetLeft: offsetLeft + BX.Main.Popup.getOption('angleLeftOffset') });
		},

		/**
		 * Controls the visibility of more button
		 */
		visibleControlMoreButton: function()
		{
			const hiddenItems = this.getHiddenItems();
			if (!hiddenItems.length)
			{
				this.getMoreButton().style.display = 'none';
			}
			else
			{
				this.getMoreButton().style.display = '';
			}
		},

		/**
		 * Creates submenu
		 * @return {BX.PopupMenu}
		 */
		createMoreMenu: function()
		{
			const menu = BX.Main.MenuManager.create(...this.getMoreMenuArgs());
			if (this.isSettingsEnabled)
			{
				this.dragAndDropInitInSubmenu();
			}

			menu.getMenuItems().forEach(function(menuItem) {
				const container = menuItem.getLayout().item;
				BX.Event.bind(container, 'click', BX.delegate(this._onDocumentClick, this));
			}, this);

			return menu;
		},

		createChildMenu: function(item)
		{
			const menuItems = this.getMenuItems(item);
			if (menuItems.length)
			{
				const menu = BX.Main.MenuManager.create(
					this.getChildMenuId(),
					item,
					menuItems,
					this.getChildMenuArgs(item),
				);

				if (!this.isEditEnabled() && this.isSettingsEnabled)
				{
					const handleDragStart = () => {
						this.showMoreMenu();
						this.enableEdit();

						this.destroyChildMenu();
						this.showChildMenu(item);
					};

					menu.getMenuItems().forEach((menuItem) => {
						const container = menuItem.getLayout().item;
						container.draggable = true;

						BX.Event.bind(container, 'dragstart', handleDragStart);
					});
				}

				return menu;
			}

			return null;
		},

		/**
		 * Shows More Menu
		 * @public
		 * @method showMoreMenu
		 * @return {undefined}
		 */
		showMoreMenu: function()
		{
			clearTimeout(this.submenuLeaveTimeout);

			if (!this.isEditEnabled())
			{
				this.closeChildMenu();
			}

			let submenu = this.getMoreMenu();
			if (submenu !== null)
			{
				submenu.getPopupWindow().show();
			}
			else
			{
				this.destroyMoreMenu();
				submenu = this.createMoreMenu();
				submenu.getPopupWindow().show();
			}

			this.setMoreMenuShown(true);
			this.activateItem(this.moreButton);

			if (this.isEditEnabled())
			{
				submenu.getPopupWindow().setAutoHide(false);
			}
		},

		showChildMenu: function(item)
		{
			clearTimeout(this.childMenuLeaveTimeout);

			if (!this.isEditEnabled())
			{
				this.closeMoreMenu();
			}

			if (!this.isVisibleItem(item))
			{
				return;
			}

			const currentMenu = BX.Main.MenuManager.getMenuById(this.getChildMenuId());
			if (currentMenu && currentMenu.bindElement === item)
			{
				currentMenu.getPopupWindow().show();
				this.destroyItemEditMenu();
			}
			else
			{
				this.destroyChildMenu(item);
				const childMenu = this.createChildMenu(item);
				if (childMenu)
				{
					childMenu.getPopupWindow().show();
					this.destroyItemEditMenu();
				}
			}
		},

		/**
		 * Closes submenu
		 * @public
		 * @method closeMoreMenu
		 * @return {undefined}
		 */
		closeMoreMenu: function()
		{
			const submenu = this.getMoreMenu();
			if (submenu === null)
			{
				return;
			}

			submenu.getPopupWindow().close();
			if (!this.isActiveInMoreMenu())
			{
				this.deactivateItem(this.moreButton);
			}

			this.setMoreMenuShown(false);
		},

		closeChildMenu: function()
		{
			const childMenu = this.getChildMenu();

			if (childMenu === null)
			{
				return;
			}

			this.closePinHint();
			childMenu.close();
		},

		/**
		 * Gets current More Menu
		 * @public
		 * @method getMoreMenu
		 * @return {BX.Main.Menu}
		 */
		getMoreMenu: function()
		{
			return BX.Main.MenuManager.getMenuById(this.getMoreMenuId());
		},

		/**
		 * Gets current Sub Menu
		 * @public
		 * @method getMoreMenu
		 * @return {BX.Main.Menu}
		 */
		getChildMenu: function()
		{
			return BX.Main.MenuManager.getMenuById(this.getChildMenuId());
		},

		/**
		 * Destroys More Menu
		 * @private
		 * @method destroySubmenu
		 * @return {undefined}
		 */
		destroyMoreMenu: function()
		{
			BX.Main.MenuManager.destroy(this.getMoreMenuId());
		},

		destroyChildMenu: function()
		{
			BX.Main.MenuManager.destroy(this.getChildMenuId());
		},

		/**
		 * Refreshes submenu
		 * @public
		 * @method refreshMoreMenu
		 * @return {undefined}
		 */
		refreshMoreMenu: function()
		{
			const submenu = this.getMoreMenu();
			if (submenu === null)
			{
				return;
			}

			const args = this.getMoreMenuArgs();
			if (BX.Type.isArray(args))
			{
				this.destroyMoreMenu();
				this.createMoreMenu();
				this.showMoreMenu();
			}
		},

		/**
		 * Sets value this.isSubmenuShown
		 * @private
		 * @method setMoreMenuShown
		 * @param {boolean} value
		 */
		setMoreMenuShown: function(value)
		{
			this.isSubmenuShown = false;
			if (BX.type.isBoolean(value))
			{
				this.isSubmenuShown = value;
			}

			if (this.isSubmenuShown)
			{
				BX.Dom.addClass(this.moreButton, this.classMenuShown);
			}
			else
			{
				BX.Dom.removeClass(this.moreButton, this.classMenuShown);
			}
		},

		/**
		 * Adds class active for item
		 * @private
		 * @method activateItem
		 * @param  {object} item
		 * @return {undefined}
		 */
		activateItem: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			if (!BX.Dom.hasClass(item, this.classItemActive))
			{
				BX.Dom.addClass(item, this.classItemActive);
			}
		},

		/**
		 * Removes class active for item
		 * @private
		 * @method deactivateItem
		 * @param  {object} item
		 * @return {undefined}
		 */
		deactivateItem: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			if (BX.Dom.hasClass(item, this.classItemActive))
			{
				BX.Dom.removeClass(item, this.classItemActive);
			}
		},

		/**
		 * Gets current component settings
		 * @public
		 * @method getCurrentSettings
		 * @return {object}
		 */
		getCurrentSettings: function()
		{
			const settings = {};

			this.getAllItems().forEach((current, index) => {
				settings[current.id] = {
					sort: index,
					isDisabled: this.isDisabled(current),
					isPinned: this.isPinned(current),
				};
			});

			return settings;
		},

		initSaving: function(ajaxSettings)
		{
			this.sendOptions = this.sendOptions.bind(this);
			this.optionsToSave = [];
			this.debouncedSendOptions = BX.debounce(this.sendOptions, 5000);
			if (BX.Type.isPlainObject(ajaxSettings))
			{
				this.ajaxSettings = {
					componentName: ajaxSettings.componentName,
					signedParams: ajaxSettings.signedParams,
				};
			}
		},

		/**
		 * Sends settings to the server
		 * @private
		 * @method sendSettings
		 * @return {undefined}
		 */
		sendOptions: function()
		{
			if (this.optionsToSave.length <= 0)
			{
				return;
			}

			const dataToSend = {};
			this.optionsToSave.forEach(function(item){
				dataToSend[item.name] = item.value;
			});
			this.optionsToSave = [];
			window.removeEventListener("beforeunload", this.sendOptions);
			BX.removeCustomEvent("SidePanel.Slider:onClose", this.sendOptions);

			return BX.ajax.runComponentAction(
				this.ajaxSettings.componentName,
				'save',
				{
					mode: 'class',
					signedParameters: this.ajaxSettings.signedParams,
					data: {
						options: dataToSend
					}
				}
			);
		},

		/**
		 * Collects settings into storage and then sends
		 * @private
		 * @method saveOptions
		 * @return {undefined}
		 * @param name
		 * @param value
		 */
		saveOptions: function(name, value)
		{
			if (this.ajaxSettings)
			{
				if (this.optionsToSave.length <= 0)
				{
					window.addEventListener("beforeunload", this.sendOptions);
					BX.addCustomEvent("SidePanel.Slider:onClose", this.sendOptions);
				}
				this.optionsToSave.push({'name': name, 'value': value});
				this.debouncedSendOptions();
			}
			else if (this.listContainer.id)
			{
				BX.userOptions.save('ui', this.listContainer.id, name, value);
			}
		},

		/**
		 * Saves current component settings
		 * @public
		 * @method saveSettings
		 * @return {undefined}
		 */
		saveSettings: function()
		{
			const settings = this.getCurrentSettings();
			const paramName = 'settings';

			if (!BX.Type.isPlainObject(settings))
			{
				return;
			}

			if (BX.Type.isDomNode(this.listContainer) && 'id' in this.listContainer)
			{
				this.saveOptions(paramName, JSON.stringify(settings));
				const homeItem = this.getHomeItem();
				if (homeItem)
				{
					const { itemData, url: firstPageLink, firstVisibleItem } = homeItem;
					if (itemData)
					{
						if (this.lastHomeLink !== firstPageLink)
						{
							this.saveOptions('firstPageLink', firstPageLink);
							this.sendOptions(); // force send
							BX.onCustomEvent(
								'BX.Main.InterfaceButtons:onFirstItemChange',
								[firstPageLink, firstVisibleItem],
							);
						}

						this.lastHomeLink = firstPageLink;
					}
				}
			}
		},

		resetSettings: function()
		{
			let button = null;
			const confirmPopup = BX.PopupWindowManager.create(
				this.listContainer.id + "_reset_popup",
				null,
				{
					content: this.message('MIB_RESET_ALERT'),
					autoHide: false,
					overlay: true,
					closeByEsc : true,
					closeIcon : true,
					draggable : { restrict : true},
					titleBar: this.message("MIB_RESET_SETTINGS"),
					buttons: [
						(button = new BX.PopupWindowButton({
							text: this.message("MIB_RESET_BUTTON"),
							className: 'popup-window-button-create',
							events: {
								click: () => {
									if (BX.Dom.hasClass(button.buttonNode, "popup-window-button-wait"))
									{
										return;
									}

									BX.Dom.addClass(button.buttonNode, "popup-window-button-wait");

									this.handleResetSettings(error => {
										if (error)
										{
											BX.Dom.removeClass(button.buttonNode, "popup-window-button-wait");
											confirmPopup.setContent(error);
										}
										else
										{
											this.saveOptions('settings', JSON.stringify({}));
											this.saveOptions('firstPageLink', '');
											this.sendOptions()
												.then(function() {
													window.location.reload();
												})
												.catch(function() {
													window.location.reload();
												})
											;
										}
									});
								}
							}
						})),
						new BX.PopupWindowButtonLink({
							text: this.message("MIB_CANCEL_BUTTON"),
							className: "popup-window-button-link-cancel",
							events: {
								click: function() {
									this.popupWindow.close();
								}
							}
						})
					]
				}
			);

			confirmPopup.show();
		},

		/**
		 * @callback cb
		 */
		handleResetSettings: function(cb)
		{
			const promises = [];
			BX.onCustomEvent("BX.Main.InterfaceButtons:onBeforeResetMenu", [promises, this]);

			let promise = new BX.Promise();
			const firstPromise = promise;

			for (let i = 0; i < promises.length; i++)
			{
				promise = promise.then(promises[i]);
			}

			promise.then(
				function(result) {
					cb(null, result);
				},
				function(reason) {
					cb(reason, null);
				}
			);

			firstPromise.fulfill();
		},

		/**
		 * Moves alias buttons
		 * @private
		 * @method moveButtonAlias
		 * @param  {HTMLElement} item
		 * @param insertAfter
		 * @return {undefined}
		 */
		moveButtonAlias: function(item, insertAfter)
		{
			if (!item || !this.dragItem)
			{
				return;
			}

			const aliasDragItem = this.getItemAlias(this.dragItem);
			const aliasItem = this.getItemAlias(item);

			if (this.isListItem(aliasDragItem))
			{
				if (aliasItem)
				{
					if (insertAfter)
					{
						BX.Dom.insertAfter(aliasDragItem, aliasItem);
					}
					else
					{
						this.listContainer.insertBefore(aliasDragItem, aliasItem);
					}
				}
				else
				{
					this.listContainer.appendChild(aliasDragItem);
				}
			}

			if (this.getMoreMenu())
			{
				this.getMoreMenu().getPopupWindow().adjustPosition();
			}
		},

		/**
		 * Moves drag item before item, or appendChild to container
		 * @private
		 * @method moveButton
		 * @param  {HTMLElement} item
		 * @param insertAfter
		 * @return {*}
		 */
		moveButton: function(item, insertAfter)
		{
			if (!BX.Type.isDomNode(item) || !BX.Type.isDomNode(this.dragItem))
			{
				return;
			}

			if (this.isListItem(item))
			{
				if (this.isDisabled(this.dragItem))
				{
					this.dragItem.dataset.disabled = 'false';
				}

				if (BX.Type.isDomNode(item))
				{
					if (insertAfter)
					{
						BX.Dom.insertAfter(this.dragItem, item);
					}
					else
					{
						this.listContainer.insertBefore(this.dragItem, item);
					}
				}
				else
				{
					this.listContainer.appendChild(this.dragItem);
				}
			}

			if (this.isSubmenuItem(item))
			{
				if (insertAfter)
				{
					BX.Dom.insertAfter(this.dragItem, item);
				}
				else
				{
					this.getMoreMenuContainer().insertBefore(this.dragItem, item);
				}
			}
		},

		/**
		 * Gets submenu container
		 * @private
		 * @method getSubmenuContainer
		 * @return {object}
		 */
		getMoreMenuContainer: function()
		{
			const submenu = this.getMoreMenu();
			let result = null;

			if (submenu !== null)
			{
				result = submenu.itemsContainer;
			}

			return result;
		},

		/**
		 * Gets next element with className
		 * @param {?HTMLElement} item
		 * @param {string} className
		 * @returns {?HTMLElement}
		 */
		findNextSiblingByClass: function(item, className)
		{
			//noinspection UnnecessaryLocalVariableJS
			const sourceItem = item;
			for (; !!item; item = item.nextElementSibling)
			{
				if (className)
				{
					if (BX.Dom.hasClass(item, className) && item !== sourceItem)
					{
						return item;
					}
				}
				else
				{
					return null;
				}
			}

			return null;
		},

		/**
		 * Finds children item by className
		 * @private
		 * @method findChildrenByClassName
		 * @param  {HTMLElement} item
		 * @param  {string} className
		 * @return {?HTMLElement}
		 */
		findChildrenByClassName: function(item, className)
		{
			let result = null;
			if (BX.Type.isDomNode(item) && BX.Type.isStringFilled(className))
			{
				result = BX.Buttons.Utils.getByClass(item, className);
			}

			return result;
		},

		/**
		 * Initialise Drag And Drop
		 * @private
		 * @method dragAndDropInit
		 * @return {undefined}
		 */
		initItems: function()
		{
			this.getAllItems().forEach((current) => {
				this.initItem(current);
			});
		},

		initItem: function(item)
		{
			if (this.isSettingsEnabled)
			{
				item.setAttribute('draggable', 'true');
				item.setAttribute('tabindex', '-1');

				BX.Event.bind(item, 'dragstart', BX.delegate(this._onDragStart, this));
				BX.Event.bind(item, 'dragend', BX.delegate(this._onDragEnd, this));
				BX.Event.bind(item, 'dragenter', BX.delegate(this._onDragEnter, this));
				BX.Event.bind(item, 'dragover', BX.delegate(this._onDragOver, this));
				BX.Event.bind(item, 'dragleave', BX.delegate(this._onDragLeave, this));
				BX.Event.bind(item, 'drop', BX.delegate(this._onDrop, this));

				item.dataset.link = 'item-' + BX.Text.getRandom().toLowerCase();
			}

			BX.Event.bind(item, 'click', this._handleItemClick.bind(this));
			BX.Event.bind(item, 'mouseenter', this.handleItemMouseEnter.bind(this));
			BX.Event.bind(item, 'mouseleave', this.handleItemMouseLeave.bind(this));
		},

		/**
		 * Initialise Drag And Drop for submenu items
		 * @private
		 * @method dragAndDropInitInSubmenu
		 * @return {undefined}
		 */
		dragAndDropInitInSubmenu: function()
		{
			const submenu = this.getMoreMenu();
			if (!submenu)
			{
				return;
			}

			const submenuItems = submenu.menuItems;

			submenuItems.forEach((current) => {
				if (
					this.isSeparator(current.layout.item)
					|| this.isSettings(current.layout.item)
					|| this.isApplySettingsButton(current.layout.item)
					|| this.isResetSettingsButton(current.layout.item)
				)
				{
					current.layout.item.draggable = false;
				}
				else
				{
					current.layout.item.draggable = true;
					current.layout.item.dataset.sortable = true;

					BX.Event.bind(current.layout.item, 'dragstart', BX.delegate(this._onDragStart, this));
					BX.Event.bind(current.layout.item, 'dragenter', BX.delegate(this._onDragEnter, this));
					BX.Event.bind(current.layout.item, 'dragover', BX.delegate(this._onDragOver, this));
					BX.Event.bind(current.layout.item, 'dragleave', BX.delegate(this._onDragLeave, this));
					BX.Event.bind(current.layout.item, 'dragend', BX.delegate(this._onDragEnd, this));
					BX.Event.bind(current.layout.item, 'drop', BX.delegate(this._onDrop, this));
				}

				if (
					BX.Dom.hasClass(current.layout.item, this.classHiddenLabel)
					|| BX.Dom.hasClass(current.layout.item, this.classManage)
				)
				{
					BX.Event.bind(current.layout.item, 'dragover', BX.delegate(this._onDragOver, this));
				}
			});
		},

		/**
		 * Gets drag and drop event target element
		 * @private
		 * @method getItem
		 * @param  {object} eventOrItem
		 * @return {?HTMLElement}
		 */
		getItem: function(eventOrItem)
		{
			if (!BX.Type.isDomNode(eventOrItem))
			{
				if ((!eventOrItem || !BX.Type.isDomNode(eventOrItem.target)))
				{
					return null;
				}
			}
			else
			{
				eventOrItem = {target: eventOrItem};
			}

			let item = eventOrItem.target.closest('.' + this.classItem);
			if (!BX.Type.isDomNode(item))
			{
				item = eventOrItem.target.closest(
					'.' + this.classDefaultSubmenuItem + ', .' + this.classDefaultSubmenuDelimimeter
				);
			}

			return item;
		},

		getItemData: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return {};
			}

			const result = this.itemData.get(item);
			if (result)
			{
				return result;
			}

			let data;
			try
			{
				data = JSON.parse(item.dataset.item);
			}
			catch(err)
			{
				data = {};
			}

			this.setItemData(item, data);

			return data;
		},

		setItemData(item, data)
		{
			if (BX.Type.isElementNode(item) && BX.Type.isPlainObject(data))
			{
				data.NODE = item;

				this.itemData.set(item, data);
			}
		},

		/**
		 * Sets default opacity style
		 * @private
		 * @method setOpacity
		 * @param {object} item
		 */
		setOpacity: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			BX.style(item, 'opacity', 0.5);
		},

		/**
		 * Unset opacity style
		 * @private
		 * @method unsetOpacity
		 * @param  {object} item
		 * @return {undefined}
		 */
		unsetOpacity: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			BX.style(item, 'opacity', '1');
		},

		/**
		 * Sets drag styles
		 * @private
		 * @method setDragStyles
		 */
		setDragStyles: function()
		{
			BX.Dom.addClass(this.listContainer, this.classOnDrag);
			BX.Dom.addClass(BX(this.getMoreMenuId(true)), this.classOnDrag);

			this.setOpacity(this.dragItem);
		},

		/**
		 * Unset drag styles
		 * @private
		 * @method unsetDragStyles
		 * @return {undefined}
		 */
		unsetDragStyles: function()
		{
			const submenu = this.getMoreMenu();
			this.getAllItems().forEach((current) => {
				this.unsetOpacity(current);
				BX.Dom.removeClass(current, this.classItemOver);
			});

			if (submenu && BX.Type.isArray(submenu.menuItems) && submenu.menuItems.length)
			{
				submenu.menuItems.forEach((current) => {
					this.unsetOpacity(current);
					BX.Dom.removeClass(current.layout.item, this.classItemOver);
				});
			}

			BX.Dom.removeClass(this.listContainer, this.classOnDrag);
			BX.Dom.removeClass(BX(this.getMoreMenuId(true)), this.classOnDrag);
		},

		/**
		 * Gets icon class
		 * @private
		 * @method getIconClass
		 * @param  {object} item
		 * @return {string} className
		 */
		getIconClass: function(item)
		{
			let result = '';
			if (BX.Type.isDomNode(item) &&
				('dataset' in item) &&
				('class' in item.dataset) &&
				(BX.Type.isStringFilled(item.dataset.class)))
			{
				result = item.dataset.class;
			}

			return result;
		},

		/**
		 * Disables the element
		 * @private
		 * @method disableItem
		 * @param  {HTMLElement} item
		 * @return {undefined}
		 */
		disableItem: function(item)
		{
			const alias = this.getItemAlias(item);
			if (item && ('dataset' in item))
			{
				item.dataset.disabled = 'true';
				if (alias)
				{
					alias.dataset.disabled = 'true';
				}
			}
		},

		/**
		 * Disables the element
		 * @private
		 * @method enableItem
		 * @param  {HTMLElement} item
		 * @return {undefined}
		 */
		enableItem: function(item)
		{
			let alias;

			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			if (this.isSubmenuItem(item))
			{
				BX.Dom.removeClass(item, this.classItemDisabled);
				alias = this.getItemAlias(item);

				if (BX.Type.isDomNode(alias))
				{
					alias.dataset.disabled = 'false';
				}
			}
		},

		/**
		 * Gets alias link
		 * @private
		 * @method getAliasLink
		 * @param  {object} item
		 * @return {string}
		 */
		getAliasLink: function(item)
		{
			return this.dataValue(item, 'link') || '';
		},

		/**
		 * Gets item alias
		 * @private
		 * @method getItemAlias
		 * @param  {HTMLElement} item
		 * @return {?HTMLElement}
		 */
		getItemAlias: function(item)
		{
			let result = null;

			if (!BX.Type.isDomNode(item))
			{
				return result;
			}

			const allItems = this.getAllItems();
			const isSubmenuItem = this.isSubmenuItem(item);
			const isListItem = this.isListItem(item);

			if (!isSubmenuItem && !isListItem)
			{
				return result;
			}

			if (isSubmenuItem)
			{
				allItems.forEach(function(current) {
					BX.Dom.hasClass(item, this.getAliasLink(current)) && (result = current);
				}, this);
			}

			if (isListItem)
			{
				result = BX.Buttons.Utils.getByClass(document, this.getAliasLink(item));
			}

			return result;
		},

		/**
		 * @param {?HTMLElement} item
		 */
		hideItem: function(item)
		{
			!!item && BX.Dom.addClass(item, this.classSecret);
		},

		/**
		 * @param {?HTMLElement} item
		 */
		showItem: function(item)
		{
			!!item && BX.Dom.removeClass(item, this.classSecret);
		},

		/**
		 * Replaces drag item
		 * @private
		 * @method fakeDragItem
		 * @return {undefined}
		 */
		fakeDragItem: function()
		{
			if (!BX.Type.isDomNode(this.dragItem) || !BX.Type.isDomNode(this.overItem))
			{
				return;
			}

			let fakeDragItem = null;
			if (this.isDragToSubmenu())
			{
				fakeDragItem = this.getItemAlias(this.dragItem);
				if (fakeDragItem !== this.dragItem)
				{
					this.listContainer.appendChild(this.dragItem);
					this.dragItem = fakeDragItem;
					this.showItem(this.dragItem);
					this.adjustMoreButtonPosition();
					this.updateMoreMenuItems();
					this.tmp.moved = false;
					this.tmp.movetToSubmenu = true;
					this.setOpacity(this.dragItem);
				}
			}

			if (this.isDragToList() && !this.tmp.movetToSubmenu)
			{
				fakeDragItem = this.getItemAlias(this.dragItem);
				if (fakeDragItem !== this.dragItem)
				{
					this.hideItem(this.dragItem);
					this.dragItem = fakeDragItem;
					this.adjustMoreButtonPosition();
					this.updateMoreMenuItems();
					this.setOpacity(this.dragItem);
				}
			}

			this.tmp.movetToSubmenu = false;
		},

		/**
		 * Updates submenu items relative to hidden items
		 * @private
		 * @method updateSubmenuItems
		 * @return {undefined}
		 */
		updateMoreMenuItems: function()
		{
			const submenu = this.getMoreMenu();
			if (submenu === null)
			{
				return;
			}

			const submenuItems = submenu.menuItems;
			if (!BX.Type.isArray(submenuItems) || !submenuItems.length)
			{
				return;
			}

			const hiddenItems = this.getHiddenItems();
			const disabledItems = this.getDisabledItems();
			const items = disabledItems.concat(hiddenItems);

			submenuItems.forEach(current => {
				const some = [].some.call(items, someEl => {
					return (
						BX.Dom.hasClass(current.layout.item, this.dataValue(someEl, 'link')) ||
						this.isDisabled(current.layout.item) ||
						this.isSeparator(current.layout.item) ||
						this.isDropzone(current.layout.item)
					);
				});

				if (
					some
					|| (
						this.isSettings(current.layout.item)
						|| this.isApplySettingsButton(current.layout.item)
						|| this.isResetSettingsButton(current.layout.item)
						|| this.isNotHiddenItem(current.layout.item)
						|| this.isSeparator(current.layout.item)
						|| current.layout.item === this.dragItem
					)
					&& !this.isMoreButton(current.layout.item)
				)
				{
					this.showItem(current.layout.item);
				}
				else
				{
					this.hideItem(current.layout.item);
				}
			});
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isNotHiddenItem: function(item)
		{
			return BX.Dom.hasClass(item, this.classSubmenuNoHiddenItem);
		},

		/**
		 * @return {?HTMLElement}
		 */
		getNotHidden: function()
		{
			return BX.Buttons.Utils.getByClass(this.getMoreMenuContainer(), this.classSubmenuNoHiddenItem);
		},

		/**
		 * Sets styles for hovered item
		 * @private
		 * @method setOverStyles
		 * @param {object} item
		 */
		setOverStyles: function(item)
		{
			if (BX.Type.isDomNode(item) && !BX.Dom.hasClass(item, this.classItemOver))
			{
				BX.Dom.addClass(item, this.classItemOver);
			}
		},

		/**
		 * Unset styles for hovered item
		 * @private
		 * @method unsetOverStyles
		 * @param  {object} item
		 * @return {undefined}
		 */
		unsetOverStyles: function(item)
		{
			if (BX.Type.isDomNode(item) && BX.Dom.hasClass(item, this.classItemOver))
			{
				BX.Dom.removeClass(item, this.classItemOver);
			}
		},

		/**
		 * Gets value data attribute
		 * @private
		 * @method dataValue
		 * @param  {object} item
		 * @param  {string} key
		 * @return {string}
		 */
		dataValue: function(item, key)
		{
			let result = '';
			if (BX.Type.isDomNode(item))
			{
				const tmpResult = BX.data(item, key);
				if (typeof(tmpResult) !== 'undefined')
				{
					result = tmpResult;
				}
			}

			return result;
		},

		/**
		 * Executes script
		 * @private
		 * @method execScript
		 * @param  {string} script
		 */
		/*jshint -W061 */
		execScript: function(script, event)
		{
			if (BX.Type.isStringFilled(script))
			{
				const fn = new Function('event', script);
				fn(event);
				//eval('(function(event) {' + script + '})();');
			}
		},

		/**
		 * Shows license window
		 * @return {undefined}
		 */
		showLicenseWindow: function()
		{
			if (!B24.licenseInfoPopup)
			{
				return;
			}

			const popup = B24.licenseInfoPopup;

			popup.init({
				B24_LICENSE_BUTTON_TEXT: this.message('MIB_LICENSE_BUY_BUTTON'),
				B24_TRIAL_BUTTON_TEXT: this.message('MIB_LICENSE_TRIAL_BUTTON'),
				IS_FULL_DEMO_EXISTS: this.licenseParams.isFullDemoExists,
				HOST_NAME: this.licenseParams.hostname,
				AJAX_URL: this.licenseParams.ajaxUrl,
				LICENSE_ALL_PATH: this.licenseParams.licenseAllPath,
				LICENSE_DEMO_PATH: this.licenseParams.licenseDemoPath,
				FEATURE_GROUP_NAME: this.licenseParams.featureGroupName,
				AJAX_ACTIONS_URL: this.licenseParams.ajaxActionsUrl,
				B24_FEATURE_TRIAL_SUCCESS_TEXT: this.message('MIB_LICENSE_WINDOW_TRIAL_SUCCESS_TEXT')
			});

			popup.show(
				'main-buttons',
				this.message('MIB_LICENSE_WINDOW_HEADER_TEXT'),
				this.message('MIB_LICENSE_WINDOW_TEXT')
			);

		},

		/**
		 * mouse click event handler
		 * @private
		 * @method handleItemClick
		 * @param  {object} event ondragstart event object
		 * @return {undefined}
		 */
		_handleItemClick: function(event)
		{
			if (!this.isEditEnabled())
			{
				const item = this.getItem(event);
				this.showChildMenu(item);
			}
		},

		handleEditButtonClick: function(event)
		{
			event.preventDefault();
			event.stopPropagation();

			let item = this.getItem(event);
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			if (this.isSubmenuItem(item))
			{
				item = this.getItemAlias(item);
			}

			const itemData = this.getItemData(item);
			const menu = this.getItemEditMenu();
			if (menu && menu.popupWindow.isShown() && this.lastEditNode === item)
			{
				menu.popupWindow.close();
			}
			else
			{
				this.showItemEditMenu(itemData, event.target);
			}

			this.lastEditNode = item;
		},

		handleDragButtonClick: function(event)
		{
			event.preventDefault();
			event.stopPropagation();
		},

		handleItemPinEnter: function(event)
		{
			const subMenu = this.getChildMenu();
			if (subMenu)
			{
				this.showPinHint(event.currentTarget);
			}
		},

		showPinHint: function(target)
		{
			const height = 48;
			const hint = BX.Main.PopupManager.create({
				id: 'main-buttons-pin-hint',
				closeByEsc: true,
				padding: 15,
				className: 'main-buttons-pin-hint-popup',
				height: height,
				cacheable: false,
				autoHide: true,
				bindOptions: {
					forceBindPosition: true
				},
				content: this.message('MIB_PIN_HINT'),
				darkMode: true,
				events: {
					onAfterShow: function(event) {
						const popup = event.getTarget();

						const targetPosition = BX.Dom.getPosition(target);
						const popupPosition = BX.Dom.getPosition(popup.getPopupContainer());

						if (popupPosition.left < targetPosition.left + targetPosition.width)
						{
							popup.setAngle({ position: 'top', offset: 0 });
							popup.setOffset({ offsetLeft: 20, offsetTop: 5 });
							popup.adjustPosition();
						}
					}
				}
			});

			hint.setAngle({
				position: 'left',
				offset: 0,
			});
			hint.setOffset({
				offsetLeft: target.offsetWidth + 5,
				offsetTop: -target.offsetHeight / 2 - height / 2 - 2,
			});
			hint.setBindElement(target);
			hint.show();
			hint.adjustPosition();
		},

		closePinHint: function()
		{
			const hint = BX.Main.PopupManager.getPopupById('main-buttons-pin-hint');
			if (hint)
			{
				hint.close();
			}
		},

		handleItemPinLeave: function(event)
		{
			this.closePinHint();
		},

		handleItemPin: function(itemData, rootNode, event)
		{
			event.stopPropagation();
			event.preventDefault();

			const newItem = this.createRootItem(itemData);

			BX.Dom.addClass(newItem, 'main-buttons-item-insert-animation');
			BX.Dom.insertBefore(newItem, rootNode);
			requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					BX.Dom.style(newItem, {
						width: newItem.scrollWidth + 'px',
						opacity: 1,
					});
				});
			});

			const finalize = () => {
				BX.Dom.removeClass(newItem, 'main-buttons-item-insert-animation');
				BX.Dom.style(newItem, 'width', null);
				BX.Dom.style(newItem, 'opacity', null);

				const rootItemData = this.getItemData(rootNode);
				this.recalculateItemsCounters([rootItemData]);

				this.showMoreMenu();
				this.updateMoreButtonCounter();
				this.showChildMenu(rootNode);
				this.saveSettings();
			};

			setTimeout(finalize, 300);

			this.initItem(newItem);
			this.pinItem(itemData);

			this.destroyMoreMenu();
			this.destroyChildMenu();
		},

		handleItemUnpin: function(itemData, rootNode)
		{
			BX.Dom.style(rootNode, { width: rootNode.offsetWidth + 'px' });
			BX.Dom.addClass(rootNode, 'main-buttons-item-insert-animation');
			requestAnimationFrame(() => {
				requestAnimationFrame(() => {
					BX.Dom.style(
						rootNode,
						{
							width: 0,
							margin: 0,
							opacity: 0,
						}
					);
				});
			});

			const finalize = () => {
				BX.Dom.remove(rootNode);
				this.showMoreMenu();
				this.updateMoreButtonCounter();
				this.saveSettings();
			};

			setTimeout(finalize, 300);

			this.pinItem(itemData, false);

			const roots = [];
			let rootItemId = '';
			const ids = itemData['ID'].split(':');
			ids.forEach(id => {
				rootItemId += `${rootItemId === '' ? '' : ':'}${id}`;
				const rootItem = this.getItemById(rootItemId);
				if (rootItem)
				{
					roots.push(this.getItemData(rootItem));
				}
			});

			this.destroyMoreMenu();
			this.recalculateItemsCounters(roots);
		},

		pinItem: function(itemData, flag = true)
		{
			const itemId = itemData['ID']
			const ids = itemId.split(':');

			let rootItemId = '';
			ids.forEach((id, index) => {
				rootItemId += `${rootItemId === '' ? '' : ':'}${id}`;
				const rootItem = this.getItemById(rootItemId);
				if (!rootItem)
				{
					return;
				}

				const rootItemData = this.getItemData(rootItem);

				let subItems = rootItemData['ITEMS'];
				let subItemId = rootItemId;
				const subItemIds = ids.slice(index + 1);
				const parentItems = [rootItemData];

				while (BX.Type.isArrayFilled(subItemIds) && BX.Type.isArrayFilled(subItems))
				{
					subItemId = subItemId + ':' + subItemIds.shift();
					for (let i = 0; i < subItems.length; i++)
					{
						const subItem = subItems[i];
						if (subItem['ID'] === itemId)
						{
							subItem['IS_PINNED'] = flag;
							for (let index = parentItems.length - 1; index >= 0; index--)
							{
								const parentItem = parentItems[index];
								const firstLevel = index === 0;

								if (flag)
								{
									const hasVisibleSubItems = parentItem['ITEMS'].some((item) => {
										const isPinned = item['IS_PINNED'] === true;
										const isDisbanded = item['IS_DISBANDED'] === true;
										const isDelimiter = item['IS_DELIMITER'] === true;

										return !isPinned && !isDisbanded && !isDelimiter;
									});

									if (!hasVisibleSubItems)
									{
										parentItem['IS_DISBANDED'] = true;

										if (firstLevel)
										{
											rootItem.dataset.disbanded = true;
										}
									}
								}
								else
								{
									if (firstLevel)
									{
										rootItem.dataset.disbanded = false;
									}

									parentItem['IS_DISBANDED'] = false;
								}

								const hasActiveSubItems = parentItem['ITEMS'].some((item) => {
									return (
										item['IS_ACTIVE'] === true
										&& item['IS_PINNED'] !== true
										&& item['IS_DELIMITER'] !== true
									);
								});

								if (hasActiveSubItems)
								{
									parentItem['IS_ACTIVE'] = true;
									if (firstLevel)
									{
										this.activateItem(rootItem);
									}
								}
								else
								{
									parentItem['IS_ACTIVE'] = false;
									if (firstLevel)
									{
										this.deactivateItem(rootItem);
									}
								}
							}

							return; // next forEach
						}
						else if (subItem['ID'] === subItemId)
						{
							subItems = subItem['ITEMS'];
							subItemId = subItem['ID'];
							parentItems.push(subItem);

							break;
						}
					}
				}
			});
		},

		getParentItem: function(itemId)
		{
			const ids = itemId.split(':');
			let rootItemId = '';
			for (let index = 0; index < ids.length; index++)
			{
				rootItemId += `${rootItemId === '' ? '' : ':'}${ids[index]}`;
				const rootItem = this.getItemById(rootItemId);
				if (!rootItem)
				{
					continue;
				}

				const rootItemData = this.getItemData(rootItem);

				let subItems = rootItemData['ITEMS'];
				let subItemId = rootItemId;
				const subItemIds = ids.slice(index + 1);
				let parentItem = null;

				while (BX.Type.isArrayFilled(subItemIds) && BX.Type.isArrayFilled(subItems))
				{
					subItemId = subItemId + ':' + subItemIds.shift();
					for (let i = 0; i < subItems.length; i++)
					{
						const subItem = subItems[i];
						if (subItem['ID'] === itemId)
						{
							return parentItem === null ? rootItemData : parentItem;
						}
						else if (subItem['ID'] === subItemId)
						{
							subItems = subItem['ITEMS'];
							subItemId = subItem['ID'];
							parentItem = subItem;

							break;
						}
					}
				}
			}

			return null;
		},

		/**
		 * dragstart event handler
		 * @private
		 * @method _onDragStart
		 * @param  {object} event ondragstart event object
		 * @return {undefined}
		 */
		_onDragStart: function(event)
		{
			const visibleItems = this.getVisibleItems();
			const visibleItemsLength = BX.Type.isArray(visibleItems) ? visibleItems.length : null;
			this.dragItem = this.getItem(event);

			if (!BX.Type.isDomNode(this.dragItem))
			{
				return;
			}

			if (visibleItemsLength === 1 && this.isListItem(this.dragItem))
			{
				event.preventDefault();
				BX.onCustomEvent(window, 'BX.Main.InterfaceButtons:onHideLastVisibleItem', [this.dragItem, this]);
				return;
			}

			if (
				this.isMoreButton(this.dragItem)
				|| this.isSeparator(this.dragItem)
				|| this.isNotHiddenItem(this.dragItem)
				|| BX.Dom.attr(this.dragItem, 'data-parent-item-id')
				|| BX.Dom.attr(this.dragItem, 'data-has-child')
			)
			{
				event.preventDefault();
				return;
			}

			this.onDragStarted = true;

			this.closeChildMenu();
			this.destroyItemEditMenu();

			if (this.isListItem(this.dragItem))
			{
				this.showMoreMenu();
			}

			this.setDragStyles();

			if (!this.isEditEnabled())
			{
				this.enableEdit();
			}
		},

		/**
		 * dragend event handler
		 * @private
		 * @method _onDragEnd
		 * @param  {object} event dragend event object
		 * @return {undefined}
		 */
		_onDragEnd: function(event)
		{
			event.preventDefault();
			const item = this.getItem(event);

			this.onDragStarted = false;

			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			this.unsetDragStyles();
			this.refreshMoreMenu();
			if (!this.isEditEnabled())
			{
				this.closeMoreMenu();
			}

			const nextVisible = BX.findNextSibling(this.dragItem, (node) => {
				return this.isVisibleItem(node);
			});

			const prevVisible = BX.findPreviousSibling(this.dragItem, (node) => {
				return this.isVisibleItem(node);
			});

			if (
				BX.Dom.hasClass(prevVisible, this.classHiddenLabel)
				|| (this.isDisabled(prevVisible) && this.isSubmenuItem(prevVisible))
				|| (this.isDisabled(nextVisible) && this.isSubmenuItem(nextVisible))
			)
			{
				this.disableItem(this.dragItem);
				this.refreshMoreMenu();
			}

			if (this.isEditEnabled())
			{
				this.enableEdit();
			}
			else
			{
				this.disableEdit();
			}

			this.updateMoreButtonCounter();

			this.saveSettings();
			this.dragItem = null;
			this.overItem = null;
			this.tmp.moved = false;
		},

		updateMoreButtonCounter: function()
		{
			let hiddenItems = this.getHiddenItems();
			const disabledItems = this.getDisabledItems();
			hiddenItems = hiddenItems.concat(disabledItems);
			let sumCount = 0;

			if (BX.Type.isArray(hiddenItems))
			{
				hiddenItems.forEach((current) => {
					const item = this.getItemData(current);
					const counter = BX.Type.isNumber(item['COUNTER']) && item['COUNTER'] > 0 ? item['COUNTER'] : 0;
					sumCount += counter;
				});
			}

			if (BX.Type.isNumber(sumCount))
			{
				this.setMoreButtonCounter(sumCount);
			}
		},

		/**
		 * dragenter event handler
		 * @private
		 * @method _onDragEnter
		 * @param  {object} event dragenter event object
		 * @return {undefined}
		 */
		_onDragEnter: function(event)
		{
			const item = this.getItem(event);
			if (BX.Type.isDomNode(item) && this.isNotHiddenItem(item))
			{
				this.setOverStyles(item);
			}
		},

		/**
		 * dragover event handler
		 * @private
		 * @method _onDragOver
		 * @param  {object} event dragover event object
		 * @return {undefined}
		 */
		_onDragOver: function(event)
		{
			event.preventDefault();

			this.overItem = this.getItem(event);

			if (
				!BX.Type.isDomNode(this.overItem)
				|| !BX.Type.isDomNode(this.dragItem)
				|| this.overItem === this.dragItem
				|| this.isNotHiddenItem(this.overItem)
				|| BX.Dom.attr(this.overItem, 'data-parent-item-id')
				|| BX.Dom.attr(this.overItem, 'data-has-child')
			)
			{
				return;
			}

			this.fakeDragItem();

			const isNext = this.isNext(event);
			const isGoodPosition = this.isGoodPosition(event);

			if (isNext && isGoodPosition)
			{
				let nextSiblingItem;
				let insertAfter = false;
				if (this.isListItem(this.overItem))
				{
					nextSiblingItem = this.findNextSiblingByClass(this.overItem, this.classItem);
					if (nextSiblingItem === null && this.getLastVisibleItem() === this.overItem)
					{
						nextSiblingItem = this.overItem;
						insertAfter = true;
					}
				}
				else
				{
					nextSiblingItem = this.findNextSiblingByClass(this.overItem, this.classSubmenuItem);
					if (
						this.isSettings(nextSiblingItem)
						|| this.isApplySettingsButton(nextSiblingItem)
						|| this.isResetSettingsButton(nextSiblingItem)
					)
					{
						return;
					}

					if (nextSiblingItem === null && this.getItemAlias(this.getLastDisabledItem()) === this.overItem)
					{
						nextSiblingItem = this.overItem;
						insertAfter = true;
					}
				}

				if (BX.Type.isDomNode(nextSiblingItem))
				{
					this.moveButton(nextSiblingItem, insertAfter);
					this.moveButtonAlias(nextSiblingItem, insertAfter);
					this.adjustMoreButtonPosition();
					this.updateMoreMenuItems();
				}
			}
			else if (!isNext && isGoodPosition && !BX.Dom.hasClass(this.overItem, this.classHiddenLabel))
			{
				this.moveButton(this.overItem);
				this.moveButtonAlias(this.overItem);
				this.adjustMoreButtonPosition();
				this.updateMoreMenuItems();
			}
		},

		/**
		 * dragleave event handler
		 * @private
		 * @method _onDragLeave
		 * @param  {object} event dragleave event object
		 * @return {undefined}
		 */
		_onDragLeave: function(event)
		{
			const item = this.getItem(event);
			if (BX.Type.isDomNode(item))
			{
				this.unsetOverStyles(event.target);
			}
		},

		/**
		 * drop event handler
		 * @private
		 * @method _onDrop
		 * @param  {object} event drop event object
		 * @return {undefined}
		 */
		_onDrop: function(event)
		{
			const item = this.getItem(event);
			if (!BX.Type.isDomNode(item))
			{
				return;
			}

			if (this.isNotHiddenItem(item) || this.isDisabled(item))
			{
				this.disableItem(this.dragItem);
				this.adjustMoreButtonPosition();
			}

			const aliasDragItem = this.getItemAlias(this.dragItem);
			if (this.isListItem(aliasDragItem))
			{
				aliasDragItem.dataset.disabled = 'false';
			}

			this.unsetDragStyles();

			event.preventDefault();
		},

		handleMoreMenuFirstShow: function(event)
		{
			const popup = event.getTarget();

			BX.Event.bind(popup.getPopupContainer(), 'mouseenter', this.handleMoreMenuMouseEnter.bind(this));
			BX.Event.bind(popup.getPopupContainer(), 'mouseleave', this.handleMoreMenuMouseLeave.bind(this));
		},

		handleMoreMenuMouseEnter: function()
		{
			clearTimeout(this.submenuLeaveTimeout);
		},

		handleMoreMenuMouseLeave: function()
		{
			this.tryCloseMoreMenuOnTimeout();
		},

		tryCloseMoreMenuOnTimeout: function()
		{
			clearTimeout(this.submenuLeaveTimeout);

			if (!this.isEditEnabled())
			{
				this.submenuLeaveTimeout = setTimeout(() => {
					this.closeMoreMenu();
				}, 500);
			}
		},

		handleMoreMenuShow: function(event)
		{
			BX.Event.EventEmitter.emit('BX.Main.InterfaceButtons:onMenuShow');
			BX.Event.EventEmitter.emit(this, 'BX.Main.InterfaceButtons:onMoreMenuShow', {event});
			setTimeout(() => {
				if (!this.isEditEnabled())
				{
					event.getTarget().setAutoHide(true);
				}
			}, 500);
		},

		handleMoreMenuClose: function()
		{
			BX.Event.EventEmitter.emit(this, 'BX.Main.InterfaceButtons:onMoreMenuClose');
			this.setMoreMenuShown(false);

			if (this.isEditEnabled())
			{
				this.activateItem(this.moreButton);
			}
			else
			{
				if (!this.isActiveInMoreMenu())
				{
					this.deactivateItem(this.moreButton);
				}
			}

			this.destroyItemEditMenu();
		},

		_onChildMenuFirstShow: function(event)
		{
			const popup = event.getTarget();

			BX.Event.bind(popup.getPopupContainer(), 'mouseenter', this._onChildMenuMouseEnter.bind(this));
			BX.Event.bind(popup.getPopupContainer(), 'mouseleave', this._onChildMenuMouseLeave.bind(this));
		},

		_onChildMenuMouseEnter: function()
		{
			clearTimeout(this.childMenuLeaveTimeout);
		},

		_onChildMenuMouseLeave: function()
		{
			this.tryCloseChildMenuOnTimeout();
		},

		tryCloseChildMenuOnTimeout: function()
		{
			if (this.isEditEnabled())
			{
				return;
			}

			clearTimeout(this.childMenuLeaveTimeout);

			this.childMenuLeaveTimeout = setTimeout(() => {
				this.closeChildMenu();
			}, 500);
		},

		_onChildMenuShow: function(item, event)
		{
			BX.Dom.addClass(item, this.classMenuShown);
			BX.Event.EventEmitter.emit('BX.Main.InterfaceButtons:onMenuShow');
			BX.Event.EventEmitter.emit(this, 'BX.Main.InterfaceButtons:onSubMenuShow', {item, event});

			if (this.theme !== 'default')
			{
				this.centerPopupArrow(event.getTarget(), item);
			}

			setTimeout(() => {
				event.getTarget().setAutoHide(true);
			}, 500);
		},

		_onChildMenuClose: function(item)
		{
			BX.Event.EventEmitter.emit(this, 'BX.Main.InterfaceButtons:onSubMenuClose');
			BX.Dom.removeClass(item, this.classMenuShown);
			this.closePinHint();
		},

		handleAdjustPosition: function(item, event)
		{
			const activeItemMargin = 25;
			const position = BX.Dom.getPosition(item);
			const popup = event.getTarget();
			if (event.left < (position.left - activeItemMargin))
			{
				const popupWidth = popup.getPopupContainer().offsetWidth;
				const left = position.right - popupWidth + activeItemMargin;
				if (left > 0)
				{
					event.left = left;
					BX.Dom.addClass(popup.getPopupContainer(), '--left-handed');
				}
			}
			else
			{
				BX.Dom.removeClass(popup.getPopupContainer(), '--left-handed');
			}
		},

		/**
		 * resize window event handler
		 * @private
		 * @method _onResizeHandler
		 * @return {object} window resize event object
		 */
		_onResizeHandler: function()
		{
			this.adjustMoreButtonPosition();
			this.updateMoreMenuItems();
			this.closeChildMenu();

			if (!this.isSettingsEnabled)
			{
				this.visibleControlMoreButton();
			}
		},

		handleMoreButtonClick: function(event)
		{
			event.preventDefault();
			this.showMoreMenu();
		},

		handleMoreButtonMouseEnter: function(event)
		{
			if (this.enableItemMouseEnter)
			{
				clearTimeout(this.menuShowTimeout);
				this.menuShowTimeout = setTimeout(() => {
					this.showMoreMenu();
				}, 100);
			}

			clearTimeout(this.submenuLeaveTimeout);
		},

		handleMoreButtonMouseLeave: function()
		{
			clearTimeout(this.menuShowTimeout);
			this.tryCloseMoreMenuOnTimeout();
		},

		handleItemMouseEnter: function(event)
		{
			if (!this.enableItemMouseEnter)
			{
				return;
			}

			if (this.onDragStarted)
			{
				return;
			}

			const item = this.getItem(event);

			clearTimeout(this.childMenuLeaveTimeout);
			clearTimeout(this.menuShowTimeout);

			this.menuShowTimeout = setTimeout(() => {
				this.showChildMenu(item);
			}, 100);

			BX.Dom.addClass(item, this.classItemOver);
		},

		handleItemMouseLeave: function(event)
		{
			clearTimeout(this.menuShowTimeout);

			if (!this.enableItemMouseEnter)
			{
				return;
			}

			if (this.onDragStarted)
			{
				return;
			}

			const item = this.getItem(event);
			BX.Dom.removeClass(item, this.classItemOver);

			this.tryCloseChildMenuOnTimeout();
		},

		handleMoreMenuItemMouseEnter: function(event)
		{
			if (this.isEditEnabled())
			{
				event.preventDefault();
			}
		},

		/**
		 * @return {?HTMLElement}
		 */
		getSettingsResetButton: function()
		{
			return BX.Buttons.Utils.getByClass(this.getMoreMenuContainer(), this.classSettingsResetButton);
		},

		/**
		 * Checks whether the item is disabled
		 * @public
		 * @method isDisabled
		 * @param  {object} item
		 * @return {boolean}
		 */
		isDisabled: function(item)
		{
			let result = false;

			if (BX.Type.isDomNode(item))
			{
				result = (
					this.dataValue(item, 'disabled') === 'true' || BX.Dom.hasClass(item, this.classItemDisabled)
				);
			}

			return result;
		},

		isPinned: function(item)
		{
			let result = false;

			if (BX.Type.isDomNode(item))
			{
				result = this.getItemData(item)['IS_PINNED'] === true;
			}

			return result;
		},

		/**
		 * @param {HTMLElement} item
		 * @return {boolean}
		 */
		isSettings: function(item)
		{
			let result = false;

			if (BX.Type.isDomNode(item))
			{
				result = BX.Dom.hasClass(item, this.classSettingMenuItem);
			}

			return result;
		},

		/**
		 * Checks whether the item is locked
		 * @private
		 * @method isLocked
		 * @param  {object}  item
		 * @return {boolean}
		 */
		isLocked: function(item)
		{
			let result = false;

			if (BX.Type.isDomNode(item))
			{
				result = (
					this.dataValue(item, 'locked') === 'true' ||
					BX.Dom.hasClass(item, this.classItemLocked)
				);
			}

			return result;
		},

		/**
		 * Checks whether the item is dropzone
		 * @private
		 * @method isOvered
		 * @param  {object} item
		 * @return {boolean}
		 */
		isDropzone: function(item)
		{
			return BX.Dom.hasClass(item, this.classDropzone);
		},

		/**
		 * Checks whether the hovered item is next
		 * @private
		 * @method isNext
		 * @param  {object} event dragover event object
		 * @return {boolean}
		 */
		isNext: function(event)
		{
			const dragItemRect = this.dragItem.getBoundingClientRect();
			const overItemRect = this.overItem.getBoundingClientRect();
			const styles = getComputedStyle(this.dragItem);
			const dragItemMarginRight = parseInt(styles.marginRight.replace('px', ''));
			let result = null;

			if (this.isListItem(this.overItem))
			{
				result = (
					event.clientX > (overItemRect.left - dragItemMarginRight) && event.clientX > dragItemRect.right
				);
			}

			if (this.isSubmenuItem(this.overItem))
			{
				result = (
					event.clientY > dragItemRect.top
				);
			}

			return result;
		},

		/**
		 * Checks whether it is possible to move the item
		 * @private
		 * @method isGoodPosition
		 * @param  {object} event dragover event object
		 * @return {boolean}
		 */
		isGoodPosition: function(event)
		{
			const overItem = this.overItem;
			if (!BX.Type.isDomNode(overItem))
			{
				return false;
			}

			let result;
			const overItemRect = overItem.getBoundingClientRect();
			if (this.isListItem(overItem))
			{
				result = (
					(this.isNext(event) && (event.clientX >= (overItemRect.left + (overItemRect.width / 2)))) ||
					(!this.isNext(event) && (event.clientX <= (overItemRect.left + (overItemRect.width / 2))))
				);
			}

			if (this.isSubmenuItem(overItem))
			{
				result = (
					(this.isNext(event) && (event.clientY >= (overItemRect.top + (overItemRect.height / 2)))) ||
					(!this.isNext(event) && (event.clientY <= (overItemRect.top + (overItemRect.height / 2))))
				);
			}

			return result;
		},

		/**
		 * Checks whether the item is a submenu item
		 * @private
		 * @method isSubmenuItem
		 * @param  {object} item
		 * @return {boolean}
		 */
		isSubmenuItem: function(item)
		{
			return BX.Dom.hasClass(item, this.classSubmenuItem);
		},

		/**
		 * Checks whether the item is visible
		 * @public
		 * @method isVisibleItem
		 * @param  {object}  item
		 * @return {boolean}
		 */
		isVisibleItem: function(item)
		{
			if (!BX.Type.isDomNode(item))
			{
				return false;
			}

			return item.offsetTop === 0;
		},

		/**
		 * Checks whether the item is more button
		 * @private
		 * @method isMoreButton
		 * @param  {object} item
		 * @return {boolean}
		 */
		isMoreButton: function(item)
		{
			let result = false;
			if (BX.Type.isDomNode(item) && BX.Dom.hasClass(item, this.classItemMore))
			{
				result = true;
			}

			return result;
		},

		/**
		 * Checks whether the item is list item
		 * @private
		 * @method isListItem
		 * @param  {object} item
		 * @return {boolean}
		 */
		isListItem: function(item)
		{
			let result = false;

			if (BX.Type.isDomNode(item) && BX.Dom.hasClass(item, this.classItem))
			{
				result = true;
			}

			return result;
		},

		/**
		 * Checks whether the item is sublink
		 * @private
		 * @method isSublink
		 * @param  {object}  item
		 * @return {boolean}
		 */
		isSublink: function(item)
		{
			let result = false;
			if (BX.Type.isDomNode(item))
			{
				result = BX.Dom.hasClass(item, this.classItemSublink);
			}

			return result;
		},

		/**
		 * Checks whether the item is separator
		 * @private
		 * @method isSeparator
		 * @param  {object}  item
		 * @return {boolean}
		 */
		isSeparator: function(item)
		{
			let result = false;
			if (BX.Type.isDomNode(item))
			{
				result = BX.Dom.hasClass(item, this.classSeparator);
			}

			return result;
		},

		/**
		 * Checks that the element is dragged into the submenu
		 * @return {boolean}
		 */
		isDragToSubmenu: function()
		{
			return !this.isSubmenuItem(this.dragItem) && this.isSubmenuItem(this.overItem);
		},

		/**
		 * Checks that the element is dragged into the list
		 * @return {boolean}
		 */
		isDragToList: function()
		{
			return this.isSubmenuItem(this.dragItem) && !this.isSubmenuItem(this.overItem);
		}
	};
}


if (typeof(BX.Main.interfaceButtonsManager) === 'undefined')
{
	BX.Main.interfaceButtonsManager =
	{
		data: {},

		init: function(params)
		{
			let container = null;

			if (!BX.Type.isPlainObject(params) || !('containerId' in params))
			{
				throw 'BX.Main.interfaceButtonsManager: containerId not set in params Object';
			}

			container = BX(params.containerId);

			if (BX.Type.isDomNode(container))
			{
				this.data[params.containerId] = new BX.Main.interfaceButtons(container, params);
			}
			else
			{
				BX(BX.delegate(function() {
					container = BX(params.containerId);

					if (!BX.Type.isDomNode(container))
					{
						throw 'BX.Main.interfaceButtonsManager: container is not dom node';
					}

					this.data[params.containerId] = new BX.Main.interfaceButtons(container, params);
				}, this));
			}
		},

		getById: function(containerId)
		{
			let result = null;

			if (BX.type.isString(containerId) && BX.Type.isStringFilled(containerId))
			{
				try
				{
					result = this.data[containerId];
				}
				catch (e) {}
			}

			return result;
		},

		getObjects: function() {
			return this.data;
		}
	};
}