Your IP : 18.191.171.136


Current Path : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/entity-editor/js/
Upload File :
Current File : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/entity-editor/js/editor.js

/* eslint-disable */

BX.namespace("BX.UI");

//region EDITOR
if(typeof BX.UI.EntityEditor === "undefined")
{
	BX.UI.EntityEditor = function()
	{
		this._id = "";
		this._settings = {};

		this._entityTypeName = '';
		this._entityId = 0;

		this._userFieldManager = null;
		this.additionalFieldsData = {};

		this._container = null;
		this._layoutContainer = null;
		this._buttonContainer = null;
		this._createSectionButton = null;
		this._configMenuButton = null;
		this._configIcon = null;

		this._pageTitle = null;
		this._pageTitleInput = null;
		this._buttonWrapper = null;
		this._editPageTitleButton = null;
		this._copyPageUrlButton = null;

		this._actionTypes = null;
		this._formElement = null;
		this._ajaxForm = null;
		this._ajaxForms = null;
		this._reloadAjaxForm = null;
		this._formSubmitHandler = BX.delegate(this.onFormSubmit, this);

		this._controllers = null;
		this._controls = null;
		this._activeControls = null;
		this._toolPanel = null;

		this._model = null;
		this._scheme = null;
		this._config = null;
		this._context = null;
		this._contextId = "";
		this._externalContextId = "";

		this._mode = BX.UI.EntityEditorMode.intermediate;

		this._isNew = false;
		this._readOnly = false;

		this._enableRequiredUserFieldCheck = true;
		this._enableAjaxForm = true;
		this._enableSectionEdit = false;
		this._enableSectionCreation = false;
		this._enableModeToggle = true;
		this._enableVisibilityPolicy = true;
		this._enablePageTitleControls = true;
		this._enableToolPanel = true;
		this._isToolPanelAlwaysVisible = false;
		this._enableBottomPanel = true;
		this._enableConfigControl = true;
		this._enableFieldsContextMenu = true;

		this._canHideField = true;

		this._serviceUrl = "";
		this._htmlEditorConfigs = null;

		this._pageTitleExternalClickHandler = BX.delegate(this.onPageTitleExternalClick, this);
		this._pageTitleKeyPressHandler = BX.delegate(this.onPageTitleKeyPress, this);

		this._validators = null;
		this._modeSwitch = null;
		this._delayedSaveHandle = 0;

		this._isEmbedded = false;
		this._isRequestRunning = false;
		this._isConfigMenuShown = false;
		this._isReleased = false;

		this._enableCloseConfirmation = true;
		this._closeConfirmationHandler = BX.delegate(this.onCloseConfirmButtonClick, this);
		this._cancelConfirmationHandler = BX.delegate(this.onCancelConfirmButtonClick, this);

		this._windowResizeHandler = BX.debounce(BX.delegate(this.onResize, this), 50);

		this._sliderOpenHandler = BX.delegate(this.onSliderOpen, this);
		this._sliderCloseHandler = BX.delegate(this.onSliderClose, this);

		this._areAvailableSchemeElementsChanged = false;
		this._availableSchemeElements = null;

		this._dragPlaceHolder = null;
		this._dragContainerController = null;
		this._dropHandler = BX.delegate(this.onDrop, this);
		this._dragConfig = {};

		this.eventsNamespace = 'BX.UI.EntityEditor';
		this.pageTitleInputClassName = "pagetitle-item";
		this._configurationFieldManager = null;
		this._commonConfigEditUrl = BX.prop.getString(
			this._settings,
			"commonConfigEditUrl",
			"/configs/editor/?ENTITY_TYPE_ID=#ENTITY_TYPE_ID_VALUE#&MODULE_ID=#MODULE_ID#"
		);
		this._canBeMultipleFields = true;
		this.moduleId = null;
		this._restrictions = {};

		this.eventIds = new Set();
		this.needReloadStorageKey = 'UI.EntityEditor.needReload';

		this._attributeManager = null;

		this._validationEnabled = true;

	};
	BX.UI.EntityEditor.prototype =
	{
		initialize: function(id, settings)
		{
			this._id = BX.type.isNotEmptyString(id) ? id : BX.util.getRandomString(4);
			this._settings = settings ? settings : {};

			this._model = BX.prop.get(this._settings, "model", null);
			this._scheme = BX.prop.get(this._settings, "scheme", null);
			this._config = BX.prop.get(this._settings, "config", null);

			this._serviceUrl = BX.prop.getString(this._settings, "serviceUrl", "");
			this._entityTypeName = BX.prop.getString(this._settings, "entityTypeName", '');
			this._entityTypeTitle = BX.prop.getString(this._settings, "entityTypeTitle", '');
			this._useFieldsSearch = BX.prop.getBoolean(this._settings, "useFieldsSearch", false);
			this._useForceFieldsAdd = BX.prop.getBoolean(this._settings, "useForceFieldsAdd", true);
			this._entityId = BX.prop.getInteger(this._settings, "entityId", 0);
			this.moduleId = BX.prop.getString(this._settings, "moduleId", '');
			this._isNew = this._entityId <= 0 && this._model.isIdentifiable();
			this.additionalFieldsData = BX.prop.getObject(this._settings, 'additionalFieldsData', {});

			this._isEmbedded = BX.prop.getBoolean(this._settings, "isEmbedded", false);
			this._creationFieldPageUrl = BX.prop.getBoolean(this._settings, "creationFieldPageUrl", false);

			var container = BX.prop.get(this._settings, "container");
			if(!BX.type.isElementNode(container))
			{
				container = BX(BX.prop.get(this._settings, "containerId")) || top.BX(BX.prop.get(this._settings, "containerId"));
			}
			this._container = container;

			this._parentContainer = BX.findParent(this._container, { className: 'ui-entity-section' }, false);
			this._buttonContainer = BX(BX.prop.get(this._settings, "buttonContainerId"));
			this._configIcon = BX(BX.prop.get(this._settings, "configIconId"));

			this._canHideField = BX.prop.getBoolean(this._settings, "canHideField", true);
			this._canBeMultipleFields = BX.prop.getBoolean(this._settings, "canBeMultipleFields", true);
			this._enableShowAlwaysFeauture = BX.prop.getBoolean(this._settings, "enableShowAlwaysFeauture", true);
			this._enableVisibilityPolicy = BX.prop.getBoolean(this._settings, "enableVisibilityPolicy", true);
			this._enablePageTitleControls = BX.prop.getBoolean(this._settings, "enablePageTitleControls", true);
			if(this._enablePageTitleControls)
			{
				this._pageTitle = BX("pagetitle");
				this._buttonWrapper = BX("pagetitle_btn_wrapper");
				this._editPageTitleButton = BX("pagetitle_edit");
				this._copyPageUrlButton = BX("page_url_copy_btn");
			}

			this.adjustSize();
			this.adjustTitle();

			//region Form
			var formTagName = BX.prop.getString(this._settings, "formTagName", "form");
			this._formElement = BX.create(formTagName, {props: { name: this._id + "_form"}});
			this._container.appendChild(this._formElement);

			this._layoutContainer = BX.create("div", {props: { className: "ui-entity-editor-column-wrapper"}});
			this._formElement.appendChild(this._layoutContainer);

			this._enableRequiredUserFieldCheck = BX.prop.getBoolean(this._settings, "enableRequiredUserFieldCheck", true);

			this._enableAjaxForm = BX.prop.getBoolean(this._settings, "enableAjaxForm", true);
			if(this._enableAjaxForm)
			{
				this.initializeAjaxForm();
			}
			//endregion

			this._restrictions = BX.prop.getObject(this._settings, "restrictions", {});

			this.initializeManagers();

			this._context = BX.prop.getObject(this._settings, "context", {});
			this._contextId = BX.prop.getString(this._settings, "contextId", "");
			this._externalContextId = BX.prop.getString(this._settings, "externalContextId", "");

			this._readOnly = BX.prop.getBoolean(this._settings, "readOnly", false);
			if(this._readOnly)
			{
				this._enableSectionEdit = this._enableSectionCreation = false;
			}
			else
			{
				this._enableSectionEdit = BX.prop.getBoolean(this._settings, "enableSectionEdit", false);
				this._enableSectionCreation = BX.prop.getBoolean(this._settings, "enableSectionCreation", false);
			}

			this._controllers = [];
			this._controls = [];
			this._activeControls = [];
			this._modeSwitch = BX.UI.EntityEditorModeSwitch.create(this._id, { editor: this });

			this._htmlEditorConfigs = BX.prop.getObject(this._settings, "htmlEditorConfigs", {});

			var initialMode = BX.UI.EntityEditorMode.view;
			if(!this._readOnly)
			{
				initialMode = BX.UI.EntityEditorMode.parse(BX.prop.getString(this._settings, "initialMode", ""));
			}
			this._mode = initialMode !== BX.UI.EntityEditorMode.intermediate ? initialMode : BX.UI.EntityEditorMode.view;

			this._enableModeToggle = false;
			if(!this._readOnly)
			{
				this._enableModeToggle = BX.prop.getBoolean(this._settings, "enableModeToggle", true);
			}

			if(this._isNew && !this._readOnly)
			{
				this._mode = BX.UI.EntityEditorMode.edit;
			}

			this._availableSchemeElements = this._scheme.getAvailableElements();

			this.initializeControllers();
			this.initializeControls();

			if(this._mode === BX.UI.EntityEditorMode.edit && this._controls.length > 0)
			{
				this.initializeControlsEditMode();
			}

			this.initializeValidators();

			this._enableToolPanel = BX.prop.getBoolean(this._settings, "enableToolPanel", true);
			this._isToolPanelAlwaysVisible = BX.prop.getBoolean(this._settings, "isToolPanelAlwaysVisible", false);
			if(this._enableToolPanel)
			{
				this.initializeToolPanel();

				if (this.isToolPanelAlwaysVisible())
				{
					this.showToolPanel();
				}
			}

			this._enableBottomPanel = BX.prop.getBoolean(this._settings, "enableBottomPanel", true);
			this._enableConfigControl = BX.prop.getBoolean(this._settings, "enableConfigControl", true);

			this._enableFieldsContextMenu = BX.prop.getBoolean(this._settings, "enableFieldsContextMenu", true);

			this.initializeDragDrop();

			this.layout();
			this.attachToEvents();

			this.initPull();

			var eventArgs =
				{
					id: this._id,
					externalContext: this._externalContextId,
					context: this._contextId,
					entityTypeName: this._entityTypeName,
					entityId: this._entityId,
					model: this._model
				};
			BX.onCustomEvent(window, this.eventsNamespace + ":onInit", [ this, eventArgs ]);
		},
		initializeControllers: function()
		{
			var i, length;
			var controllerData = BX.prop.getArray(this._settings, "controllers", []);
			for(i = 0, length = controllerData.length; i < length; i++)
			{
				var controller = this.createController(controllerData[i]);
				if(controller)
				{
					this._controllers.push(controller);
				}
			}
		},
		initializeControls: function()
		{
			var elements = this._scheme.getElements();
			var i, length, element, control;
			for(i = 0, length = elements.length; i < length; i++)
			{
				element = elements[i];
				control = this.createControl(
					element.getType(),
					element.getName(),
					{ schemeElement: element, mode: BX.UI.EntityEditorMode.view }
				);

				if(!control)
				{
					continue;
				}

				this._controls.push(control);
			}
		},
		initializeControlsEditMode: function()
		{
			var i, length;
			for(i = 0, length = this._controls.length; i < length; i++)
			{
				this._controls[i].setMode(BX.UI.EntityEditorMode.edit, { notify: false });
			}
		},
		initializeValidators: function()
		{
			//region Validators
			var i, length;
			this._validators = [];
			var validatorConfigs = BX.prop.getArray(this._settings, "validators", []);
			for(i = 0, length = validatorConfigs.length; i < length; i++)
			{
				var validator = this.createValidator(validatorConfigs[i]);
				if(validator)
				{
					this._validators.push(validator);
				}
			}
			//endregion
		},
		initializeToolPanel: function()
		{
			var buttonsOrder = BX.prop.getObject(this._settings, 'toolPanelButtonsOrder', {});
			var customButtons = BX.prop.getArray(this._settings, 'customToolPanelButtons', []);

			this._toolPanel = BX.UI.EntityEditorToolPanel.create(
				this._id,
				{
					container: this._isEmbedded ? this._formElement : document.body,
					editor: this,
					visible: false,
					buttonsOrder: buttonsOrder,
					customButtons: customButtons,
				}
			);
		},
		initializeDragDrop: function()
		{
			this._dragConfig = {};

			var sectionDragModes = {};
			sectionDragModes[BX.UI.EntityEditorMode.names.view]
				= sectionDragModes[BX.UI.EntityEditorMode.names.edit]
				= BX.prop.getBoolean(this._settings, "enableSectionDragDrop", true);

			this._dragConfig[BX.UI.EditorDragObjectType.section] =
				{
					scope: BX.UI.EditorDragScope.form,
					modes: sectionDragModes
				};

			var fieldDragModes = {};
			fieldDragModes[BX.UI.EntityEditorMode.names.view]
				= fieldDragModes[BX.UI.EntityEditorMode.names.edit]
				= BX.prop.getBoolean(this._settings, "enableFieldDragDrop", true);

			this._dragConfig[BX.UI.EditorDragObjectType.field] =
				{
					scope: BX.UI.EditorDragScope.form,
					modes: fieldDragModes
				};

			// if(this.canChangeScheme())
			// {
			// 	this._dragContainerController = BX.UI.EditorDragContainerController.create(
			// 		"editor_" + this.getId(),
			// 		{
			// 			charge: BX.UI.EditorSectionDragContainer.create({ editor: this }),
			// 			node: this._formElement
			// 		}
			// 	);
			// 	this._dragContainerController.addDragFinishListener(this._dropHandler);
			// }
		},
		initializeManagers: function()
		{
			this._userFieldManager = BX.prop.get(this._settings, "userFieldManager", null);
			this._configurationFieldManager = BX.UI.EntityConfigurationManager.create(
				this._id,
				{ editor: this }
			);

			let eventArgs = {
				id: this._id,
				editor: this,
				type: 'editor',
				configurationFieldManager: this._configurationFieldManager,
			};
			BX.onCustomEvent(window, "BX.UI.EntityConfigurationManager:onInitialize", [ this, eventArgs ]);
			this._configurationFieldManager = eventArgs.configurationFieldManager;
		},
		initializeCustomEditors: function()
		{
		},
		attachToEvents: function()
		{
			BX.bind(window, "resize", this._windowResizeHandler);

			BX.addCustomEvent("SidePanel.Slider:onOpenComplete", this._sliderOpenHandler);
			BX.addCustomEvent("SidePanel.Slider:onClose", this._sliderCloseHandler);
		},
		deattachFromEvents: function()
		{
			BX.unbind(window, "resize", this._windowResizeHandler);

			BX.removeCustomEvent("SidePanel.Slider:onOpenComplete", this._sliderOpenHandler);
			BX.removeCustomEvent("SidePanel.Slider:onClose", this._sliderCloseHandler);
		},
		initPull: function()
		{
			const settings = this._settings;

			BX.Event.ready(() => {
				if (
					!settings.pullTag
					|| !settings.pullModuleId
					|| !settings.canUsePull
				)
				{
					return;
				}

				const Pull = BX.PULL;
				if (!Pull)
				{
					console.error('pull is not initialized');

					return;
				}

				const entityPull = new BX.UI.EntityPull({
					editor: this,
				});

				Pull.subscribe({
					moduleId: settings.pullModuleId,
					callback: (data) => {
						if (data.command !== settings.pullTag)
						{
							return;
						}

						const eventId = data.params.eventId ?? null;
						if (eventId && this.eventIds.has(eventId))
						{
							return;
						}

						if (data.params.eventName === 'ITEMUPDATED')
						{
							entityPull.onItemUpdated();
						}
					},
				});
				Pull.extendWatch(settings.pullTag);

				BX.Event.bind(document, 'visibilitychange', () => {
					if (document.hidden)
					{
						return;
					}

					const isNeedReload = window.sessionStorage.getItem(this.needReloadStorageKey);
					if (isNeedReload === 'Y')
					{
						window.sessionStorage.removeItem(this.needReloadStorageKey);
						BX.Crm.EntityEditor.getDefault().reload();
					}
				});
			});
		},
		release: function()
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				this._controls[i].clearLayout();
			}

			this.deattachFromEvents();

			this.releaseAjaxForm();
			this.releaseReloadAjaxForm();
			this._attributeManager = null;
			this._container = BX.remove(this._container);

			this._isReleased = true;
		},
		onSliderOpen: function(event)
		{
			//Reset close confirmation flag
			this._enableCloseConfirmation = true;
			var eventArgs =
				{
					id: this._id,
					externalContext: this._externalContextId,
					context: this._contextId,
					entityTypeId: this._entityTypeId,
					entityId: this._entityId,
					model: this._model
				};
			BX.onCustomEvent(window, this.eventsNamespace + ":onOpen", [ this, eventArgs ]);
		},
		onSliderClose: function(event)
		{
			if(!this._enableCloseConfirmation)
			{
				return;
			}

			var slider = top.BX.SidePanel.Instance.getSliderByWindow(window);
			if(slider !== event.getSlider())
			{
				return;
			}

			if(!slider.isOpen())
			{
				return;
			}

			if(!this.hasChangedControls() && !this.hasChangedControllers())
			{
				return;
			}

			event.denyAction();

			if(BX.UI.EditorAuxiliaryDialog.isItemOpened("close_confirmation"))
			{
				return;
			}

			BX.UI.EditorAuxiliaryDialog.create(
				"close_confirmation",
				{
					title: BX.message("UI_ENTITY_EDITOR_CONFIRMATION"),
					content: BX.message("UI_ENTITY_EDITOR_CLOSE_CONFIRMATION"),
					zIndex: 100,
					buttons:
						[
							{
								id: "close",
								type: BX.UI.DialogButtonType.accept,
								text: BX.message("JS_CORE_WINDOW_CLOSE"),
								callback: this._closeConfirmationHandler
							},
							{
								id: "cancel",
								type: BX.UI.DialogButtonType.cancel,
								text: BX.message("JS_CORE_WINDOW_CANCEL"),
								callback: this._closeConfirmationHandler
							}
						]
				}
			).open();
		},
		onCloseConfirmButtonClick: function(button)
		{
			button.getDialog().close();
			if(button.getId() === "close")
			{
				this._enableCloseConfirmation = false;
				top.BX.SidePanel.Instance.getSliderByWindow(window).close();
			}
		},
		initializeAjaxForm: function()
		{
			if(this._ajaxForm)
			{
				return;
			}

			var ajaxData = BX.prop.getObject(this._settings, "ajaxData", {});
			var actionName = BX.prop.getString(ajaxData, "ACTION_NAME", "");
			var componentName = BX.prop.getString(ajaxData, "COMPONENT_NAME", "");
			var signedParameters = BX.prop.getString(ajaxData, "SIGNED_PARAMETERS", "");

			this._ajaxForms = {};
			this._actionTypes = {};

			this._actionTypes[BX.UI.EntityEditorActionIds.defaultActionId] = BX.UI.EntityEditorActionTypes.save;
			var defaultAjaxForm = this.createAjaxForm(
				{
					componentName: componentName,
					actionName: actionName,
					elementNode: this._formElement,
					signedParameters: signedParameters,
					enableRequiredUserFieldCheck: this._enableRequiredUserFieldCheck
				},
				{
					onSuccess: this.onSaveSuccess.bind(this),
					onFailure: this.onSaveFailure.bind(this)
				}
			);
			this._ajaxForms[BX.UI.EntityEditorActionIds.defaultActionId] = defaultAjaxForm;

			BX.addCustomEvent(defaultAjaxForm, "onAfterSubmit", this._formSubmitHandler);

			if (ajaxData.ADDITIONAL_ACTIONS && ajaxData.ADDITIONAL_ACTIONS.length > 0)
			{
				var additionalActions = ajaxData.ADDITIONAL_ACTIONS;
				for (var i=0; i < additionalActions.length; i++) {
					var action = additionalActions[i];

					this._actionTypes[action.ID] = action.ACTION_TYPE;

					var ajaxForm = this.createAjaxForm(
						{
							componentName: componentName,
							actionName: action.ACTION,
							elementNode: this._formElement,
							signedParameters: signedParameters,
							enableRequiredUserFieldCheck: this._enableRequiredUserFieldCheck
						},
						{
							onSuccess: this.onSaveSuccess.bind(this),
							onFailure: this.onSaveFailure.bind(this)
						}
					);
					BX.addCustomEvent(ajaxForm, "onAfterSubmit", this._formSubmitHandler);
					this._ajaxForms[action.ID] = ajaxForm;
				}
			}

			// compatibility
			this._ajaxForm = this._ajaxForms[BX.UI.EntityEditorActionIds.defaultActionId];

			//Disable submit action by pressing Enter key (if there is only one input on the form)
			this._formElement.setAttribute("onsubmit", "return false;");
		},
		createAjaxForm: function(options, callbacks)
		{
			var componentName = BX.prop.getString(options, "componentName", "");
			var actionName = BX.prop.getString(options, "actionName", "");
			var elementNode = BX.prop.getElementNode(options, "elementNode", null);
			var formData = BX.prop.getObject(options, "formData", null);

			if(componentName !== "")
			{
				if(actionName === "")
				{
					actionName = "save";
				}

				return BX.UI.ComponentAjax.create(
					this._id,
					{
						elementNode: elementNode,
						formData: formData,
						className: componentName,
						signedParameters: BX.prop.getString(options, "signedParameters", null),
						actionName: actionName,
						callbacks:
							{
								onSuccess: (callbacks ? callbacks.onSuccess : null),
								onFailure: (callbacks ? callbacks.onFailure : null)
							}
					}
				);
			}
			else
			{
				if(actionName === "")
				{
					actionName = "SAVE";
				}

				return BX.UI.AjaxForm.create(
					this._id,
					{
						elementNode: elementNode,
						formData: formData,
						config:
							{
								url: this._serviceUrl,
								method: "POST",
								dataType: "json",
								processData : true,
								onsuccess: (callbacks ? callbacks.onSuccess : null),
								data: Object.assign(
									{
										"ACTION": actionName,
										"ENABLE_REQUIRED_USER_FIELD_CHECK": BX.prop.getBoolean(options, "enableRequiredUserFieldCheck", false) ? 'Y' : 'N'
									},
									this.getAjaxFormConfigData()
								)
							}
					}
				);
			}
		},
		getAjaxFormConfigData: function ()
		{
			return {
				"ACTION_ENTITY_TYPE": this._entityTypeName
			};
		},
		releaseAjaxForm: function()
		{
			if(!this._ajaxForm)
			{
				return;
			}

			var _this = this;

			if (BX.Type.isObject(this._ajaxForms))
			{
				Object.keys(this._ajaxForms).forEach(function (ajaxForm) {
					BX.removeCustomEvent(_this._ajaxForms[ajaxForm], "onAfterSubmit", _this._formSubmitHandler);
					_this._ajaxForms[ajaxForm] = null;
				});
			}

			BX.removeCustomEvent(this._ajaxForm, "onAfterSubmit", this._formSubmitHandler);
			this._ajaxForm = null;
		},
		releaseReloadAjaxForm: function()
		{
			if(!this._reloadAjaxForm)
			{
				return;
			}

			this._reloadAjaxForm = null;
		},
		getId: function()
		{
			return this._id;
		},
		getEntityTypeName: function()
		{
			return this._entityTypeName;
		},
		getEntityId: function()
		{
			return this._entityId;
		},
		getOwnerInfo: function()
		{
			return this._model.getOwnerInfo();
		},
		getMode: function()
		{
			return this._mode;
		},
		getContextId: function()
		{
			return this._contextId;
		},
		getContext: function()
		{
			return this._context;
		},
		getExternalContextId: function()
		{
			return this._externalContextId;
		},
		getScheme: function()
		{
			return this._scheme;
		},
		canHideField: function()
		{
			return this._canHideField;
		},
		isVisible: function()
		{
			return this._container.offsetParent !== null;
		},
		isShowAlwaysFeautureEnabled: function()
		{
			return this._enableShowAlwaysFeauture;
		},
		isVisibilityPolicyEnabled: function()
		{
			return this._enableVisibilityPolicy;
		},
		isToolPanelAlwaysVisible: function()
		{
			return this._isToolPanelAlwaysVisible;
		},
		isBottomPanelEnabled: function()
		{
			return this._enableBottomPanel;
		},
		isConfigControlEnabled: function()
		{
			return this._enableConfigControl;
		},
		isSectionEditEnabled: function()
		{
			return this._enableSectionEdit;
		},
		isSectionCreationEnabled: function()
		{
			return this._enableSectionCreation && this.canChangeScheme();
		},
		isFieldsContextMenuEnabled: function()
		{
			return this._enableFieldsContextMenu;
		},
		isModeToggleEnabled: function()
		{
			return this._enableModeToggle;
		},
		isNew: function()
		{
			return this._isNew;
		},
		isReadOnly: function()
		{
			return this._readOnly;
		},
		isEmbedded: function()
		{
			return this._isEmbedded;
		},
		isEditInViewEnabled: function()
		{
			return this._entityId > 0;
		},
		getDetailManager: function()
		{
			if(typeof(BX.UI.EntityDetailManager) === "undefined")
			{
				return null;
			}

			return BX.UI.EntityDetailManager.get(BX.prop.getString(this._settings, "detailManagerId", ""));
		},
		getConfigurationFieldManager: function()
		{
			return this._configurationFieldManager;
		},
		getUserFieldManager: function()
		{
			return this._userFieldManager;
		},
		setAttributeManager: function(attributeManager)
		{
			if (BX.Type.isObject(attributeManager))
			{
				this._attributeManager = attributeManager;
			}

			return this._attributeManager;
		},
		getAttributeManager: function()
		{
			return this._attributeManager;
		},
		getHtmlEditorConfig: function(fieldName)
		{
			return BX.prop.getObject(this._htmlEditorConfigs, fieldName, null);
		},
		getAdditionalFieldsData: function()
		{
			return this.additionalFieldsData;
		},
		//region Validators
		createValidator: function(settings)
		{
			settings["editor"] = this;
			return BX.UI.EntityEditorValidatorFactory.create(
				BX.prop.getString(settings, "type", ""),
				settings
			);
		},
		//endregion
		//region Controls & Events
		getControlByIndex: function(index)
		{
			return (index >= 0 && index < this._controls.length) ? this._controls[index] : null;
		},
		getControlIndex: function(control)
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				if(this._controls[i] === control)
				{
					return i;
				}
			}
			return -1;
		},
		getControls: function()
		{
			return this._controls;
		},
		getControlCount: function()
		{
			return this._controls.length;
		},
		createControl: function(type, controlId, settings)
		{
			settings["serviceUrl"] = this._serviceUrl;
			settings["container"] = this._layoutContainer;
			settings["model"] = this._model;
			settings["editor"] = this;

			return BX.UI.EntityEditorControlFactory.create(type, controlId, settings);
		},
		addControlAt: function(control, index)
		{
			var options = {};
			if(index < this._controls.length)
			{
				options["anchor"] = this._controls[index].getWrapper();
				this._controls.splice(index, 0, control);
			}
			else
			{
				this._controls.push(control);
			}
			control.layout(options);
		},
		moveControl: function(control, index)
		{
			var qty = this._controls.length;
			var lastIndex = qty - 1;
			if(index < 0  || index > qty)
			{
				index = lastIndex;
			}

			var currentIndex = this.getControlIndex(control);
			if(currentIndex < 0 || currentIndex === index)
			{
				return false;
			}

			control.clearLayout();
			this._controls.splice(currentIndex, 1);
			qty--;

			var anchor = index < qty
				? this._controls[index].getWrapper()
				: null;

			if(index < qty)
			{
				this._controls.splice(index, 0, control);
			}
			else
			{
				this._controls.push(control);
			}

			if(anchor)
			{
				control.layout({ anchor: anchor });
			}
			else
			{
				control.layout();
			}

			this._config.moveSchemeElement(control.getSchemeElement(), index);
		},
		removeControl: function(control)
		{
			var index = this.getControlIndex(control);
			if(index < 0)
			{
				return false;
			}

			this.processControlRemove(control);
			control.clearLayout();
			this._controls.splice(index, 1);
		},
		getControlById: function(id)
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				var control = this._controls[i];
				if(control.getId() === id)
				{
					return control;
				}

				var child = control.getChildById(id);
				if(child)
				{
					return child;
				}
			}
			return null;
		},
		getControlByIdRecursive: function(name, controls)
		{
			if(!controls)
			{
				controls = this.getControls();
			}

			for (let i = 0; i < controls.length; i++)
			{
				if (!controls[i] instanceof BX.UI.EntityEditorControl)
				{
					continue;
				}

				if(controls[i].getId() === name)
				{
					return controls[i];
				}
				else if (
					controls[i] instanceof BX.UI.EntityEditorColumn
					|| controls[i] instanceof BX.UI.EntityEditorSection
				)
				{
					const res = this.getControlByIdRecursive(name, controls[i].getChildren());
					if (res)
					{
						return res;
					}
				}
			}

			return null;
		},
		quoteRegExp: function(regExpString)
		{
			return regExpString.replace(/([\\.+*?\[^\]$(){}=!<>|:])/g, "\\$1");
		},
		getCombinedIdRegExp: function (id)
		{
			return new RegExp(this.quoteRegExp(id).replace(/\\\[n?\d+\\]/gi, "\\[n?\\d+\\]"));
		},
		getControlByCombinedIdRecursive: function(id, controls)
		{
			if (!BX.type.isNotEmptyString(id))
			{
				return null;
			}

			let idRegExp = this.getCombinedIdRegExp(id);

			if(!controls)
			{
				controls = this.getControls();
			}

			for (let i = 0; i < controls.length; i++)
			{
				if (!controls[i] instanceof BX.UI.EntityEditorControl)
				{
					continue;
				}
				if(idRegExp.test(controls[i].getId()))
				{
					return controls[i];
				}
				else if (
					controls[i] instanceof BX.UI.EntityEditorColumn
					|| controls[i] instanceof BX.UI.EntityEditorSection
				)
				{
					const control = this.getControlByCombinedIdRecursive(id, controls[i].getChildren());
					if (control instanceof BX.UI.EntityEditorControl)
					{
						return control;
					}
				}
			}

			return null;
		},
		getAvailableControlByCombinedId: function(id)
		{
			let control = null;

			let element = this.getAvailableSchemeElementByCombinedName(id);
			if(element)
			{
				control = this.createControl(
					element.getType(),
					element.getName(),
					{
						schemeElement: element,
						model: this._model,
						mode: this._mode
					}
				);
			}

			return control;
		},
		getAllControls: function(controls)
		{
			let result = [];

			if(!controls)
			{
				controls = this.getControls();
			}

			for (var i=0; i < controls.length; i++)
			{
				if (controls[i] instanceof BX.UI.EntityEditorControl)
				{
					if (
						controls[i] instanceof BX.UI.EntityEditorColumn
						|| controls[i] instanceof BX.UI.EntityEditorSection
					)
					{
						const res = this.getAllControls(controls[i].getChildren());
						if(res)
						{
							result = result.concat(res);
						}
					}
					else
					{
						result.push(controls[i]);
					}
				}
			}

			return result;
		},
		getActiveControlCount: function()
		{
			return this._activeControls.length;
		},
		getActiveControlIndex: function(control)
		{
			var length = this._activeControls.length;
			if(length === 0)
			{
				return -1;
			}

			for(var i = 0; i < length; i++)
			{
				if(this._activeControls[i] === control)
				{
					return i;
				}
			}
			return -1;
		},
		getActiveControlById: function(id, recursive)
		{
			recursive = !!recursive;
			var length = this._activeControls.length;
			if(length === 0)
			{
				return null;
			}

			for(var i = 0; i < length; i++)
			{
				var control = this._activeControls[i];
				if(control.getId() === id)
				{
					return control;
				}

				if(recursive)
				{
					var child = control.getChildById(id);
					if(child)
					{
						return child;
					}
				}
			}
			return null;
		},
		getActiveControlByIndex: function(index)
		{
			return index >= 0 && index < this._activeControls.length ? this._activeControls[index] : null;
		},
		registerActiveControl: function(control)
		{
			var index = this.getActiveControlIndex(control);
			if(index >= 0)
			{
				return;
			}

			this._activeControls.push(control);
			control.setActive(true);
			if(this._mode !== BX.UI.EntityEditorMode.edit)
			{
				this._mode = BX.UI.EntityEditorMode.edit;
			}
		},
		unregisterActiveControl: function(control)
		{
			var index = this.getActiveControlIndex(control);
			if(index < 0)
			{
				return;
			}

			this._activeControls.splice(index, 1);
			control.setActive(false);
			if(this._activeControls.length === 0 && this._mode !== BX.UI.EntityEditorMode.view)
			{
				this._mode = BX.UI.EntityEditorMode.view;
			}
		},
		releaseActiveControls: function(options)
		{
			//region Release Event
			var eventArgs =
				{
					id: this._id,
					externalContext: this._externalContextId,
					context: this._contextId,
					entityTypeName: this._entityTypeName,
					entityId: this._entityId,
					model: this._model
				};
			BX.onCustomEvent(window, this.eventsNamespace + ":onRelease", [ this, eventArgs ]);
			//endregion

			var modifiedActiveControls = [];
			for(var i = 0, length = this._activeControls.length; i < length; i++)
			{
				var control = this._activeControls[i];
				if(control.isModeToggleEnabled())
				{
					control.setActive(false);
					control.toggleMode(false, options);
				}
				else
				{
					modifiedActiveControls.push(control);
				}
			}
			this._activeControls = modifiedActiveControls;
		},
		hasChangedControls: function()
		{
			for(var i = 0, length = this._activeControls.length; i < length; i++)
			{
				if(this._activeControls[i].isChanged())
				{
					return true;
				}
			}
			return false;
		},
		hasChangedControllers: function()
		{
			for(var i = 0, length = this._controllers.length; i < length; i++)
			{
				if(this._controllers[i].isChanged())
				{
					return true;
				}
			}
			return false;
		},
		isWaitingForInput: function()
		{
			if(this._mode !== BX.UI.EntityEditorMode.edit)
			{
				return false;
			}

			for(var i = 0, length = this._activeControls.length; i < length; i++)
			{
				if(this._activeControls[i].isWaitingForInput())
				{
					return true;
				}
			}
			return false;
		},
		processControlModeChange: function(control, options)
		{
			if(control.getMode() === BX.UI.EntityEditorMode.edit)
			{
				this.registerActiveControl(control);
			}
			else //BX.UI.EntityEditorMode.view
			{
				this.unregisterActiveControl(control);
			}

			if(this.getActiveControlCount() > 0)
			{
				this.showToolPanel();
			}
			else if (!this.isToolPanelAlwaysVisible())
			{
				this.hideToolPanel();
			}

			var eventArgs = {
				control: control,
			}
			BX.onCustomEvent(window, this.eventsNamespace + ":onControlModeChange", [ this, eventArgs ]);
		},
		processControlChange: function(control, params, options)
		{
			this.showToolPanel();

			if (!BX.prop.getBoolean(options, 'skipEvents', false))
			{
				BX.onCustomEvent(
					window,
					this.eventsNamespace + ':onControlChange',
					[this, { control: control, params: params }],
				);
			}
		},
		processControlAdd: function(control, options)
		{
			this.removeAvailableSchemeElement(control.getSchemeElement());

			if (!BX.prop.getBoolean(options, 'skipEvents', false))
			{
				BX.onCustomEvent(
					this,
					this.eventsNamespace + ':onControlAdd',
					[this, { control: control, params: {} }],
				);
			}
		},
		processControlMove: function(control, options)
		{
			if (!BX.prop.getBoolean(options, "skipEvents", false))
			{
				BX.onCustomEvent(
					this,
					this.eventsNamespace + ":onControlMove",
					[ this, { control: control, params: options } ]
				);
			}
		},
		processControlRemove: function(control, options)
		{
			if(control instanceof BX.UI.EntityEditorField)
			{
				this.addAvailableSchemeElement(control.getSchemeElement());
			}
			else if(control instanceof BX.UI.EntityEditorSection)
			{
				var children = control.getChildren();
				for(var i= 0, length = children.length; i < length; i++)
				{
					this.addAvailableSchemeElement(children[i].getSchemeElement());
				}
			}

			if (!BX.prop.getBoolean(options, "skipEvents", false))
			{
				BX.onCustomEvent(
					this,
					this.eventsNamespace + ":onControlRemove",
					[ this, { control: control, params: {} } ]
				);
			}
		},
		processSchemeChange: function()
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				this._controls[i].processSchemeChange();
			}
		},
		//endregion
		//region Available Scheme Elements
		getAvailableSchemeElements: function()
		{
			return this._availableSchemeElements;
		},
		addAvailableSchemeElement: function(schemeElement)
		{
			this._availableSchemeElements.push(schemeElement);
			this._areAvailableSchemeElementsChanged = true;
			this.notifyAvailableSchemeElementsChanged();
		},
		removeAvailableSchemeElement: function(element)
		{
			var index = this.getAvailableSchemeElementIndex(element);
			if(index < 0)
			{
				return;
			}

			this._availableSchemeElements.splice(index, 1);
			this._areAvailableSchemeElementsChanged = true;
			this.notifyAvailableSchemeElementsChanged();
		},
		getAvailableSchemeElementIndex: function(element)
		{
			var schemeElements = this._availableSchemeElements;
			for(var i = 0, length = schemeElements.length; i < length; i++)
			{
				if(schemeElements[i] === element)
				{
					return i;
				}
			}
			return -1;
		},
		getAvailableSchemeElementByName: function(name)
		{
			var schemeElements = this._availableSchemeElements;
			for(var i = 0, length = schemeElements.length; i < length; i++)
			{
				var schemeElement = schemeElements[i];
				if(schemeElement.getName() === name)
				{
					return schemeElement;
				}
			}
			return null;
		},
		getAvailableSchemeElementByCombinedName: function(name)
		{
			if (!BX.type.isNotEmptyString(name))
			{
				return null;
			}

			let nameRegExp = this.getCombinedIdRegExp(name);

			const schemeElements = this._availableSchemeElements;
			for (let i = 0, length = schemeElements.length; i < length; i++)
			{
				if(nameRegExp.test(schemeElements[i].getName()))
				{
					return schemeElements[i];
				}
			}

			return null;
		},
		hasAvailableSchemeElements: function()
		{
			return (this._availableSchemeElements.length > 0);
		},
		getSchemeElementByName: function(name)
		{
			return this._scheme.findElementByName(name, { isRecursive: true });
		},
		notifyAvailableSchemeElementsChanged: function()
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				this._controls[i].processAvailableSchemeElementsChange();
			}
		},
		hasTransferableElements: function(excludedNames)
		{
			var excludedCount = 0;
			if(BX.type.isArray(excludedNames))
			{
				excludedCount = excludedNames.length;
			}

			var columns = this._scheme.getElements();
			for (var columnsIterator = 0, columnsCount = columns.length; columnsIterator < columnsCount; columnsIterator++)
			{
				var sections = columns[columnsIterator].getElements();
				for(var sectionsIterator = 0, sectionsCount = sections.length; sectionsIterator < sectionsCount; sectionsIterator++)
				{
					var section = sections[sectionsIterator];
					var isExcluded = false;
					if(excludedCount > 0)
					{
						var sectionName = section.getName();
						for(var j = 0; j < excludedCount; j++)
						{
							if(excludedNames[j] === sectionName)
							{
								isExcluded = true;
								break;
							}
						}
					}

					if(isExcluded)
					{
						continue;
					}

					var childElements = section.getElements();
					for(var k = 0, childrenCount = childElements.length; k < childrenCount; k++)
					{
						if(childElements[k].isTransferable() &&  childElements[k].getName() !== "")
						{
							return true;
						}
					}
				}
			}

			return false;
		},
		//endregion
		//region Controllers
		createController: function(data)
		{
			return BX.UI.EntityEditorControllerFactory.create(
				BX.prop.getString(data, "type", ""),
				BX.prop.getString(data, "name", ""),
				{
					config: BX.prop.getObject(data, "config", {}),
					model: this._model,
					editor: this
				}
			);
		},
		processControllerChange: function(controller)
		{
			this.showToolPanel();
			var eventArgs = {
				controller: controller,
			}
			BX.onCustomEvent(window, this.eventsNamespace + ":onControllerChange", [ this, eventArgs ]);
		},
		getControllers: function()
		{
			return this._controllers;
		},
		//endregion
		//region Layout
		getContainer: function()
		{
			return this._container;
		},
		prepareContextDataLayout: function(context, parentName)
		{
			for(var key in context)
			{
				if(!context.hasOwnProperty(key))
				{
					continue;
				}

				var item = context[key];
				var name = key;
				if(BX.type.isNotEmptyString(parentName))
				{
					name = parentName + "[" + name + "]";
				}
				if(BX.type.isPlainObject(item))
				{
					this.prepareContextDataLayout(item, name);
				}
				else
				{
					this._formElement.appendChild(
						BX.create("input", { props: { type: "hidden", name: name, value: item } })
					);
				}
			}
		},
		layout: function()
		{
			var eventArgs = { cancel: false };
			BX.onCustomEvent(window, this.eventsNamespace + ":onBeforeLayout", [ this, eventArgs ]);
			if(eventArgs["cancel"])
			{
				return;
			}

			this.prepareContextDataLayout(this._context, "");

			if(this._toolPanel)
			{
				this._toolPanel.layout();
			}

			var userFieldLoaders =
			{
				edit: BX.UI.EntityUserFieldLayoutLoader.create(
					this._id,
					{ mode: BX.UI.EntityEditorMode.edit, enableBatchMode: true, owner: this }
				),
				view: BX.UI.EntityUserFieldLayoutLoader.create(
					this._id,
					{ mode: BX.UI.EntityEditorMode.view, enableBatchMode: true, owner: this }
				)
			};

			var i, length, control;
			for(i = 0, length = this._controls.length; i < length; i++)
			{
				control = this._controls[i];
				var mode = control.getMode();

				var layoutOptions =
					{
						userFieldLoader: userFieldLoaders[BX.UI.EntityEditorMode.getName(mode)],
						enableFocusGain: !this._isEmbedded
					};
				control.layout(layoutOptions);

				if(mode === BX.UI.EntityEditorMode.edit)
				{
					this.registerActiveControl(control);
				}
			}

			for(var key in userFieldLoaders)
			{
				if(userFieldLoaders.hasOwnProperty(key))
				{
					userFieldLoaders[key].runBatch();
				}
			}

			if(this.getActiveControlCount() > 0)
			{
				this.showToolPanel();
			}

			if(this._model.isCaptionEditable())
			{
				BX.bind(
					this._pageTitle,
					"click",
					BX.delegate(this.onPageTileClick, this)
				);

				if(this._editPageTitleButton)
				{
					BX.bind(
						this._editPageTitleButton,
						"click",
						BX.delegate(this.onPageTileClick, this)
					);
				}
			}

			if(this._buttonContainer && this.isBottomPanelEnabled())
			{
				if(this.isSectionCreationEnabled())
				{
					this._createSectionButton = BX.create(
						"span",
						{
							props: { className: "ui-entity-add-widget-link" },
							text: BX.message("UI_ENTITY_EDITOR_CREATE_SECTION"),
							events: { click: BX.delegate(this.onCreateSectionButtonClick, this) }
						}
					);
					this._buttonContainer.appendChild(this._createSectionButton);
				}

				if(this.isConfigControlEnabled())
				{
					var configScope = this._config.getScope();
					var configScopeCaption = BX.UI.EntityConfigScope.getCaption(configScope);
					this._buttonContainer.appendChild(
						BX.create(
							"span",
							{
								props:
									{
										className: configScope === BX.UI.EntityConfigScope.common ? "ui-entity-card-common" : "ui-entity-card-private",
										title: configScopeCaption
									}
							}
						)
					);

					this._configMenuButton = BX.create(
						"span",
						{
							props: { className: "ui-entity-settings-link" },
							text: configScopeCaption,
							events: { click: BX.delegate(this.onConfigMenuButtonClick, this) }
						}
					);
					this._buttonContainer.appendChild(this._configMenuButton);
				}
			}

			this.adjustButtons();
		},
		refreshLayout: function(options)
		{
			var userFieldLoaders =
				{
					edit: BX.UI.EntityUserFieldLayoutLoader.create(
						this._id,
						{ mode: BX.UI.EntityEditorMode.edit, enableBatchMode: true, owner: this }
					),
					view: BX.UI.EntityUserFieldLayoutLoader.create(
						this._id,
						{ mode: BX.UI.EntityEditorMode.view, enableBatchMode: true, owner: this }
					)
				};


			if(!BX.type.isPlainObject(options))
			{
				options = {};
			}

			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				var control = this._controls[i];
				var mode = control.getMode();

				var layoutOptions = BX.mergeEx(
					options,
					{
						userFieldLoader: userFieldLoaders[BX.UI.EntityEditorMode.getName(mode)],
						enableFocusGain: !this._isEmbedded
					}
				);
				control.refreshLayout(layoutOptions);
			}

			for(var key in userFieldLoaders)
			{
				if(userFieldLoaders.hasOwnProperty(key))
				{
					userFieldLoaders[key].runBatch();
				}
			}

			this.adjustButtons();

			BX.onCustomEvent(window, this.eventsNamespace + ":onRefreshLayout", [ this ]);
		},
		refreshViewModeLayout: function(options)
		{
			var userFieldLoader = BX.UI.EntityUserFieldLayoutLoader.create(
				this._id,
				{ mode: BX.UI.EntityEditorMode.view, enableBatchMode: true, owner: this }
			);

			if(!BX.type.isPlainObject(options))
			{
				options = {};
			}

			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				var control = this._controls[i];

				var layoutOptions = BX.mergeEx(
					options,
					{
						userFieldLoader: userFieldLoader,
						enableFocusGain: false,
						isRefreshViewModeLayout: true
					}
				);
				control.refreshViewModeLayout(layoutOptions);
			}

			userFieldLoader.runBatch();

			BX.onCustomEvent(window, this.eventsNamespace + ":onRefreshViewModeLayout", [ this ]);
		},
		//endregion
		switchControlMode: function(control, mode, options)
		{
			if(!this.isModeToggleEnabled())
			{
				return;
			}

			if(mode === BX.UI.EntityEditorMode.view)
			{
				if(control.checkModeOption(BX.UI.EntityEditorModeOptions.saveOnExit))
				{
					this._modeSwitch.getQueue().add(control, BX.UI.EntityEditorMode.view);
					this._modeSwitch.run();
				}
				else
				{
					control.setMode(mode, { options: options, notify: true });
					control.refreshLayout();
				}
			}
			else// if(mode === BX.UI.EntityEditorMode.edit)
			{
				if(!BX.UI.EntityEditorModeOptions.check(options, BX.UI.EntityEditorModeOptions.exclusive))
				{
					control.setMode(BX.UI.EntityEditorMode.edit, { options: options, notify: true });
					control.refreshLayout();
				}
				else
				{
					var queuedControlQty = 0;
					for(var i = 0, length = this._activeControls.length; i < length; i++)
					{
						var activeControl = this._activeControls[i];
						if(activeControl.checkModeOption(BX.UI.EntityEditorModeOptions.saveOnExit))
						{
							this._modeSwitch.getQueue().add(activeControl, BX.UI.EntityEditorMode.view, options);
							queuedControlQty++;
						}
					}

					if(queuedControlQty > 0)
					{
						this._modeSwitch.getQueue().add(control, BX.UI.EntityEditorMode.edit, options);
						this._modeSwitch.run();
					}
					else
					{
						control.setMode(BX.UI.EntityEditorMode.edit, { options: options, notify: true });
						control.refreshLayout();
					}
				}
			}
		},
		switchToViewMode: function(options)
		{
			this.releaseActiveControls(options);
			if(this.getActiveControlCount() > 0)
			{
				return; // some controls prevent switching to view mode
			}
			if (!this.isToolPanelAlwaysVisible())
			{
				this.hideToolPanel();
			}

			var eventArgs = {
				options: options,
			}
			BX.onCustomEvent(window, this.eventsNamespace + ":onSwitchToViewMode", [ this, eventArgs ]);
		},
		switchTitleMode: function(mode)
		{
			if(mode === BX.UI.EntityEditorMode.edit)
			{

				this._pageTitle.style.display = "none";
				document.body.classList.add('--edit__title-input');

				if (this._buttonWrapper)
				{
					this._buttonWrapper.style.display = "none";
				}

				this._pageTitleInput = BX.create(
					"input",
					{
						props:
							{
								type: "text",
								className: this.pageTitleInputClassName,
								value: this._model.getCaption()
							}
					}
				);
				//this._pageTitle.parentNode.insertBefore(this._pageTitleInput, this._buttonWrapper);
				this._pageTitle.parentNode.insertBefore(this._pageTitleInput, this._pageTitle);
				this._pageTitleInput.focus();

				window.setTimeout(
					BX.delegate(
						function()
						{
							BX.bind(document, "mousedown", this._pageTitleExternalClickHandler);
							BX.bind(this._pageTitleInput, "keyup", this._pageTitleKeyPressHandler);
						},
						this
					),
					300
				);
			}
			else
			{
				if(this._pageTitleInput)
				{
					this._pageTitleInput = BX.remove(this._pageTitleInput);
				}

				this._pageTitle.innerHTML = BX.util.htmlspecialchars(this._model.getCaption());
				this._pageTitle.style.display = "";

				if(this._buttonWrapper)
				{
					this._buttonWrapper.style.display = "";
				}

				BX.unbind(document, "mousedown", this._pageTitleExternalClickHandler);
				BX.unbind(this._pageTitleInput, "keyup", this._pageTitleKeyPressHandler);

				this.adjustTitle();
			}
		},
		adjustTitle: function()
		{
			if(!this._enablePageTitleControls || !this._buttonWrapper)
			{
				return;
			}

			var caption = this._model.getCaption().trim();
			var captionTail = "";
			var match = caption.match(/\s+\S+\s*$/);
			if(match)
			{
				captionTail = caption.substr(match["index"]);
				caption = caption.substr(0, match["index"]);
			}
			else
			{
				captionTail = caption;
				caption = "";
			}

			BX.cleanNode(this._buttonWrapper);
			if(captionTail !== "")
			{
				this._buttonWrapper.appendChild(document.createTextNode(captionTail));
			}
			if(this._editPageTitleButton)
			{
				this._buttonWrapper.appendChild(this._editPageTitleButton);
			}
			if(this._copyPageUrlButton)
			{
				this._buttonWrapper.appendChild(this._copyPageUrlButton);
			}

			this._pageTitle.innerHTML = BX.util.htmlspecialchars(caption);
		},
		adjustSize: function()
		{
			if(!this._enablePageTitleControls || !this._pageTitle)
			{
				return;
			}

			var wrapper = this._pageTitle.parentNode ? this._pageTitle.parentNode : this._pageTitle;
			var enableNarrowSize = wrapper.offsetWidth <= 480 && this._model.getCaption().length >= 40;
			if(enableNarrowSize && !BX.hasClass(wrapper, "pagetitle-narrow"))
			{
				BX.addClass(wrapper, "pagetitle-narrow");
			}
			else if(!enableNarrowSize && BX.hasClass(wrapper, "pagetitle-narrow"))
			{
				BX.removeClass(wrapper, "pagetitle-narrow");
			}

		},
		adjustButtons: function()
		{
			//Move configuration menu button to last section if bottom panel is hidden.
			if(this._config.isScopeToggleEnabled() && !this._enableBottomPanel && this._controls.length > 0)
			{
				var control = this._controls[this._controls.length - 1];
				if(control instanceof BX.UI.EntityEditorColumn)
				{
					if(control._sections.length > 0)
					{
						control = control._sections[control._sections.length - 1]
					}
					else
					{
						// nowhere to add
						return;
					}
				}
				control.addButtonElement(
					BX.create(
						"span",
						{
							props:
								{
									className: this._config.getScope() === BX.UI.EntityConfigScope.common
										? "ui-entity-card-common" : "ui-entity-card-private"
								},
							events: { click: BX.delegate(this.onConfigMenuButtonClick, this) }
						}
					),
					{ position: "right" }
				);
			}
		},
		showToolPanel: function()
		{
			if(!this._toolPanel || this._toolPanel.isVisible())
			{
				return;
			}

			this._toolPanel.setVisible(true);
			if(this._parentContainer)
			{
				this._parentContainer.style.paddingBottom = "50px";

				document.body.style.paddingBottom = "60px";
				document.body.style.height = "auto";
			}
		},
		hideToolPanel: function()
		{
			if(!this._toolPanel || !this._toolPanel.isVisible())
			{
				return;
			}

			this._toolPanel.setVisible(false);
			if(this._parentContainer)
			{
				this._parentContainer.style.paddingBottom = "";

				document.body.style.paddingBottom = "";
				document.body.style.height = "";
			}
		},
		showMessageDialog: function(id, title, content)
		{
			var dlg = BX.UI.EditorAuxiliaryDialog.create(
				id,
				{
					title: title,
					content: content,
					buttons:
						[
							{
								id: "continue",
								type: BX.UI.DialogButtonType.accept,
								text: BX.message("UI_ENTITY_EDITOR_CONTINUE"),
								callback: function(button) { button.getDialog().close(); }
							}
						]
				}
			);
			dlg.open();
		},
		getMessage: function(name)
		{
			var m = BX.UI.EntityEditor.messages;
			return m.hasOwnProperty(name) ? m[name] : name;
		},
		getGlobalEventName: function(eventName)
		{
			return eventName;
		},
		getFormElement: function()
		{
			return this._formElement;
		},
		isChanged: function()
		{
			return this._isNew || this.hasChangedControls() || this.hasChangedControllers();
		},
		getEntityTypeForAction: function()
		{
			return this._entityTypeName;
		},
		prepareControllersData: function(data)
		{
			if (!BX.type.isPlainObject(data))
			{
				data = {};
			}
			for(var i = 0, length = this._controllers.length; i < length; i++)
			{
				if (BX.Type.isFunction(this._controllers[i].onBeforesSaveControl))
				{
					data = this._controllers[i].onBeforesSaveControl(data);
				}
			}
			return data;
		},
		savePageTitle: function()
		{
			if(!this._pageTitleInput)
			{
				return;
			}

			var title = BX.util.trim(this._pageTitleInput.value);
			if(title === "")
			{
				return;
			}

			this._model.setCaption(title);
			var data =
				{
					"ACTION": "SAVE",
					"ACTION_ENTITY_ID": this._entityId,
					"ACTION_ENTITY_TYPE": this.getEntityTypeForAction(),
					"PARAMS": BX.prop.getObject(this._context, "PARAMS", {})
				};

			this._model.prepareCaptionData(data);
			data = BX.mergeEx(data, this.prepareControllersData(data));
			BX.ajax(
				{
					url: this._serviceUrl,
					method: "POST",
					dataType: "json",
					data: data,
					onsuccess: BX.delegate(this.onSaveSuccess, this)
				}
			);
		},
		performAction: function (actionId)
		{
			if (!this._actionTypes)
			{
				return;
			}
			if (this._actionTypes[actionId] === BX.UI.EntityEditorActionTypes.save)
			{
				this.performSaveChangedAction(actionId);
			}
			else if (this._actionTypes[actionId] === BX.UI.EntityEditorActionTypes.direct)
			{
				this.performDirectAction(actionId);
			}
		},
		performDirectAction: function (actionId)
		{
			if(this._toolPanel)
			{
				this._toolPanel.setLocked(true);
			}

			var eventArgs = this.getActionEventArguments();
			eventArgs.actionId = actionId;

			BX.onCustomEvent(window, this.eventsNamespace + ":onDirectAction", [ this, eventArgs ]);

			if(eventArgs["cancel"])
			{
				return;
			}

			this._ajaxForms[actionId].submit();
		},
		saveChanged: function()
		{
			this.performSaveChangedAction(BX.UI.EntityEditorActionIds.defaultActionId);
		},
		performSaveChangedAction: function(action)
		{
			if(!this._isNew && !this.hasChangedControls() && !this.hasChangedControllers() && !this.isWaitingForInput())
			{
				this._modeSwitch.reset();
				this.releaseActiveControls();
				this.refreshLayout({ reset: true });
				if (!this.isToolPanelAlwaysVisible())
				{
					this.hideToolPanel();
				}

				BX.onCustomEvent(window, this.eventsNamespace + ":onNothingChanged", [ this ]);
			}
			else
			{
				this._modeSwitch.reset();
				this._modeSwitch.getQueue().addBatch(this._activeControls, BX.UI.EntityEditorMode.view);
				this._modeSwitch.setRunAction(action);
				this._modeSwitch.run();
			}
		},
		saveDelayed: function(delay)
		{
			this.performSaveDelayedAction(BX.UI.EntityEditorActionIds.defaultActionId, delay);
		},
		performSaveDelayedAction: function (action, delay)
		{
			if(typeof(delay) === "undefined")
			{
				delay = 0;
			}

			if(this._delayedSaveHandle > 0)
			{
				window.clearTimeout(this._delayedSaveHandle);
			}
			this._delayedSaveHandle = window.setTimeout(BX.delegate(function () {
				this.performSaveAction(action);
			}, this), delay);
		},
		save: function()
		{
			this.performSaveAction(BX.UI.EntityEditorActionIds.defaultActionId);
		},
		performSaveAction: function (action)
		{
			if(this._toolPanel)
			{
				this._toolPanel.setLocked(true);
			}

			this.registerSaveAnalyticsEvent('attempt');

			var result = BX.UI.EntityValidationResult.create();
			this.validate(result).then(
				BX.delegate(
					function()
					{
						if(this._bizprocManager)
						{
							return this._bizprocManager.onBeforeSave(result);
						}

						var promise = new BX.Promise();
						window.setTimeout(function(){ promise.fulfill(); }, 0);
						return promise;
					},
					this
				)
			).then(
				BX.delegate(
					function()
					{
						if(result.getStatus())
						{
							if (this.performInnerSaveAction(action) !== false)
							{
								if (this._bizprocManager)
								{
									this._bizprocManager.onAfterSave();
								}
							}
							else
							{
								if(this._toolPanel)
								{
									this._toolPanel.setLocked(false);
								}

								this.registerSaveAnalyticsEvent('error');
							}
						}
						else
						{
							if(this.isVisible())
							{
								var field = result.getTopmostField();
								if(field)
								{
									field.focus();
									var toolPanelOffset = 130;
									window.scroll(window.pageXOffset, window.pageYOffset + toolPanelOffset);
								}
							}

							if(this._toolPanel)
							{
								this._toolPanel.setLocked(false);
							}

							this.registerSaveAnalyticsEvent('error');
							BX.onCustomEvent(window, this.eventsNamespace + ":onFailedValidation", [ this, result ]);
						}
					},
					this
				)
			);

			if(this._delayedSaveHandle > 0)
			{
				this._delayedSaveHandle = 0;
			}
		},
		saveControl: function(control)
		{
			if(this._entityId <= 0 && this._model.isIdentifiable())
			{
				return;
			}

			var result = BX.UI.EntityValidationResult.create();
			control.validate(result);

			if(!result.getStatus())
			{
				return;
			}

			var data =
				{
					"ACTION": "SAVE",
					"ACTION_ENTITY_ID": this._entityId,
					"ACTION_ENTITY_TYPE": this.getEntityTypeForAction()
				};

			data = BX.mergeEx(data, this._context);
			control.save();
			control.prepareSaveData(data);

			data = BX.mergeEx(data, this.prepareControllersData(data));

			BX.ajax(
				{
					method: "POST",
					dataType: "json",
					url: this._serviceUrl,
					data: data,
					onsuccess: function(result) {
						this.onSaveSuccess(result, {switchMode: false});
					}.bind(this)
				}
			);
		},
		saveData: function(data)
		{
			if(this._entityId <= 0 && this._model.isIdentifiable())
			{
				return;
			}

			data = BX.mergeEx(data, this._context);
			data = BX.mergeEx(
				data,
				{
					"ACTION": "SAVE",
					"ACTION_ENTITY_ID": this._entityId,
					"ACTION_ENTITY_TYPE": this.getEntityTypeForAction()
				}
			);

			BX.ajax(
				{
					method: "POST",
					dataType: "json",
					url: this._serviceUrl,
					data: data,
					onsuccess: BX.delegate(this.onSaveSuccess, this)
				}
			);
		},
		reload: function()
		{
			if(this._isRequestRunning)
			{
				return;
			}
			if(this._entityId <= 0 && this._model.isIdentifiable())
			{
				return;
			}
			var eventArgs = this.getActionEventArguments();

			BX.onCustomEvent(window, this.eventsNamespace + ':onEntityStartReload', [ this, eventArgs ]);

			if(eventArgs["cancel"])
			{
				return;
			}

			var ajaxData = BX.prop.getObject(this._settings, 'ajaxData', {});
			var componentName = BX.prop.getString(ajaxData, 'COMPONENT_NAME', '');
			var signedParameters = BX.prop.getString(ajaxData, 'SIGNED_PARAMETERS', '');
			var reloadActionFormData = BX.prop.getObject(ajaxData,'RELOAD_FORM_DATA', {});
			var reloadActionName = BX.prop.getString(ajaxData, "RELOAD_ACTION_NAME", '');
			if (reloadActionName === '')
			{
				console.warn("Can't reload entity editor because RELOAD_ACTION_NAME is not defined");
				return;
			}

			this._reloadAjaxForm = this.createAjaxForm(
				{
					componentName: componentName,
					actionName: reloadActionName,
					signedParameters: signedParameters,
					formData: reloadActionFormData,
					enableRequiredUserFieldCheck: false
				},
				{
					onSuccess: this.onReloadSuccess.bind(this)
				}
			);

			if(this._reloadAjaxForm)
			{
				this._reloadAjaxForm.submit();
			}
		},
		setValidationEnabled: function(isEnabled)
		{
			isEnabled = !!isEnabled;

			this._validationEnabled = isEnabled;

			if (this._userFieldManager)
			{
				this._userFieldManager.setValidationEnabled(isEnabled);
			}
		},
		validate: function(result)
		{
			const promise = new BX.Promise();

			if (this._validationEnabled)
			{
				for(let i = 0, length = this._activeControls.length; i < length; i++)
				{
					this._activeControls[i].validate(result);
				}

				if (this._userFieldManager)
				{
					this._userFieldManager.validate(result).then(
						BX.delegate(function() { promise.fulfill(); }, this)
					);
				}
				else
				{
					promise.fulfill();
				}
			}
			else
			{
				promise.fulfill();
			}

			return promise;
		},
		isRequestRunning: function()
		{
			return this._isRequestRunning;
		},
		getActionEventArguments: function()
		{
			return {
				id: this._id,
				externalContext: this._externalContextId,
				context: this._contextId,
				entityTypeName: this._entityTypeName,
				entityId: this._entityId,
				model: this._model,
				cancel: false
			};
		},
		innerSave: function()
		{
			this.performInnerSaveAction(BX.UI.EntityEditorActionIds.defaultActionId);
		},
		performInnerSaveAction: function(action)
		{
			if(this._isRequestRunning)
			{
				return true;
			}

			var i, length;
			for(i = 0, length = this._controllers.length; i < length; i++)
			{
				this._controllers[i].onBeforeSubmit();
			}

			for(i = 0, length = this._activeControls.length; i < length; i++)
			{
				var control = this._activeControls[i];

				control.save();
				control.onBeforeSubmit();

				if(control.isSchemeChanged())
				{
					this._config.updateSchemeElement(control.getSchemeElement());
				}
			}

			if(this._areAvailableSchemeElementsChanged)
			{
				this._scheme.setAvailableElements(this._availableSchemeElements);
				this._areAvailableSchemeElementsChanged = false;
			}

			if(this._config && this._config.isChanged())
			{
				this._config.save(false);
			}

			//region Rise Save Event
			var eventArgs = this.getActionEventArguments();
			eventArgs.actionId = action;

			BX.onCustomEvent(window, this.eventsNamespace + ":onSave", [ this, eventArgs ]);

			if(eventArgs["cancel"])
			{
				return false;
			}

			var enableCloseConfirmation = BX.prop.getBoolean(
				eventArgs,
				"enableCloseConfirmation",
				null
			);
			if(BX.type.isBoolean(enableCloseConfirmation))
			{
				this._enableCloseConfirmation = enableCloseConfirmation;
			}

			var ajaxFormToSubmit = null;

			if (this._ajaxForms && this._ajaxForms[action])
			{
				ajaxFormToSubmit = this._ajaxForms[action];
			}
			else
			{
				ajaxFormToSubmit = this._ajaxForm;
			}

			if(ajaxFormToSubmit)
			{
				const eventId = BX.Text.getRandom();

				this.eventIds.add(eventId);

				return ajaxFormToSubmit.submit({
					data: {
						EVENT_ID: eventId,
					},
				});
			}

			return true;
			//endregion
		},
		cancel: function()
		{
			//region Rise Cancel Event
			var eventArgs = this.getActionEventArguments();

			BX.onCustomEvent(window, this.eventsNamespace + ":onCancel", [ this, eventArgs ]);

			if(eventArgs["cancel"])
			{
				return;
			}
			//endregion

			var enableCloseConfirmation = BX.prop.getBoolean(
				eventArgs,
				"enableCloseConfirmation",
				null
			);
			if(BX.type.isBoolean(enableCloseConfirmation))
			{
				this._enableCloseConfirmation = enableCloseConfirmation;
			}

			if(this.hasChangedControls() || this.hasChangedControllers())
			{
				window.setTimeout(
					BX.delegate(this.openCancellationConfirmationDialog, this),
					250
				);
				return;
			}

			this.innerCancel();
		},
		innerCancel: function()
		{
			var i, length;
			for(i = 0, length = this._controllers.length; i < length; i++)
			{
				this._controllers[i].innerCancel();
			}

			this.rollback();

			if(this._isNew)
			{
				this.refreshLayout();
				if(typeof(top.BX.SidePanel) !== "undefined")
				{
					window.setTimeout(
						function ()
						{
							var slider = top.BX.SidePanel.Instance.getSliderByWindow(window);
							if(slider && slider.isOpen())
							{
								slider.close(false);
							}
						},
						250
					);
				}
			}
			else
			{
				this.switchToViewMode({ refreshLayout: false });
				this.refreshLayout();
			}
		},
		openCancellationConfirmationDialog: function()
		{
			if (this._confirmationCancelDialog)
			{
				return;
			}

			this._confirmationCancelDialog = BX.UI.EditorAuxiliaryDialog.create(
				"cancel_confirmation",
				{
					title: BX.message("UI_ENTITY_EDITOR_CONFIRMATION"),
					content: BX.message("UI_ENTITY_EDITOR_CANCEL_CONFIRMATION"),
					buttons:
						[
							{
								id: "yes",
								type: BX.UI.DialogButtonType.accept,
								text: BX.message("UI_ENTITY_EDITOR_YES"),
								callback: this._cancelConfirmationHandler
							},
							{
								id: "no",
								type: BX.UI.DialogButtonType.cancel,
								text: BX.message("UI_ENTITY_EDITOR_NO"),
								callback: this._cancelConfirmationHandler
							}
						]
				}
			);
			this._confirmationCancelDialog.open();
		},
		onCancelConfirmButtonClick: function(button)
		{
			button.getDialog().close();
			this._confirmationCancelDialog = null;
			if(button.getId() === "yes")
			{
				this.innerCancel();
			}
		},
		rollback: function()
		{
			this._model.rollback();

			var i, length;
			for(i = 0, length = this._controllers.length; i < length; i++)
			{
				this._controllers[i].rollback();
			}

			for(i = 0, length = this._activeControls.length; i < length; i++)
			{
				this._activeControls[i].rollback();
			}

			if(this._areAvailableSchemeElementsChanged)
			{
				this._availableSchemeElements = this._scheme.getAvailableElements();
				this._areAvailableSchemeElementsChanged = false;
			}
		},
		addSchemeElementAt: function(schemeElement, index)
		{
			if(this._config)
			{
				this._config.addSchemeElementAt(schemeElement, index);
			}
		},
		updateSchemeElement: function(schemeElement)
		{
			if(this._config)
			{
				this._config.updateSchemeElement(schemeElement);
			}
		},
		removeSchemeElement: function(schemeElement)
		{
			if(this._config)
			{
				this._config.removeSchemeElement(schemeElement);
			}
		},
		canChangeScheme: function()
		{
			return this._config && this._config.isChangeable();
		},
		isSchemeChanged: function()
		{
			return this._config && this._config.isChanged();
		},
		saveScheme: function()
		{
			if(!this._config)
			{
				return false;
			}

			var result = this._config.save(false);
			if (result)
			{
				this._areAvailableSchemeElementsChanged = false;
				this.processSchemeChange();

				BX.onCustomEvent(
					this,
					this.eventsNamespace + ":onSchemeSave",
					[ this, { params: {} } ]
				);
			}
			return result;
		},
		saveSchemeChanges: function()
		{
			this.commitSchemeChanges();
			return this.saveScheme();
		},
		commitSchemeChanges: function()
		{
			for(var i = 0, length = this._controls.length; i < length; i++)
			{
				this._controls[i].commitSchemeChanges();
			}

			if(this._areAvailableSchemeElementsChanged)
			{
				this._scheme.setAvailableElements(this._availableSchemeElements);
				this._areAvailableSchemeElementsChanged = false;
			}
		},
		canChangeCommonConfiguration: function()
		{
			return this._config.isCanChangeCommonConfiguration();
		},
		registerSaveAnalyticsEvent: function(status)
		{
			const analyticsConfig = BX.prop.getObject(this._settings, 'analyticsConfig', {});

			let analyticsData = BX.prop.getObject(analyticsConfig, 'data', null);
			if (!BX.Type.isPlainObject(analyticsData))
			{
				return;
			}

			analyticsData.status = status;

			if (BX.prop.getBoolean(analyticsConfig, 'appendParamsFromCurrentUrl', false))
			{
				const currentUrl = new BX.Uri(decodeURI(window.location.href));

				analyticsData = Object.assign(
					currentUrl.getQueryParam('st') || {},
					analyticsData,
				);
			}

			BX.UI.Analytics.sendData(analyticsData);
		},
		onSaveSuccess: function(result, params)
		{
			this._isRequestRunning = false;

			if(this._toolPanel)
			{
				this._toolPanel.setLocked(false);
				this._toolPanel.clearErrors();
			}

			if (result.ADDITIONAL_FIELDS_DATA)
			{
				this.additionalFieldsData = BX.prop.getObject(result, 'ADDITIONAL_FIELDS_DATA', {});
			}

			const eventParams = this.prepareEventParams(result);

			var checkErrors = BX.prop.getObject(result, "CHECK_ERRORS", null);
			var error = BX.prop.getString(result, "ERROR", "");
			var hasRestriction = BX.prop.getBoolean(result, 'RESTRICTION', false);
			if(checkErrors || error !== "" || hasRestriction)
			{
				this.registerSaveAnalyticsEvent('error');

				if(checkErrors)
				{
					var firstField = null;
					var errorMessages = [];
					for(var fieldId in checkErrors)
					{
						if(!checkErrors.hasOwnProperty(fieldId))
						{
							return;
						}

						var field = this.getActiveControlById(fieldId, true);
						if(field)
						{
							field.showError(checkErrors[fieldId]);
							if(!firstField)
							{
								firstField = field;
							}
						}
						else
						{
							errorMessages.push(checkErrors[fieldId]);
						}
					}

					if(firstField)
					{
						firstField.scrollAnimate();
					}

					error = errorMessages.join("<br/>");
				}

				var restrictionAction = BX.prop.getString(result, "RESTRICTION_ACTION", "");
				if (hasRestriction && restrictionAction.length)
				{
					eval(restrictionAction);
					BX.onCustomEvent(window, this.eventsNamespace + ":onRestrictionAction", []);
				}
				else
				{
					if (error !== "" && this._toolPanel)
					{
						this._toolPanel.addError(error);
					}

					eventParams["checkErrors"] = checkErrors;
					eventParams["error"] = error;

					if (this._isNew)
					{
						BX.onCustomEvent(window, this.getGlobalEventName("onEntityCreateError"), [eventParams]);
					}
					else
					{
						eventParams["entityId"] = this._entityId;
						BX.onCustomEvent(window, this.getGlobalEventName("onEntityUpdateError"), [eventParams]);
					}
				}

				this.releaseAjaxForm();
				this.initializeAjaxForm();

				return;
			}

			var entityData = BX.prop.getObject(result, "ENTITY_DATA", null);
			eventParams["entityData"] = entityData;

			if(!this._model.isIdentifiable())
			{
				//fire onEntityUpdate
				eventParams["sender"] = this;
				BX.onCustomEvent(window, this.getGlobalEventName("onEntityUpdate"), [eventParams]);
			}
			else
			{
				if(this._isNew)
				{
					this._entityId = BX.prop.getInteger(result, "ENTITY_ID", 0);
					if(this._entityId <= 0)
					{
						if(this._toolPanel)
						{
							let message = this.getMessage('couldNotFindEntityIdError');
							if (message === 'couldNotFindEntityIdError')
							{
								message = BX.message("UI_ENTITY_EDITOR_COULD_NOT_FIND_ENTITY_ID");
							}

							this._toolPanel.addError(message);
						}

						this.registerSaveAnalyticsEvent('error');

						return;
					}

					//fire onEntityCreate
					eventParams["sender"] = this;
					eventParams["entityId"] = this._entityId;
					BX.onCustomEvent(window, this.getGlobalEventName("onEntityCreate"), [eventParams]);

					if(BX.prop.getBoolean(eventParams, "isCancelled", true))
					{
						this.registerSaveAnalyticsEvent('success');

						this._entityId = 0;

						this.rollback();

						this.releaseAjaxForm();
						this.initializeAjaxForm();

						return;
					}

					this._isNew = false;
				}
				else
				{
					//fire onEntityUpdate
					eventParams["sender"] = this;
					eventParams["entityId"] = this._entityId;
					BX.onCustomEvent(window, this.getGlobalEventName("onEntityUpdate"), [eventParams]);

					if(BX.prop.getBoolean(eventParams, "isCancelled", true))
					{
						this.registerSaveAnalyticsEvent('success');

						this.rollback();

						this.releaseAjaxForm();
						this.initializeAjaxForm();

						return;
					}
				}
			}

			const redirectUrl = BX.prop.getString(result, 'REDIRECT_URL', '');
			const isOpenInNewSlide = BX.prop.getBoolean(result, 'OPEN_IN_NEW_SLIDE', false);

			const additionalEventParams = BX.prop.getObject(result, 'EVENT_PARAMS', null);
			if (additionalEventParams)
			{
				const eventName = BX.prop.getString(additionalEventParams, 'name', '');
				const eventArgs = BX.prop.getObject(additionalEventParams, 'args', null);

				if (BX.Type.isStringFilled(eventName) && eventArgs !== null)
				{
					if (BX.Type.isStringFilled(redirectUrl))
					{
						eventArgs.redirectUrl = redirectUrl;
					}
					BX.localStorage.set(eventName, eventArgs, 10);
				}
			}

			this.registerSaveAnalyticsEvent('success');

			if (this._isReleased)
			{
				return;
			}

			if (BX.Type.isStringFilled(redirectUrl))
			{
				// postpone redirect to next event loop cycle, so that analytics request is sent before this page closes
				setTimeout(() => {
					eventParams.redirectUrl = redirectUrl;
					BX.onCustomEvent(window, this.getGlobalEventName('beforeEntityRedirect'), [eventParams]);

					const url = BX.util.add_url_param(
						redirectUrl,
						{
							IFRAME: 'Y',
							IFRAME_TYPE: 'SIDE_SLIDER',
						},
					);

					const sidePanel = window.top.BX.SidePanel ? window.top.BX.SidePanel.Instance : null;
					if (isOpenInNewSlide && sidePanel && sidePanel.isOpen())
					{
						sidePanel.close(false, () => sidePanel.open(url));
					}
					else
					{
						window.location.replace(url);
					}
				});
			}
			else
			{
				if (BX.prop.getBoolean(params, "switchMode", true))
				{
					if (BX.type.isPlainObject(entityData))
					{
						// Notification event is disabled because we will
						// call "refreshLayout" for all controls at the end.
						this._model.setData(entityData, { enableNotification: false });
					}

					this.adjustTitle();
					this.adjustSize();
					this.releaseAjaxForm();
					this.initializeAjaxForm();

					for (var i = 0, length = this._controllers.length; i < length; i++)
					{
						this._controllers[i].onAfterSave();
					}

					if (this._modeSwitch.isRunning())
					{
						this._modeSwitch.complete();
					}
					else
					{
						this.switchToViewMode({refreshLayout: false});
					}

					this.refreshLayout({reset: true});
					if (!this.isToolPanelAlwaysVisible())
					{
						this.hideToolPanel();
					}
				}
				else if(BX.type.isPlainObject(entityData))
				{
					var previousModel = Object.create(this._model); // clone model object
					previousModel.setData(  // copy model data
						BX.clone(this._model.getData()),
						{
							enableNotification: false
						}
					);

					//Notification event is disabled because we will call "refreshViewModeLayout" for all controls at the end.
					this._model.setData(entityData, {enableNotification: false});

					this.adjustTitle();
					this.adjustSize();

					for(var i = 0, length = this._controllers.length; i < length; i++)
					{
						this._controllers[i].onReload();
					}

					this.refreshViewModeLayout({
						previousModel: previousModel,
						reset: true
					});
				}
			}
		},
		prepareEventParams: function(result)
		{
			var eventParams = BX.prop.getObject(result, "EVENT_PARAMS", {});
			eventParams["entityTypeName"] = this._entityTypeName;
			eventParams["isCancelled"] = false;

			if(typeof(window.top.BX.Bitrix24) !== "undefined")
			{
				var slider = window.top.BX.Bitrix24.Slider.getTopSlider();
				if(slider)
				{
					eventParams["sliderUrl"] = slider.getUrl();
				}
			}

			return eventParams;
		},
		onSaveFailure: function(response)
		{
			this._isRequestRunning = false;

			this.registerSaveAnalyticsEvent('error');

			if(this._toolPanel)
			{
				this._toolPanel.setLocked(false);
				this._toolPanel.clearErrors();
			}

			var errors = BX.prop.getArray(response, "ERRORS", []);
			if(this._toolPanel)
			{
				for(var i = 0, length = errors.length; i < length; i++)
				{
					this._toolPanel.addError(errors[i]);
				}
			}
			var eventParams = {
				errors: errors,
			};

			eventParams["sender"] = this;
			eventParams["entityTypeName"] = this._entityTypeName;
			eventParams["entityId"] = this._entityId;

			BX.onCustomEvent(window, this.eventsNamespace + ':onEntitySaveFailure', [eventParams]);
		},
		onReloadSuccess: function(result)
		{
			var eventParams = BX.prop.getObject(result, "EVENT_PARAMS", {});
			eventParams["entityId"] = this._entityId;
			eventParams["entityTypeName"] = this._entityTypeName;

			var checkErrors = BX.prop.getObject(result, "CHECK_ERRORS", null);
			var error = BX.prop.getString(result, "ERROR", "");
			if(checkErrors || error !== "")
			{
				eventParams["checkErrors"] = checkErrors;
				eventParams["error"] = error;

				BX.onCustomEvent(window, this.eventsNamespace + ":onEntityReloadError", [eventParams]);
				return;
			}
			var entityData = BX.prop.getObject(result, "ENTITY_DATA", null);

			if (result.ADDITIONAL_FIELDS_DATA)
			{
				this.additionalFieldsData = BX.prop.getObject(result, 'ADDITIONAL_FIELDS_DATA', {});
			}

			eventParams["entityData"] = entityData;
			eventParams["sender"] = this;
			eventParams["entityId"] = this._entityId;
			BX.onCustomEvent(window, this.eventsNamespace + ":onEntityReload", [eventParams]);

			if(BX.type.isPlainObject(entityData))
			{
				var previousModel = Object.create(this._model); // clone model object
				previousModel.setData(  // copy model data
					BX.clone(this._model.getData()),
					{
						enableNotification: false
					}
				);

				//Notification event is disabled because we will call "refreshViewModeLayout" for all controls at the end.
				this._model.setData(entityData, {enableNotification: false});

				this.adjustTitle();
				this.adjustSize();

				for(var i = 0, length = this._controllers.length; i < length; i++)
				{
					this._controllers[i].onReload();
				}

				this.refreshViewModeLayout({
					previousModel: previousModel,
					reset: true
				});
			}
		},
		formatMoney: function(sum, currencyId, callback)
		{
			BX.ajax(
				{
					url: BX.prop.getString(this._settings, "serviceUrl", ""),
					method: "POST",
					dataType: "json",
					data:
						{
							"ACTION": "GET_FORMATTED_SUM",
							"CURRENCY_ID": currencyId,
							"SUM": sum
						},
					onsuccess: callback
				}
			);
		},
		findOption: function (value, options)
		{
			for(var i = 0, l = options.length; i < l; i++)
			{
				if(value === options[i].VALUE)
				{
					return options[i].NAME;
				}
			}
			return value;
		},
		prepareConfigMenuItems: function()
		{
			var items = [];
			var callback = BX.delegate(this.onMenuItemClick, this);

			if(this._config.isScopeToggleEnabled())
			{
				var configScope = this._config.getScope();
				items.push(
					{
						id: "switchToPersonalConfig",
						text: BX.message("UI_ENTITY_EDITOR_SWITCH_TO_PERSONAL_CONFIG_MSGVER_1"),
						onclick: callback,
						className: configScope === BX.UI.EntityConfigScope.personal
							? "menu-popup-item-accept" : "menu-popup-item-none"
					}
				);

				items.push(
					{
						id: "switchToCommonConfig",
						text: BX.message("UI_ENTITY_EDITOR_SWITCH_TO_COMMON_CONFIG_MSGVER_1"),
						onclick: callback,
						className: configScope === BX.UI.EntityConfigScope.common
							? "menu-popup-item-accept" : "menu-popup-item-none"
					}
				);
			}

			if (this._config._userScopes)
			{
				for (var userScopeId in this._config._userScopes)
				{
					items.push(
						{
							text: BX.message('UI_ENTITY_EDITOR_CHECK_SCOPE').replace('#SCOPE_NAME#', this._config._userScopes[userScopeId]['NAME']),
							onclick: callback,
							attributes: {
								'data-id': userScopeId
							},
							className:
								(
									this._config.getScope() === BX.UI.EntityConfigScope.custom
									&& this._config._userScopeId === userScopeId
								)
									? "menu-popup-item-accept" : "menu-popup-item-none"

						}
					);
				}
			}

			if(this.canChangeScheme())
			{
				if(this._config.isScopeToggleEnabled())
				{
					items.push({ delimiter: true });
				}

				items.push(
					{
						id: "resetConfig",
						text: BX.message("UI_ENTITY_EDITOR_RESET_CONFIG_MSGVER_1"),
						onclick: callback,
						className: "menu-popup-item-none"
					}
				);

				if(BX.prop.getBoolean(this._settings, "enableSettingsForAll", false))
				{
					items.push(
						{
							id: "forceCommonConfigForAllUsers",
							text: BX.message("UI_ENTITY_EDITOR_FORCE_COMMON_CONFIG_FOR_ALL_MSGVER_1"),
							onclick: callback,
							className: "menu-popup-item-none"
						}
					);
				}

				if(this.moduleId && this.canChangeCommonConfiguration())
				{
					items.push({ delimiter: true });

					items.push(
						{
							id: "createConfigForCheckedUsers",
							text: BX.message('UI_ENTITY_EDITOR_CREATE_SCOPE'),
							onclick: callback,
							className: "menu-popup-item-none"
						}
					);

					items.push(
						{
							id: "editCommonConfig",
							text: BX.message('UI_ENTITY_EDITOR_UPDATE_SCOPE'),
							onclick: callback,
							className: "menu-popup-item-none"
						}
					);
				}
			}

			BX.onCustomEvent(window, this.eventsNamespace + ":onPrepareConfigMenuItems", [ this, items ]);
			return items;
		},
		getServiceUrl: function()
		{
			return this._serviceUrl;
		},
		loadCustomHtml: function(actionName, actionData, callback)
		{
			actionData["ACTION"] = actionName;
			actionData["ACTION_ENTITY_ID"] = this._entityId;
			BX.ajax(
				{
					url: this._serviceUrl,
					method: "POST",
					dataType: "html",
					data: actionData,
					onsuccess: callback
				}
			);
		},
		onFormSubmit: function(sender, eventArgs)
		{
			this._isRequestRunning = true;
			if(this._toolPanel)
			{
				this._toolPanel.setLocked(true);
			}
		},
		//region Events
		onResize: function(e)
		{
			this.adjustSize();
		},
		onPageTileClick: function(e)
		{
			if(this._readOnly)
			{
				return
			}

			if(this.isChanged())
			{
				this.showMessageDialog(
					"titleEditDenied",
					BX.message("UI_ENTITY_EDITOR_TITLE_EDIT"),
					BX.message("UI_ENTITY_EDITOR_TITLE_EDIT_UNSAVED_CHANGES")
				);
				return;
			}

			this.switchTitleMode(BX.UI.EntityEditorMode.edit);
		},
		onCreateSectionButtonClick: function(e)
		{
			if(!this.isSectionCreationEnabled())
			{
				return;
			}

			var index = this.getControlCount();
			var name = "user_" + BX.util.getRandomString(8).toLowerCase();

			var schemeElement = BX.UI.EntitySchemeElement.create(
				{
					type: "section",
					name: name,
					title: BX.message("UI_ENTITY_EDITOR_NEW_SECTION_TITLE")
				}
			);

			var sectionSettings = {
				schemeElement: schemeElement,
				model: this._model,
			};

			var firstColumn = this.getControlByIndex(0);

			if (!firstColumn)
			{
				this.addSchemeElementAt(schemeElement, index);
				sectionSettings.container = this._formElement;
			}

			var control = this.createControl(
				"section",
				name,
				sectionSettings
			);

			if (firstColumn)
			{
				firstColumn.addChild(control,{
					enableSaving: false
				});
			}
			else
			{
				this.addControlAt(control, 0);
				this.saveScheme();
			}

			control.setMode(BX.UI.EntityEditorMode.edit, { notify: false });
			control.refreshLayout();
			control.setTitleMode(BX.UI.EntityEditorMode.edit);
			this.registerActiveControl(control);
		},
		onConfigMenuButtonClick: function(e)
		{
			if(this._isConfigMenuShown)
			{
				return;
			}

			var menuItems = this.prepareConfigMenuItems();
			if(menuItems.length > 0)
			{
				BX.PopupMenu.show(
					this._id + "_config_menu",
					BX.getEventTarget(e),
					menuItems,
					{
						angle: false,
						autoHide: true,
						closeByEsc: true,
						events:
							{
								onPopupShow: function(){ this._isConfigMenuShown = true; }.bind(this),
								onPopupClose: function(){ BX.PopupMenu.destroy(this._id + "_config_menu"); }.bind(this),
								onPopupDestroy: function(){ this._isConfigMenuShown = false; }.bind(this)
							}
					}
				);
			}
		},
		onPageTitleExternalClick: function(e)
		{
			var target = BX.getEventTarget(e);
			if(target !== this._pageTitleInput)
			{
				this.savePageTitle();
				this.switchTitleMode(BX.UI.EntityEditorMode.view);
			}
		},
		onPageTitleKeyPress: function(e)
		{
			var c = e.keyCode;
			if(c === 13)
			{
				this.savePageTitle();
				this.switchTitleMode(BX.UI.EntityEditorMode.view);
			}
			else if(c === 27)
			{
				this.switchTitleMode(BX.UI.EntityEditorMode.view);
			}
		},
		onInterfaceToolbarMenuBuild: function(sender, eventArgs)
		{
			var menuItems = BX.prop.getArray(eventArgs, "items", null);
			if(!menuItems)
			{
				return;
			}

			var configMenuItems = this.prepareConfigMenuItems();
			if(configMenuItems.length > 0)
			{
				if(menuItems.length > 0)
				{
					menuItems.push({ delimiter: true });
				}

				for(var i = 0, length = configMenuItems.length; i < length; i++)
				{
					menuItems.push(configMenuItems[i]);
				}
			}
		},
		//endregion
		//region Configuration
		getCommonConfigEditUrl: function(entityTypeId, moduleId)
		{
			return this._commonConfigEditUrl
				.replace(/#ENTITY_TYPE_ID_VALUE#/gi, entityTypeId)
				.replace(/#MODULE_ID#/gi, moduleId);
		},
		onMenuItemClick: function(event, menuItem)
		{
			var id = BX.prop.getString(menuItem, "id", "");
			switch (id) {
				case 'resetConfig':
					this.resetConfig();
					break;
				case 'switchToPersonalConfig':
					this.setConfigScope(BX.UI.EntityConfigScope.personal);
					break;
				case 'switchToCommonConfig':
					this.setConfigScope(BX.UI.EntityConfigScope.common);
					break;
				case 'forceCommonConfigForAllUsers':
					this.forceCommonConfigScopeForAll();
					break;
				case 'createConfigForCheckedUsers':
					this.createConfigScopeForCheckedUsers();
					break;
				case 'editCommonConfig':
					BX.SidePanel.Instance.open(
						this.getCommonConfigEditUrl(this._config._id, this.moduleId),
						{width: 980}
					);
					break;
				default:
					var attributes = BX.prop.getObject(menuItem, "attributes", "");
					if (attributes['data-id'] !== undefined)
					{
						this.setConfigScope(BX.UI.EntityConfigScope.custom, attributes['data-id']);
					}
			}

			if(menuItem.menuWindow)
			{
				menuItem.menuWindow.close();
			}
		},
		setConfigScope: function(scope, userScopeId)
		{
			if(
				(
					scope === this._config.getScope()
					&& this._config.getScope() !== BX.UI.EntityConfigScope.custom
				)
				||
				(
					scope === BX.UI.EntityConfigScope.custom
					&&
					(userScopeId === undefined || userScopeId === this._config._userScopeId)
				)
			)
			{
				return;
			}

			this._config.setScope(scope, userScopeId, this.moduleId).then(
				function()
				{
					var eventArgs = {
						id: this._id,
						moduleId: this.moduleId,
						scope: scope, userScopeId: userScopeId,
						enableReload: true
					};
					BX.onCustomEvent(window, this.eventsNamespace + ":onConfigScopeChange", [ this, eventArgs ]);

					if(eventArgs["enableReload"] && !this._isEmbedded)
					{
						window.location.reload(true);
					}
				}.bind(this)
			);
		},
		createConfigScopeForCheckedUsers: function()
		{
			var config = BX.UI.EntityEditorScopeConfig.create(
				this._id+'_config', {
					editor: this,
					config: this._config.toJSON(),
					entityTypeId: this._config._id,
					isCommonConfig: true,
					moduleId: this.moduleId
				});
			config.open();
		},
		forceCommonConfigScopeForAll: function()
		{
			this._config.forceCommonScopeForAll().then(
				function()
				{
					var scope = this._config.getScope();
					var eventArgs = { id: this._id, scope: scope, enableReload: true };
					BX.onCustomEvent(window, this.eventsNamespace + ":onForceCommonConfigScopeForAll", [ this, eventArgs ]);

					if(eventArgs["enableReload"] && !this._isEmbedded && scope !== BX.UI.EntityConfigScope.common)
					{
						window.location.reload(true);
					}
				}.bind(this)
			);
		},
		resetConfig: function()
		{
			this._config.reset(false).then(
				function()
				{
					var scope = this._config.getScope();
					var eventArgs = { id: this._id, scope: scope, enableReload: true };
					BX.onCustomEvent(window, this.eventsNamespace + ":onConfigReset", [ this, eventArgs ]);

					if(eventArgs["enableReload"] && !this._isEmbedded)
					{
						window.location.reload(true);
					}
				}.bind(this)
			);
		},
		getConfigOption: function(name, defaultValue)
		{
			return this._config.getOption(name, defaultValue);
		},
		setConfigOption: function(name, value)
		{
			return this._config.setOption(name, value);
		},
		//endregion
		//region Options
		getOption: function(name, defaultValue)
		{
			return BX.prop.getString(this._settings["options"], name, defaultValue);
		},
		setOption: function(name, value)
		{
			if(typeof(value) === "undefined" || value === null)
			{
				return;
			}

			if(BX.prop.getString(this._settings["options"], name, null) === value)
			{
				return;
			}

			this._settings["options"][name] = value;
		},
		//endregion
		//region D&D
		// D&D for sections moved from root editor entity to column entities
		getDragConfig: function(typeId)
		{
			return BX.prop.getObject(this._dragConfig, typeId, {});
		},
		hasPlaceHolder: function()
		{
			return !!this._dragPlaceHolder;
		},
		createPlaceHolder: function(index)
		{
			var qty = this.getControlCount();
			if(index < 0 || index > qty)
			{
				index = qty > 0 ? qty : 0;
			}

			if(this._dragPlaceHolder)
			{
				if(this._dragPlaceHolder.getIndex() === index)
				{
					return this._dragPlaceHolder;
				}

				this._dragPlaceHolder.clearLayout();
				this._dragPlaceHolder = null;
			}

			this._dragPlaceHolder = BX.UI.EditorDragSectionPlaceholder.create(
				{
					container: this._formElement,
					anchor: (index < qty) ? this._controls[index].getWrapper() : null,
					index: index
				}
			);

			this._dragPlaceHolder.layout();
			return this._dragPlaceHolder;
		},
		getPlaceHolder: function()
		{
			return this._dragPlaceHolder;
		},
		removePlaceHolder: function()
		{
			if(this._dragPlaceHolder)
			{
				this._dragPlaceHolder.clearLayout();
				this._dragPlaceHolder = null;
			}
		},
		processDraggedItemDrop: function(dragContainer, draggedItem)
		{
			var containerCharge = dragContainer.getCharge();
			if(!((containerCharge instanceof BX.UI.EditorSectionDragContainer) && containerCharge.getEditor() === this))
			{
				return;
			}

			var context = draggedItem.getContextData();
			var contextId = BX.type.isNotEmptyString(context["contextId"]) ? context["contextId"] : "";
			if(contextId !== BX.UI.EditorSectionDragItem.contextId)
			{
				return;
			}

			var itemCharge = typeof(context["charge"]) !== "undefined" ?  context["charge"] : null;
			if(!(itemCharge instanceof BX.UI.EditorSectionDragItem))
			{
				return;
			}

			var control = itemCharge.getControl();
			if(!control)
			{
				return;
			}

			var currentIndex = this.getControlIndex(control);
			if(currentIndex < 0)
			{
				return;
			}

			var placeholder = this.getPlaceHolder();
			var placeholderIndex = placeholder ? placeholder.getIndex() : -1;
			if(placeholderIndex < 0)
			{
				return;
			}

			var index = placeholderIndex <= currentIndex ? placeholderIndex : (placeholderIndex - 1);
			if(index !== currentIndex)
			{
				this.moveControl(control, index);
				this.saveScheme();
			}
		},
		onDrop: function(event)
		{
			this.processDraggedItemDrop(event.data["dropContainer"], event.data["draggedItem"]);
		},
		//endregion
		getConfigScope: function()
		{
			return this._config.getScope();
		},
		prepareFieldLayoutOptions: function(field)
		{
			var hasContent = field.hasContentToDisplay();
			var result = { isNeedToDisplay: (hasContent || this._showEmptyFields) };
			if(this.isExternalLayoutResolversEnabled())
			{
				var eventArgs =
					{
						id: this._id,
						field: field,
						hasContent: hasContent,
						showEmptyFields: this._showEmptyFields,
						layoutOptions: result
					};

				BX.onCustomEvent(
					window,
					this.eventsNamespace + ":onResolveFieldLayoutOptions",
					[ this, eventArgs ]
				);
			}
			return result;
		},
		isExternalLayoutResolversEnabled: function()
		{
			return !!this._enableExternalLayoutResolvers;
		},
		getRestriction: function(id)
		{
			return BX.prop.getObject(this._restrictions, id, null);
		}
	};
	BX.UI.EntityEditor.defaultInstance = null;
	BX.UI.EntityEditor.items = {};
	BX.UI.EntityEditor.get = function(id)
	{
		return this.items.hasOwnProperty(id) ? this.items[id] : null;
	};
	if(typeof(BX.UI.EntityEditor.messages) === "undefined")
	{
		BX.UI.EntityEditor.messages = {};
	}
	BX.UI.EntityEditor.setDefault = function(instance)
	{
		BX.UI.EntityEditor.defaultInstance = instance;
	};
	BX.UI.EntityEditor.getDefault = function()
	{
		return BX.UI.EntityEditor.defaultInstance;
	};
	BX.UI.EntityEditor.create = function(id, settings)
	{
		var self = new BX.UI.EntityEditor();
		self.initialize(id, settings);
		this.items[self.getId()] = self;
		return self;
	};
}
//endregion

//region ENTITY EDITOR MODE QUEUE
if(typeof BX.UI.EntityEditorModeQueue === "undefined")
{
	BX.UI.EntityEditorModeQueue = function()
	{
		this._id = "";
		this._settings = {};
		this._items = [];
	};
	BX.UI.EntityEditorModeQueue.prototype =
		{
			initialize: function(id, settings)
			{
				this._id = BX.type.isNotEmptyString(id) ? id : BX.util.getRandomString(4);
				this._settings = settings ? settings : {};
			},
			findIndex: function(control)
			{
				for(var i = 0, length = this._items.length; i < length; i++)
				{
					if(this._items[i]["control"] === control)
					{
						return i;
					}
				}
				return -1;
			},
			getLength: function ()
			{
				return this._items.length;
			},
			add: function(control, mode, options)
			{
				if(typeof(options) === "undefined")
				{
					options = BX.UI.EntityEditorModeOptions.none;
				}
				var index = this.findIndex(control);
				if(index >= 0)
				{
					this._items[index] = { control: control, mode: mode, options: options };
				}
				else
				{
					this._items.push({ control: control, mode: mode, options: options });
				}
			},
			addBatch: function(controls, mode, options)
			{
				for(var i = 0, length = controls.length; i < length; i++)
				{
					this.add(controls[i], mode, options);
				}
			},
			remove: function(control)
			{
				var index = this.findIndex(control);
				if(index >= 0)
				{
					this._items.splice(index, 1)
				}
			},
			clear: function()
			{
				this._items = [];
			},
			process: function()
			{
				var length = this._items.length;
				if(length === 0)
				{
					return 0;
				}

				for(var i = 0; i < length; i++)
				{
					var item = this._items[i];
					item["control"].setMode(item["mode"], { options: item["options"], notify: true });
				}

				return length;
			}
		};
	BX.UI.EntityEditorModeQueue.create = function(id, settings)
	{
		var self = new BX.UI.EntityEditorModeQueue();
		self.initialize(id, settings);
		return self;
	};
}
//endregion

//region ENTITY EDITOR MODE SWITCH
if(typeof BX.UI.EntityEditorModeSwitch === "undefined")
{
	BX.UI.EntityEditorModeSwitch = function()
	{
		this._id = "";
		this._settings = {};
		this._queue = null;
		this._isRunning = false;
		this._runHandle = 0;

		this._runAction = "";
	};
	BX.UI.EntityEditorModeSwitch.prototype =
		{
			initialize: function(id, settings)
			{
				this._id = BX.type.isNotEmptyString(id) ? id : BX.util.getRandomString(4);
				this._settings = settings ? settings : {};
				this._editor = BX.prop.get(this._settings, "editor");
				this._queue = BX.UI.EntityEditorModeQueue.create(this._id, {});
			},
			getQueue: function()
			{
				return this._queue;
			},
			reset: function()
			{
				this._queue.clear();
				this._isRunning = false;
			},
			isRunning: function()
			{
				return this._isRunning;
			},
			setRunAction: function(action)
			{
				this._runAction = action;
			},
			run: function()
			{
				if(this._isRunning)
				{
					return;
				}

				if(this._runHandle > 0)
				{
					window.clearTimeout(this._runHandle);
				}
				this._runHandle = window.setTimeout(BX.delegate(this.doRun, this), 50);
			},
			doRun: function()
			{
				this._editor.performSaveDelayedAction(this._runAction);

				this._isRunning = true;
				this._runHandle = 0;
			},
			complete: function ()
			{
				this._queue.process();
				this.reset();
			}
		};
	BX.UI.EntityEditorModeSwitch.create = function(id, settings)
	{
		var self = new BX.UI.EntityEditorModeSwitch();
		self.initialize(id, settings);
		return self;
	};
}
//endregion

//region ENTITY EDITOR MODE
if(typeof(BX.UI.EntityEditorScopeConfig) === "undefined")
{
	BX.UI.EntityEditorScopeConfig = function()
	{
		this._id = "";
		this._settings = {};

		this._editor = {};
		this._config = {};
		this._isCommonConfig = false;

		this._popup = null;
		this._selector = null;

		this._name = "";
		this._items = [];

		this._nameInput = {};
		this._usersInput = {};
		this._usersInputTagSelector = {};
		this._forceSetInput = {};
		this._nameInputError = null;
		this._usersInputError = null;

		this._entityId = "";
		this._entityTypeId = '';

		this._isOpened = false;
		this._closeNotifier = null;

		this.moduleId = null;

		this._onSquareClick = BX.delegate(this.onSquareClick, this);
	};

	BX.UI.EntityEditorScopeConfig.prototype =
		{
			initialize: function(id, settings)
			{
				this._id = BX.type.isNotEmptyString(id) ? id : BX.util.getRandomString(4);
				this._settings = settings ? settings : {};

				this._editor = this.getSetting('editor', {});
				this._config = this.getSetting('config', {});
				this._isCommonConfig = this.getSetting('isCommonConfig', false);

				this._name = this.getSetting('name', '');
				this._items = this.getSetting('items', []);

				this._entityId = this.getSetting('entityId', null);

				this._entityTypeId = this.getSetting('entityTypeId', null);
				this.moduleId = this.getSetting('moduleId', null);
			},
			getId: function()
			{
				return this._id;
			},
			getSetting: function(name, defaultValue)
			{
				return (this._settings.hasOwnProperty(name) ? this._settings[name] : defaultValue);
			},
			isOpened: function()
			{
				return this._isOpened;
			},
			open: function()
			{
				if(this._isOpened)
				{
					return;
				}

				this._popup = this.createPopup();

				this._popup.show();
			},
			createPopup: function()
			{
				return (this._popup || new BX.PopupWindow(this._id, null, {
					className: 'ui-entity-editor-content-user-scope-popup',
					titleBar: BX.message('UI_ENTITY_EDITOR_CREATE_SCOPE'),
					closeIcon : true,
					autoHide: false,
					closeByEsc: true,
					padding: 0,
					contentPadding: 0,
					contentBackground: 'none',
					draggable: true,
					minWidth: 550,
					maxWidth: 550,
					content: this.prepareContent(),
					buttons: this.prepareButtons(),
					events:
						{
							onPopupShow: BX.delegate(this.onPopupShow, this),
							onPopupClose: BX.delegate(this.onPopupClose, this),
							onPopupDestroy: BX.delegate(this.onPopupDestroy, this)
						},
				}));
			},
			prepareContent: function()
			{
				var container = BX.create('div', {
					style: {
						padding: '0 20px',
					}
				});
				container.appendChild(this.prepareNameControl());
				container.appendChild(this.prepareUserSelectControl());
				container.appendChild(this.prepareForceSetToUsersControl());

				return container;
			},
			prepareNameControl: function()
			{
				var container = BX.create('div', {
					style: {
						paddingBottom: '25px',
						marginBottom: '20px',
						borderBottom: '1px solid #f2f2f4'
					}
				});
				container.appendChild(BX.create('div', {
					props: {
						className: 'ui-ctl-label-text'
					},
					text: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_NAME')
				}));

				var control = BX.create('div', {
					props:{
						className: 'ui-ctl ui-ctl-textbox ui-ctl-w100'
					}
				});

				this._nameInput = BX.create("input", {
					props:{
						className: 'ui-ctl-element',
						value: this.getName(),
						type: 'text',
						placeholder: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_NAME_PLACEHOLDER')
					}
				});

				control.appendChild(this._nameInput);
				container.appendChild(control);
				return container;
			},
			prepareUserSelectControl: function()
			{
				var container = BX.create('div', {
					style: {
						paddingBottom: '25px',
						marginBottom: '10px',
						borderBottom: '1px solid #f2f2f4'
					}
				});
				container.appendChild(BX.create('div', {
					props: {
						className: 'ui-ctl-label-text'
					},
					text: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_MEMBERS')
				}));

				var control = BX.create('div', {
					props:{
						className: 'ui-ctl ui-ctl-textbox ui-ctl-w100'
					}
				});

				this._usersInput = BX.create("div", {
					style:{
						width: '100%'
					},
					attrs: {
						id: 'user-selector-item',
					},
				});

				control.appendChild(this._usersInput);
				container.appendChild(control);

				this._usersInputTagSelector = new BX.UI.EntitySelector.TagSelector({
					dialogOptions: {
						context: 'UI_ENTITY_EDITOR_SCOPE',
						entities: [
							{
								id: 'user',
							},
							{
								id: 'project',
							},
							{
								id: 'department',
								options: {
									selectMode: 'usersAndDepartments'
								}
							},
						],
					}
				});

				this._usersInputTagSelector.renderTo(this._usersInput);

				return container;
			},
			prepareForceSetToUsersControl: function()
			{
				var container = BX.create('div', {
					style: {
						paddingBottom: '10px',
						borderBottom: '1px solid #f2f2f4'
					}
				});

				var control = BX.create('div', {
					props:{
						className: 'ui-ctl ui-ctl-checkbox ui-ctl-w100'
					}
				});

				this._forceSetInput = BX.create("input", {
					props:{
						className: 'ui-ctl-element',
						type: 'checkbox',
						checked: true
					},
				});

				control.appendChild(this._forceSetInput);
				control.appendChild(BX.create('div', {
					props:{
						className: 'ui-ctl-label-text',
					},
					text: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_FORCE_INSTALL_TO_USERS')
				}));

				container.appendChild(control);

				return container;
			},
			prepareButtons: function()
			{
				return [
					new BX.UI.Button({
						text: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_SAVE'),
						tag: BX.UI.Button.Tag.LINK,
						color: BX.UI.Button.Color.PRIMARY,
						events: {
							click: function(params, event) {
								event.preventDefault();
								this.processSave();
							}.bind(this)
						}
					}),
					new BX.UI.Button({
						text: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_CANCEL'),
						tag: BX.UI.Button.Tag.LINK,
						color: BX.UI.Button.Color.LINK,
						events: {
							click: function(params, event) {
								event.preventDefault();
								this.processCancel();
							}.bind(this)
						}
					})
				];
			},
			close: function()
			{
				if (this._popup)
				{
					this._popup.close();
				}
			},
			addCloseListener: function(listener)
			{
				this._closeNotifier.addListener(listener);
			},
			removeCloseListener: function(listener)
			{
				this._closeNotifier.removeListener(listener);
			},
			createUserInfo: function(item)
			{
				return {
					ID: item.id,
					FORMATTED_NAME: BX.util.htmlspecialcharsback(BX.prop.getString(item, 'name', '')),
				};
			},
			isCustomized: function()
			{
				var accessCodes = BX.prop.getObject(this._config, 'accessCodes', []);
				return !!Object.keys(accessCodes).length;
			},
			getName: function()
			{
				return this._name;
			},
			setName: function(name)
			{
				this._name = name;
			},
			processSave: function()
			{
				this.clearErrors();
				this.setName(this._nameInput.value);

				BX.ajax.runComponentAction(
					'bitrix:ui.form.config',
					'save',
					{
						data: {
							moduleId: this.moduleId,
							entityTypeId: this._entityTypeId,
							name: this.getName(),
							accessCodes: this.getSelectedItems(),
							config: this._config,
							params: {
								forceSetToUsers: this._forceSetInput.checked,
								categoryName: this._editor._config.categoryName,
								common: 'Y',
							}
						}
					}
				)
				.then(
					function(response) {
						this.close();
						BX.UI.EntityEditorScopeConfig.prototype.notifyShow(response);
						var scopeId = parseInt(response.data, 10);
						this._editor.setConfigScope(BX.UI.EntityConfigScope.custom, scopeId);
					}.bind(this)
				).catch(function(response){
					//todo show errors some other way
					this.fillErrors(response.data);
				}.bind(this));
			},
			/**
			 *
			 * @returns {{entityType: string, id: string|number}[]}
			 */
			getSelectedItems: function()
			{
				var items = this._usersInputTagSelector.getTags();
				return items.map(function(item){
					return {
						id: item.id,
						entityId: item.entityId,
					}
				});
			},
			fillErrors: function(errors)
			{
				if (errors.name)
				{
					this._nameInputError = this.createErrorElement(this._nameInput, errors.name.message);
				}
				if (errors.accessCodes)
				{
					this._usersInputError = this.createErrorElement(this._usersInput, errors.accessCodes.message);
				}
			},
			createErrorElement: function(fieldNode, message)
			{
				var errorContainer = BX.create('div', {
					props: {
						className: 'ui-entity-section-control-error-text'
					}
				});
				errorContainer.innerHTML = message;
				fieldNode.parentNode.parentNode.appendChild(errorContainer);

				return errorContainer;
			},
			clearErrors: function()
			{
				if (this._nameInputError)
				{
					this._nameInputError.remove();
				}
				if (this._usersInputError)
				{
					this._usersInputError.remove();
				}
			},
			notifyShow: function(response)
			{
				window.top.BX.UI.Notification.Center.notify({
					content: BX.message('UI_ENTITY_EDITOR_CONFIG_SCOPE_SAVED'),
					width: 'auto',
				});
			},
			processCancel: function()
			{
				this.close();
			},
			onPopupShow: function()
			{
				this._isOpened = true;
			},
			onPopupClose: function()
			{
				if(this._popup)
				{
					this._popup.destroy();
				}
			},
			onPopupDestroy: function()
			{
				this._isOpened = false;
				this._popup = null;
			},
		};

	if(BX.UI.EntityEditorScopeConfig.messages === undefined)
	{
		BX.UI.EntityEditorScopeConfig.messages = {};
	}

	BX.UI.EntityEditorScopeConfig.create = function(id, settings)
	{
		var self = new BX.UI.EntityEditorScopeConfig();
		self.initialize(id, settings);
		return self;
	};
}
//endregion