AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 3.144.30.196
Web Server : Apache
System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64
User : bitrix ( 600)
PHP Version : 8.1.27
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/bitrix/www/bitrix/js/calendar/userfield/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/bitrix/www/bitrix/js/calendar/userfield/resourcebooking-webform-field.js
;(function()
{
	'use strict';
	BX.namespace('BX.Calendar.UserField');

	/**
	 * Controller used to create and manage instances of resourcebooking user fields during filling of forms
	 *
	 * @constructor
	 * @this  {CrmFormResourceBookingFieldLiveController}
	 * @param {array} params - incoming data params
	 * @param {DOM} params.wrap - DOM node, wrapper of control in the form.
	 */
	// region *CrmFormResourceBookingFieldLiveController*
	function CrmFormResourceBookingFieldLiveController(params)
	{
		this.params = params;

	}
	BX.Calendar.UserField.CrmFormResourceBookingFieldLiveController = CrmFormResourceBookingFieldLiveController;
	CrmFormResourceBookingFieldLiveController.prototype = {
		init: function()
		{
			this.previewFieldLayout = new ResourceBookingFieldLiveViewLayout({
				wrap: this.params.wrap,
				title: this.getCaption(),
				settings: this.getSettings()
			});
			this.previewFieldLayout.build();
		},

		getCaption: function()
		{
			return this.params.field.caption;
		},

		getSettings: function()
		{
			return {
				caption: this.getCaption(),
				data: this.params.field.settings_data,
				userfieldSettings: {
					useUsers: true,
					useResources: true
				}
			};
		}
	};
	// endregion

	// region *ResourceBookingFieldLayoutAbstract*
	function ResourceBookingFieldLayoutAbstract(config)
	{
		this.settings = config.settings || {};
		this.showTitle = config.displayTitle !== false;
		this.title = config.title || '';
		this.DOM = {
			wrap: config.wrap // outer wrap of the form
		};
	}
	BX.Calendar.UserField.ResourceBookingFieldLayoutAbstract = ResourceBookingFieldLayoutAbstract;

	ResourceBookingFieldLayoutAbstract.prototype = {
		build: function()
		{
			this.controls = {};
			// inner wrap
			this.DOM.outerWrap = this.DOM.wrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-wrapper calendar-resbook-webform-wrapper-form'}}));
			this.DOM.innerWrap = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-inner'}}));

			if (this.settings.userfieldSettings.useUsers || this.settings.userfieldSettings.useResources)
			{
				this.displayTitle();
				this.displayUsersControl();
				this.displayResourcesControl();
				this.displayServicesControl();
				this.displayDurationControl();
				this.displayDateControl();
				this.displayTimeControl();
			}
			else
			{
				this.displayWarning(BX.message('WEBF_RES_BOOKING_WARNING'));
			}
		},

		destroy: function()
		{
			BX.remove(this.DOM.outerWrap);
		},

		displayTitle: function()
		{
			if (this.showTitle)
			{
				this.DOM.titleWrap = this.DOM.innerWrap
					.appendChild(BX.create("div", {props:{className:'calendar-resbook-webform-title'}}))
					.appendChild(BX.create("div", {props:{className:'calendar-resbook-webform-title-text'}}));
				this.updateTitle(this.title);
			}
		},

		updateTitle: function(title)
		{
			if (this.showTitle)
			{
				this.title = title;
				BX.adjust(this.DOM.titleWrap, {text: this.title});
			}
		},

		displayWarning: function(message)
		{
			this.DOM.warningWrap = this.DOM.innerWrap
				.appendChild(BX.create("div", {
					props:{className:'ui-alert ui-alert-warning ui-alert-text-center ui-alert-icon-warning'},
					style: {marginBottom: 0},
					html: '<span class="ui-alert-message">' + message + '</span>'
				}));
		},

		displayUsersControl: function()
		{
			if (this.settings.userfieldSettings.useUsers)
			{
				if (this.settings.data.users.value === null
					&& BX.type.isArray(this.settings.userfieldSettings.users))
				{
					this.settings.data.users.value = this.settings.userfieldSettings.users;
				}

				this.controls.users = new ViewFormUsersControl({
					outerWrap: this.DOM.innerWrap,
					data: this.settings.data.users,
					userIndex: this.settings.userfieldSettings.userIndex
				});
				this.controls.users.display();
			}
		},

		displayResourcesControl: function()
		{
			if (this.settings.userfieldSettings.useResources)
			{
				if (this.settings.data.resources.value === null
					&& BX.type.isArray(this.settings.userfieldSettings.resources))
				{
					this.settings.data.resources.value = [];
					this.settings.userfieldSettings.resources.forEach(function(res)
					{
						this.settings.data.resources.value.push(parseInt(res.id));
					}, this);
				}

				this.controls.resources = new ViewFormResourcesControl({
					outerWrap: this.DOM.innerWrap,
					data: this.settings.data.resources,
					resourceList: this.settings.userfieldSettings.resources
				});
				this.controls.resources.display();
			}
		},

		displayServicesControl: function()
		{
			if (this.settings.userfieldSettings.useServices)
			{
				if (this.settings.data.services.value === null
					&& BX.type.isArray(this.settings.userfieldSettings.services))
				{
					this.settings.data.services.value = [];
					this.settings.userfieldSettings.services.forEach(function(serv)
					{
						this.settings.data.services.value.push(serv.name);
					}, this);
				}

				this.controls.services = new ViewFormServicesControl({
					outerWrap: this.DOM.innerWrap,
					data: this.settings.data.services,
					serviceList: this.settings.userfieldSettings.services
				});
				this.controls.services.display();
			}
		},

		displayDurationControl: function()
		{
			if (!this.settings.userfieldSettings.useServices)
			{
				this.controls.duration = new ViewFormDurationControl({
					outerWrap: this.DOM.innerWrap,
					data: this.settings.data.duration,
					fullDay: this.settings.userfieldSettings.fullDay
				});
				this.controls.duration.display();
			}
		},

		displayDateControl: function()
		{
			this.controls.date = new DateSelector({
				outerWrap: this.DOM.innerWrap,
				data: this.settings.data.date
			});
			this.controls.date.display();
		},

		displayTimeControl: function()
		{
			if (!this.settings.userfieldSettings.fullDay)
			{
				this.controls.time = new TimeSelector({
					outerWrap: this.DOM.innerWrap,
					data: this.settings.data.time
				});
				this.controls.time.display();
			}
		},

		refreshLayout: function(settingsData)
		{
			for (var k in this.controls)
			{
				if (this.controls.hasOwnProperty(k) && BX.type.isFunction(this.controls[k].refresh))
				{
					this.controls[k].refresh(settingsData[k] || this.settings.data[k]);
				}
			}
		},

		getInnerWrap: function()
		{
			return this.DOM.innerWrap;
		},

		getOuterWrap: function()
		{
			return this.DOM.outerWrap;
		}
	};
	//endregion

	function ResourceBookingFieldViewLayout()
	{
		ResourceBookingFieldViewLayout.superclass.constructor.apply(this, arguments);
	}

	BX.Calendar.UserField.ResourceBookingFieldViewLayout = ResourceBookingFieldViewLayout;
	BX.extend(ResourceBookingFieldViewLayout, ResourceBookingFieldLayoutAbstract);


	function ResourceBookingFieldPreviewLayout()
	{
		ResourceBookingFieldPreviewLayout.superclass.constructor.apply(this, arguments);
	}
	BX.Calendar.UserField.ResourceBookingFieldPreviewLayout = ResourceBookingFieldPreviewLayout;
	BX.extend(ResourceBookingFieldPreviewLayout, ResourceBookingFieldLayoutAbstract);

	ResourceBookingFieldPreviewLayout.prototype.build = function()
	{
		ResourceBookingFieldViewLayout.superclass.build.apply(this);
		this.DOM.outerWrap.className = 'calendar-resbook-webform-wrapper calendar-resbook-webform-wrapper-preview calendar-resbook-webform-wrapper-dark';
	};


	function ResourceBookingFieldLiveViewLayout()
	{
		ResourceBookingFieldLiveViewLayout.superclass.constructor.apply(this, arguments);
	}
	BX.extend(ResourceBookingFieldLiveViewLayout, ResourceBookingFieldLayoutAbstract);

	ResourceBookingFieldLiveViewLayout.prototype.build = function()
	{
		ResourceBookingFieldLiveViewLayout.superclass.build.apply(this);
		this.DOM.outerWrap.className = 'calendar-resbook-webform-wrapper';
	};

	// region *+ResourceBookingViewControlAbstract*
	function ResourceBookingViewControlAbstract(params)
	{
		this.name = null;
		this.classNames = {
			wrap: params.wrapClassName || 'calendar-resbook-webform-block',
			innerWrap: 'calendar-resbook-webform-block-inner',
			title: 'calendar-resbook-webform-block-title',
			field: 'calendar-resbook-webform-block-field'
		};

		this.DOM = {
			outerWrap: params.outerWrap,
			wrap: null,
			dataWrap: null,
			innerWrap: null,
			labelWrap: null
		};
		this.data = params.data;
		this.shown = false;
	}

	ResourceBookingViewControlAbstract.prototype = {
		isDisplayed: function()
		{
			return this.data.show !== 'N';
		},

		isShown: function()
		{
			return this.shown;
		},

		display: function()
		{
			this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : this.classNames.wrap}}));

			this.DOM.dataWrap = this.DOM.wrap.appendChild(BX.create("div", {attrs : {'data-bx-resource-data-wrap' : 'Y'}}));

			if (this.isDisplayed())
			{
				this.show({animation: false});
			}
		},

		refresh: function(data)
		{
			this.refreshLabel(data);
			this.data = data;

			if (this.setDataConfig())
			{
				if (this.isDisplayed())
				{
					this.show({animation: true});
				}
				else
				{
					this.hide({animation: true});
				}
			}
			this.data = data;
		},

		setDataConfig: function()
		{
			return true;
		},

		refreshLabel: function(data)
		{
			if (this.data.label !== data.label)
			{
				BX.adjust(this.DOM.labelWrap, {text: data.label});
			}
		},

		show: function()
		{
			if (this.DOM.innerWrap)
			{
				this.hide();
			}

			this.DOM.innerWrap = this.DOM.wrap.appendChild(BX.create("div", {props : { className : this.classNames.innerWrap}}));

			if (this.data.label || this.label)
			{
				this.DOM.labelWrap = this.DOM.innerWrap.appendChild(BX.create("div", {props : { className : this.classNames.title}, text: this.data.label || this.label}));
			}
			this.DOM.controlWrap = this.DOM.innerWrap.appendChild(BX.create("div", {props : { className : this.classNames.field}}));
			this.displayControl();
			this.shown = true;
		},

		hide: function()
		{
			BX.remove(this.DOM.innerWrap);
			this.DOM.innerWrap = null;
			this.shown = false;
		},

		displayControl: function()
		{
		},

		showWarning: function(errorMessage)
		{
			if (this.shown && this.DOM.wrap && this.DOM.innerWrap)
			{
				BX.addClass(this.DOM.wrap, "calendar-resbook-webform-block-error");
				this.displayErrorText(errorMessage || BX.message('WEBF_RES_BOOKING_REQUIRED_WARNING'));
			}
		},

		hideWarning: function()
		{
			if (this.DOM.wrap)
			{
				BX.removeClass(this.DOM.wrap, "calendar-resbook-webform-block-error");
				if (this.DOM.errorTextWrap)
				{
					BX.remove(this.DOM.errorTextWrap);
				}
			}
		},

		displayErrorText: function(errorMessage)
		{
			if (this.DOM.errorTextWrap)
			{
				BX.remove(this.DOM.errorTextWrap);
			}
			this.DOM.errorTextWrap = this.DOM.innerWrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-error-text'}, text: errorMessage}));
		}
	};
	//endregion

	// region '+UserSelector'
	function ViewFormUsersControl(params)
	{
		ViewFormUsersControl.superclass.constructor.apply(this, arguments);
		this.name = 'ViewFormUsersControl';
		this.data = params.data;
		this.userList = [];
		this.userIndex = {};

		this.values = [];
		this.defaultMode = 'auto';
		this.previewMode = params.previewMode === undefined;
		this.autoSelectDefaultValue = params.autoSelectDefaultValue;
		this.changeValueCallback = params.changeValueCallback;

		this.handleSettingsData(this.data, params.userIndex);
	}
	BX.extend(ViewFormUsersControl, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.ViewFormUsersControl = ViewFormUsersControl;

	ViewFormUsersControl.prototype.displayControl = function()
	{
		this.selectedValue = this.getSelectedUser();

		this.dropdownSelect = new ViewFormDropDownSelect({
			wrap: this.DOM.controlWrap,
			values: this.userList,
			selected: this.selectedValue,
			handleChangesCallback: BX.proxy(this.handleChanges, this)
		});
		this.dropdownSelect.build();
	};

	ViewFormUsersControl.prototype.refresh = function(data, userIndex)
	{
		this.refreshLabel(data);
		this.data = data;
		this.handleSettingsData(this.data, userIndex);
		this.selectedValue = this.getSelectedUser();

		if (this.dropdownSelect)
		{
			this.dropdownSelect.setSettings({
				values: this.userList,
				selected: this.selectedValue
			});
		}

		if (this.setDataConfig())
		{
			if (this.isDisplayed())
			{
				this.show({animation: true});
			}
			else
			{
				this.hide({animation: true});
			}
		}
	};

	ViewFormUsersControl.prototype.handleSettingsData = function(data, userIndex)
	{
		if (BX.type.isPlainObject(userIndex))
		{
			for (var id in userIndex)
			{
				if (userIndex.hasOwnProperty(id))
				{
					this.userIndex[id] = userIndex[id];
				}
			}
		}

		this.defaultMode = this.data.defaultMode === 'none' ? 'none' : 'auto';
		var dataValue = [];
		this.userList = [];
		if (this.data.value)
		{
			var dataValueRaw = BX.type.isArray(this.data.value) ? this.data.value : this.data.value.split('|');
			dataValueRaw.forEach(function(id)
			{
				id = parseInt(id);
				if (id > 0)
				{
					dataValue.push(id);
					if (this.userIndex[id])
					{
						this.userList.push({
							id: id,
							title: this.userIndex[id].displayName
						});
					}
				}
			}, this);
		}
		this.values = dataValue;
	};

	ViewFormUsersControl.prototype.getSelectedUser = function()
	{
		var selected = null;
		if (this.dropdownSelect)
		{
			selected = this.dropdownSelect.getSelectedValues();
			selected = (BX.type.isArray(selected) && selected.length) ? selected[0] : null;
		}

		if (!selected && this.previewMode
			&& this.data.defaultMode === 'auto'
			&& this.userList && this.userList[0])
		{
			selected = this.userList[0].id;
		}

		if (!selected && this.autoSelectDefaultValue)
		{
			selected = this.autoSelectDefaultValue;
		}

		return selected;
	};

	ViewFormUsersControl.prototype.setSelectedUser = function(userId)
	{
		if (this.dropdownSelect)
		{
			this.dropdownSelect.setSelectedValues([userId]);
		}
		else
		{
			this.autoSelectDefaultValue = parseInt(userId);
		}
	};

	ViewFormUsersControl.prototype.handleChanges = function(selectedValues)
	{
		if (!this.previewMode && BX.type.isFunction(this.changeValueCallback))
		{
			this.changeValueCallback(selectedValues[0] || null);
		}
	};
	// endregion


	// region '+ResourceSelector'
	function ViewFormResourcesControl(params)
	{
		ViewFormResourcesControl.superclass.constructor.apply(this, arguments);
		this.name = 'ViewFormResourcesControl';
		this.data = params.data;
		this.allResourceList = params.resourceList;
		this.autoSelectDefaultValue = params.autoSelectDefaultValue;
		this.changeValueCallback = params.changeValueCallback;
		this.handleSettingsData(params.data);
	}
	BX.extend(ViewFormResourcesControl, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.ViewFormResourcesControl = ViewFormResourcesControl;

	ViewFormResourcesControl.prototype.handleSettingsData = function(data)
	{
		if (!BX.type.isArray(data.value))
		{
			var dataValue = [];
			if (data.value)
			{
				data.value.split('|').forEach(function(id)
				{
					if (parseInt(id) > 0)
					{
						dataValue.push(parseInt(id))
					}
				});
			}
			this.data.value = dataValue;
		}

		this.resourceList = [];
		if (BX.type.isArray(this.allResourceList))
		{
			this.allResourceList.forEach(function(item)
			{
				if (BX.util.in_array(parseInt(item.id), this.data.value))
				{
					this.resourceList.push(item);
				}
			}, this);
		}

		this.setSelectedValues(this.getSelectedValues());
	};

	ViewFormResourcesControl.prototype.displayControl = function()
	{
		this.dropdownSelect = new ViewFormDropDownSelect({
			wrap: this.DOM.controlWrap,
			values: this.resourceList,
			selected: this.selectedValues,
			multiple: this.data.multiple === 'Y',
			handleChangesCallback: this.changeValueCallback
		});
		this.dropdownSelect.build();
	};

	ViewFormResourcesControl.prototype.refresh = function(data)
	{
		this.refreshLabel(data);
		this.data = data;
		this.handleSettingsData(this.data);
		this.setSelectedValues(this.getSelectedValues());

		if (this.dropdownSelect)
		{
			this.dropdownSelect.setSettings({
				values: this.resourceList,
				selected: this.selectedValues,
				multiple: this.data.multiple === 'Y'
			});
		}

		if (this.setDataConfig())
		{
			if (this.isDisplayed())
			{
				this.show({animation: true});
			}
			else
			{
				this.hide({animation: true});
			}
		}
	};

	ViewFormResourcesControl.prototype.getSelectedValues = function()
	{
		var selected = null;

		if (this.dropdownSelect)
		{
			selected = this.dropdownSelect.getSelectedValues();
		}

		if (!selected && this.autoSelectDefaultValue)
		{
			selected = [this.autoSelectDefaultValue];
		}

		if (!selected && this.data.defaultMode === 'auto')
		{
			if (this.resourceList && this.resourceList[0])
			{
				selected = [this.resourceList[0].id];
			}
		}

		return selected;
	};
	ViewFormResourcesControl.prototype.setSelectedValues = function(selectedValues)
	{
		this.selectedValues = selectedValues;
	};

	ViewFormResourcesControl.prototype.setSelectedResource = function(id)
	{
		if (this.dropdownSelect)
		{
			this.dropdownSelect.setSelectedValues([id]);
		}
		else
		{
			this.autoSelectDefaultValue = parseInt(id);
			this.selectedValues = [id];
		}
	};
	// endregion


	// region 'ServiceSelector'
	function ViewFormServicesControl(params)
	{
		ViewFormServicesControl.superclass.constructor.apply(this, arguments);
		this.name = 'ViewFormServicesControl';

		this.data = params.data;
		this.serviceList = [];
		this.allServiceList = params.serviceList || [];
		this.values = [];
		this.changeValueCallback = params.changeValueCallback;

		if (params.selectedValue)
		{
			this.setSelectedService(params.selectedValue);
		}

		this.handleSettingsData(this.data);
	}
	BX.extend(ViewFormServicesControl, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.ViewFormServicesControl = ViewFormServicesControl;

	ViewFormServicesControl.prototype.displayControl = function()
	{
		this.dropdownSelect = new ViewFormDropDownSelect({
			wrap: this.DOM.controlWrap,
			values: this.serviceList,
			selected: this.getSelectedService(),
			handleChangesCallback: BX.proxy(function (selectedValues)
			{
				if (selectedValues && selectedValues[0])
				{
					this.setSelectedService(selectedValues[0]);
					this.changeValueCallback();
				}
			}, this)
		});
		this.dropdownSelect.build();
	};

	ViewFormServicesControl.prototype.refresh = function(data)
	{
		this.refreshLabel(data);
		this.data = data;

		this.handleSettingsData(this.data);

		if (this.dropdownSelect)
		{
			this.dropdownSelect.setSettings({
				values: this.serviceList,
				selected: this.getSelectedService()
			});
		}

		if (this.setDataConfig())
		{
			if (this.isDisplayed())
			{
				this.show({animation: true});
			}
			else
			{
				this.hide({animation: true});
			}
		}
	};

	ViewFormServicesControl.prototype.handleSettingsData = function()
	{
		this.serviceIndex = {};
		if (BX.type.isArray(this.allServiceList))
		{
			this.allServiceList.forEach(function(service)
			{
				if (BX.type.isPlainObject(service)
					&& BX.type.isString(service.name)
					&& BX.util.trim(service.name) !== '')
				{
					this.serviceIndex[this.prepareServiceId(service.name)] = service;
				}
			}, this);
		}

		this.serviceList = [];
		if (this.data.value)
		{
			var dataValueRaw = BX.type.isArray(this.data.value) ? this.data.value : this.data.value.split('|');
			dataValueRaw.forEach(function(id)
			{
				var service = this.serviceIndex[this.prepareServiceId(id)];
				if (BX.type.isPlainObject(service)
					&& BX.type.isString(service.name)
					&& BX.util.trim(service.name) !== '')
				{
					this.serviceList.push({
						id: this.prepareServiceId(service.name),
						title: service.name + ' - ' + BX.Calendar.UserField.ResourceBooking.getDurationLabel(service.duration)
					});
				}
			}, this);
		}
	};

	ViewFormServicesControl.prototype.setSelectedService = function(serviceName)
	{
		this.selectedService = serviceName;
	};
	ViewFormServicesControl.prototype.getSelectedService = function(getMeta)
	{
		return getMeta !== true ? (this.selectedService || null) : (this.serviceIndex[this.prepareServiceId(this.selectedService)] || null);
	};

	ViewFormServicesControl.prototype.prepareServiceId = function(str)
	{
		return BX.type.isString(str) ? BX.translit(str).replace(/[^a-z0-9_]/ig, "_") : str;
	};
	//endregion

	// region 'DurationSelector'
	function ViewFormDurationControl(params)
	{
		ViewFormDurationControl.superclass.constructor.apply(this, arguments);
		this.name = 'ViewFormDurationControl';
		this.data = params.data;

		this.durationList = BX.Calendar.UserField.ResourceBooking.getDurationList(params.fullDay);
		this.changeValueCallback = params.changeValueCallback;
		this.defaultValue = params.defaultValue || this.data.defaultValue;

		this.handleSettingsData(params.data);
	}
	BX.extend(ViewFormDurationControl, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.ViewFormDurationControl = ViewFormDurationControl;

	ViewFormDurationControl.prototype.handleSettingsData = function()
	{
		this.durationItems = [];
		if (BX.type.isArray(this.durationList))
		{
			this.durationList.forEach(function(item)
			{
				this.durationItems.push({
					id: item.value,
					title: item.label
				});
			}, this);
		}
	};

	ViewFormDurationControl.prototype.displayControl = function()
	{
		this.DOM.durationInput = this.DOM.controlWrap.appendChild(BX.create('INPUT', {
			attrs: {
				value: this.data.defaultValue || null,
				type: 'text'
			},
			props: {className: 'calendar-resbook-webform-block-input calendar-resbook-webform-block-input-dropdown'}
		}));

		this.durationControl = new BX.Calendar.UserField.ResourceBooking.SelectInput({
			input: this.DOM.durationInput,
			values: this.durationList,
			value: this.data.defaultValue || null,
			editable: this.data.manualInput === 'Y',
			defaultValue: this.defaultValue,
			setFirstIfNotFound: true,
			onChangeCallback: this.changeValueCallback
		});
	};

	ViewFormDurationControl.prototype.refresh = function(data)
	{
		this.refreshLabel(data);
		this.data = data;
		this.handleSettingsData(this.data);

		if (this.setDataConfig())
		{
			if (this.isDisplayed())
			{
				this.show({animation: true});
				if (this.durationControl)
				{
					this.durationControl.setValue(this.data.defaultValue || null);
				}
			}
			else
			{
				this.hide({animation: true});
			}
		}
	};

	ViewFormDurationControl.prototype.getSelectedValue = function()
	{
		var duration = null;
		if (this.durationControl)
		{
			duration = BX.Calendar.UserField.ResourceBooking.parseDuration(this.durationControl.getValue());
		}
		else
		{
			duration = parseInt(this.defaultValue);
		}
		return duration;
	};
	// endregion


	// region 'Date Selector'
	function DateSelector(params)
	{
		this.DOM = {
			outerWrap: params.outerWrap,
			wrap: null
		};

		this.DATE_FORMAT = BX.date.convertBitrixFormat(BX.message("FORMAT_DATE"));
		this.data = params.data || {};
		this.changeValueCallback = params.changeValueCallback;
		this.requestDataCallback = params.requestDataCallback;
		this.previewMode = params.previewMode === undefined;
		this.allowOverbooking = params.allowOverbooking;
		this.setDataConfig();
		this.displayed = true;
	}
	BX.extend(DateSelector, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.DateSelector = DateSelector;

	DateSelector.prototype.display = function(params)
	{
		params = params || {};
		this.setDateIndex(params.availableDateIndex);
		this.setCurrentDate(params.selectedValue);

		this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-block'}}));
		this.DOM.innerWrap = this.DOM.wrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-block-inner'}}));
		if (this.data.label)
		{
			this.DOM.labelWrap = this.DOM.innerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-block-title'}, text: this.data.label + '*'}));
		}
		this.displayControl();
		this.shown = true;
	};

	DateSelector.prototype.refresh = function(data, params)
	{
		params = params || {};
		this.setDateIndex(params.availableDateIndex);
		this.setCurrentDate(params.selectedValue);

		this.data = data;
		BX.adjust(this.DOM.labelWrap, {text: this.data.label + '*'});

		if (this.setDataConfig())
		{
			BX.remove(this.DOM.controlWrap);
			this.displayControl();
		}

		if (this.style === 'line')
		{
			this.lineDateControl.refreshDateAvailability();
		}
	};

	DateSelector.prototype.setDataConfig = function ()
	{
		var
			style = this.data.style === 'line' ? 'line' : 'popup', // line|popup
			start = this.data.start === 'today' ? 'today' : 'free',
			configWasChanged = this.style !== style || this.start !== start;

		this.style = style;
		this.start = start;

		return configWasChanged;
	};

	DateSelector.prototype.hide = function()
	{
		BX.remove(this.DOM.innerWrap);
		this.DOM.innerWrap = null;
	};

	DateSelector.prototype.displayControl = function()
	{
		this.DOM.controlWrap = this.DOM.innerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-block-date'}}));

		if (this.style === 'popup')
		{
			this.DOM.controlWrap.className = 'calendar-resbook-webform-block-calendar';
			this.popupSateControl = new PopupDateSelector(
			{
				wrap: this.DOM.controlWrap,
				isDateAvailable: BX.proxy(this.isDateAvailable, this),
				onChange: BX.proxy(function(value)
				{
					this.onChange(value);
				}, this)
			});
			this.popupSateControl.build();
			this.popupSateControl.setValue(this.getValue());
		}
		else if (this.style === 'line')
		{
			this.DOM.controlWrap.className = 'calendar-resbook-webform-block-date';
			this.lineDateControl = new LineDateSelector(
				{
					wrap: this.DOM.controlWrap,
					isDateAvailable: BX.proxy(this.isDateAvailable, this),
					onChange: BX.proxy(this.onChange, this)
				}
			);
			this.lineDateControl.build();
			this.lineDateControl.setValue(this.getValue());
		}
	};

	DateSelector.prototype.setCurrentDate = function(date)
	{
		if (BX.type.isDate(date))
		{
			this.currentDate = date;
		}
	};

	DateSelector.prototype.setDateIndex = function(availableDateIndex)
	{
		if (BX.type.isPlainObject(availableDateIndex))
		{
			this.availableDateIndex = availableDateIndex;
		}
	};

	DateSelector.prototype.isDateLoaded = function(date)
	{
		if (BX.type.isDate(date) && !this.isItPastDate(date) && this.availableDateIndex)
		{
			if (this.availableDateIndex[BX.date.format(this.DATE_FORMAT, date)] !== undefined)
			{
				return true;
			}

			if (BX.type.isFunction(this.requestDataCallback))
			{
				this.requestDataCallback({date: date});
			}
		}
		return false;
	};

	DateSelector.prototype.isDateAvailable = function(date)
	{
		if (this.previewMode || this.allowOverbooking)
		{
			return true;
		}

		if (BX.type.isDate(date) && !this.isItPastDate(date) && this.availableDateIndex)
		{
			var dateKey = BX.date.format(this.DATE_FORMAT, date);
			if (this.availableDateIndex[dateKey] === undefined)
			{
				if (BX.type.isFunction(this.requestDataCallback))
				{
					this.requestDataCallback({date: date});
				}
				return false;
			}
			else
			{
				return this.availableDateIndex[dateKey];
			}
		}
		return false;
	};

	DateSelector.prototype.isItPastDate = function(date)
	{
		if (BX.type.isDate(date))
		{
			var
				nowDate = new Date(),
				checkDate = new Date(date.getTime());

			nowDate.setHours(0, 0, 0, 0);
			checkDate.setHours(0, 0, 0, 0);

			return checkDate.getTime() < nowDate.getTime();
		}
		return false;
	};

	DateSelector.prototype.refreshCurrentValue = function()
	{
		this.onChange(this.getDisplayedValue());
	};

	DateSelector.prototype.getDisplayedValue = function()
	{
		return this.style === 'popup' ? this.popupSateControl.getValue() : this.lineDateControl.getValue();;
	};

	DateSelector.prototype.onChange = function(date)
	{
		if (BX.type.isFunction(this.changeValueCallback))
		{
			// if (value === false)
			// {
			// 	this.showWarning(BX.message('WEBF_RES_BOOKING_STATUS_DATE_IS_NOT_AVAILABLE'));
			// }
			// else
			// {
			// 	this.hideWarning();
			// }
			var realDate = date;
			if (!BX.type.isDate(realDate))
			{
				realDate = this.getDisplayedValue();
			}
			this.setCurrentDate(date);
			this.changeValueCallback(date, realDate, this.isDateAvailable(realDate));
		}
	};

	DateSelector.prototype.getValue = function()
	{
		if (!this.currentDate)
		{
			this.currentDate = new Date();
		}
		return this.currentDate;
	};


	function PopupDateSelector(params)
	{
		this.DOM = {
			outerWrap: params.wrap,
			wrap: null
		};
		this.value = null;
		this.isDateAvailable = BX.type.isFunction(params.isDateAvailable) ? params.isDateAvailable : function(){return true};
		this.onChange = BX.type.isFunction(params.onChange) ? params.onChange : function(){};
	}

	PopupDateSelector.prototype = {
		build: function()
		{
			this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {
					props : { className : 'calendar-resbook-webform-block-strip'},
					events: {click: BX.proxy(this.handleClick, this)}
			}));
			this.DOM.valueInput = this.DOM.wrap.appendChild(BX.create("input", {
				attrs : {
					type : 'hidden',
					value: ''
				}
			}));

			this.DOM.previousArrow = this.DOM.wrap.appendChild(BX.create("span", {
				attrs : {
					className : 'calendar-resbook-webform-block-strip-arrow calendar-resbook-webform-block-strip-arrow-prev',
					'data-bx-resbook-date-meta' : 'previous'
				}
			}));

			this.DOM.stateWrap = this.DOM.wrap.appendChild(BX.create("span", {
				attrs : {
					className : 'calendar-resbook-webform-block-strip-text',
					'data-bx-resbook-date-meta' : 'calendar'
				}
			}));
			this.DOM.stateWrapDate = this.DOM.stateWrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-strip-date'}}));
			this.DOM.stateWrapDay = this.DOM.stateWrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-strip-day'}}));

			this.DOM.nextArrow = this.DOM.wrap.appendChild(BX.create("span", {
				attrs : {
					className : 'calendar-resbook-webform-block-strip-arrow calendar-resbook-webform-block-strip-arrow-next',
					'data-bx-resbook-date-meta' : 'next'
				}}));
		},

		getValue: function()
		{
			return this.value;
		},

		setValue: function(dateValue)
		{
			this.value = dateValue;
			BX.adjust(this.DOM.stateWrapDate, {text: BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_DATE_LINE'), dateValue)});
			BX.adjust(this.DOM.stateWrapDay, {text: BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_DAY_LINE'), dateValue)});

			if (!this.isDateAvailable(dateValue) || !BX.type.isDate(dateValue))
			{
				this.onChange(false);
			}
			else
			{
				this.onChange(this.value);
			}
		},

		handleClick: function(e)
		{
			var
				dateValue,
				target = e.target || e.srcElement;

			if (target.hasAttribute('data-bx-resbook-date-meta') ||
				(target = BX.findParent(target, {attribute: 'data-bx-resbook-date-meta'}, this.DOM.wrap))
			)
			{
				var dateMeta = target.getAttribute('data-bx-resbook-date-meta');
				if (dateMeta === 'previous')
				{
					dateValue = this.getValue();
					dateValue.setDate(dateValue.getDate() - 1);
					this.setValue(dateValue);
				}
				else if (dateMeta === 'next')
				{
					dateValue = this.getValue();
					dateValue.setDate(dateValue.getDate() + 1);
					this.setValue(dateValue);
				}
				else if (dateMeta === 'calendar')
				{
					this.openCalendarPopup();
				}
			}
		},

		openCalendarPopup: function()
		{
			this.DOM.valueInput.value = BX.date.format(BX.date.convertBitrixFormat(BX.message("FORMAT_DATE")), this.getValue().getTime() / 1000);

			BX.calendar({node: this.DOM.stateWrap, field: this.DOM.valueInput, bTime: false});
			if (BX.calendar.get().popup)
			{
				BX.removeCustomEvent(BX.calendar.get().popup, 'onPopupClose', BX.proxy(this.handleCalendarClose, this));
				BX.addCustomEvent(BX.calendar.get().popup, 'onPopupClose', BX.proxy(this.handleCalendarClose, this));
			}
		},

		handleCalendarClose: function()
		{
			this.setValue(BX.parseDate(this.DOM.valueInput.value));
		}
	};


	function LineDateSelector(params)
	{
		params = params || {};
		this.DOM = {
			outerWrap: params.wrap,
			wrap: null
		};
		this.value = null;
		this.isDateAvailable = BX.type.isFunction(params.isDateAvailable) ? params.isDateAvailable : function(){return true};
		this.onChange = BX.type.isFunction(params.onChange) ? params.onChange : function(){};
		this.DAYS_DISPLAY_SIZE = 30;
		this.DOM.dayNodes = {};
		this.dayNodeIndex = {};
	}

	LineDateSelector.prototype = {
		build: function()
		{
			this.DOM.monthTitle = this.DOM.outerWrap.appendChild(BX.create("span", {
				props : { className : 'calendar-resbook-webform-block-date-month'}
			}));

			this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {
				props : { className : 'calendar-resbook-webform-block-date-range'},
				events: {click: BX.proxy(this.handleClick, this)}
			}));

			this.DOM.controlStaticWrap = this.DOM.wrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-date-range-static-wrap'}}));
			this.DOM.controlInnerWrap = this.DOM.controlStaticWrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-date-range-inner-wrap'}}));

			this.DOM.valueInput = this.DOM.wrap.appendChild(BX.create("input", {
				attrs : {
					type : 'hidden',
					value: ''
				}
			}));

			this.fillDays();
			this.initCustomScroll();
		},

		fillDays: function()
		{
			var
				i,
				startDate = this.getStartLoadDate(),
				date = new Date(startDate.getTime());

			for (i = 0; i < this.DAYS_DISPLAY_SIZE; i++)
			{
				this.addDateSlot(date);
				date.setDate(date.getDate() + 1);
			}

			this.innerWidth = parseInt(this.DOM.controlInnerWrap.offsetWidth);
		},

		addDateSlot: function(date)
		{
			var dateCode = BX.date.format('Y-m-d', date.getTime() / 1000);
			this.dayNodeIndex[dateCode] = new Date(date.getTime());
			this.DOM.dayNodes[dateCode] = this.DOM.controlInnerWrap.appendChild(BX.create("div", {
				attrs : {
					className : 'calendar-resbook-webform-block-date-item' + (this.isDateAvailable(date) ? '' : ' calendar-resbook-webform-block-date-item-off'),
					'data-bx-resbook-date-meta' : dateCode
				},
				html: '<div class="calendar-resbook-webform-block-date-item-inner">' +
					'<span class="calendar-resbook-webform-block-date-number">' +
						BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_DATE'), date) +
					'</span>' +
					'<span class="calendar-resbook-webform-block-date-day">' +
						BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_DAY_OF_THE_WEEK'), date) +
					'</span>' +
				'</div>'
			}));
		},

		refreshDateAvailability: function()
		{
			for (var dateCode in this.DOM.dayNodes)
			{
				if (this.DOM.dayNodes.hasOwnProperty(dateCode))
				{
					if (this.isDateAvailable(this.dayNodeIndex[dateCode]))
					{
						BX.removeClass(this.DOM.dayNodes[dateCode], 'calendar-resbook-webform-block-date-item-off');
					}
					else
					{
						BX.addClass(this.DOM.dayNodes[dateCode], 'calendar-resbook-webform-block-date-item-off');
					}
				}
			}
		},

		handleClick: function(e)
		{
			var
				dateValue,
				target = e.target || e.srcElement;

			if (target.hasAttribute('data-bx-resbook-date-meta') ||
				(target = BX.findParent(target, {attribute: 'data-bx-resbook-date-meta'}, this.DOM.wrap))
			)
			{
				var dateMeta = target.getAttribute('data-bx-resbook-date-meta');
				if (dateMeta && (dateValue = BX.parseDate(dateMeta, false, 'YYYY-MM-DD')))
				{
					this.setValue(dateValue);
				}
			}
		},

		setValue: function(dateValue)
		{
			if (BX.type.isDate(dateValue))
			{
				this.value = dateValue;
				var dayNode = this.getDayNode(dateValue);
				if (dayNode)
				{
					this.setSelected(dayNode);
				}
				this.onChange(this.value);
			}
		},

		getValue: function()
		{
			return this.value;
		},

		getDayNode: function(dateValue)
		{
			var dateCode = BX.date.format('Y-m-d', dateValue.getTime() / 1000);
			if (this.DOM.dayNodes[dateCode])
			{
				return this.DOM.dayNodes[dateCode];
			}
			else
			{
				this.fillDays(dateValue);
				if (this.DOM.dayNodes[dateCode])
				{
					return this.DOM.dayNodes[dateCode];
				}
			}
			return null;
		},

		setSelected: function(dayNode)
		{
			if (this.currentSelected)
			{
				BX.removeClass(this.currentSelected, 'calendar-resbook-webform-block-date-item-select');
			}
			this.currentSelected = dayNode;
			BX.addClass(dayNode, 'calendar-resbook-webform-block-date-item-select');
		},

		getStartLoadDate: function()
		{
			if (!this.startLoadDate)
			{
				this.startLoadDate = new Date();
			}
			else
			{
				this.startLoadDate.setDate(this.startLoadDate.getDate() + this.DAYS_DISPLAY_SIZE);
			}
			return this.startLoadDate;
		},

		initCustomScroll: function()
		{
			var arrowWrap = this.DOM.wrap.appendChild(BX.create("div", {
				props : { className : 'calendar-resbook-webform-block-arrow-container'}
			}));

			this.DOM.leftArrow = arrowWrap.appendChild(BX.create("span",
				{
					props : {className : 'calendar-resbook-webform-block-arrow calendar-resbook-webform-block-arrow-prev'},
					events: {click: BX.proxy(this.handlePrevArrowClick, this)}
				}));
			this.DOM.rightArrow = arrowWrap.appendChild(BX.create("span",
				{
					props : { className : 'calendar-resbook-webform-block-arrow calendar-resbook-webform-block-arrow-next'},
					events: {click: BX.proxy(this.handleNextArrowClick, this)}
				}));

			this.outerWidth = parseInt(this.DOM.controlStaticWrap.offsetWidth);
			this.innerWidth = parseInt(this.DOM.controlInnerWrap.offsetWidth);

			if ('onwheel' in document)
			{
				BX.bind(this.DOM.controlStaticWrap, "wheel", BX.proxy(this.mousewheelScrollHandler, this));
			}
			else
			{
				BX.bind(this.DOM.controlStaticWrap, "mousewheel", BX.proxy(this.mousewheelScrollHandler, this));
			}

			this.checkScrollPosition();
		},

		handleNextArrowClick: function()
		{
			this.DOM.controlStaticWrap.scrollLeft = this.DOM.controlStaticWrap.scrollLeft + 100;
			this.checkScrollPosition();
		},

		handlePrevArrowClick: function()
		{
			this.DOM.controlStaticWrap.scrollLeft = Math.max(this.DOM.controlStaticWrap.scrollLeft - 100, 0);
			this.checkScrollPosition();
		},

		mousewheelScrollHandler: function(e)
		{
			e = e || window.event;
			var delta = e.deltaY || e.detail || e.wheelDelta;
			if (Math.abs(delta) > 0)
			{
				if (!BX.browser.IsMac())
				{
					delta = delta * 3;
				}
				this.DOM.controlStaticWrap.scrollLeft = Math.max(this.DOM.controlStaticWrap.scrollLeft + delta, 0);
				this.checkScrollPosition();
				return BX.PreventDefault(e);
			}
		},

		checkScrollPosition: function()
		{
			if (this.outerWidth <= this.innerWidth)
			{
				this.DOM.leftArrow.style.display = this.DOM.controlStaticWrap.scrollLeft === 0 ? 'none' : '';
				//this.DOM.rightArrow.style.display = (this.innerWidth - this.outerWidth - 4 <= this.DOM.controlStaticWrap.scrollLeft) ? 'none' : '';
				if (this.innerWidth - this.outerWidth - 4 <= this.DOM.controlStaticWrap.scrollLeft)
				{
					this.fillDays();
				}
			}

			this.updateMonthTitle();
		},

		updateMonthTitle: function()
		{
			if (!this.dayNodeOuterWidth)
			{
				this.dayNodeOuterWidth = this.DOM.controlInnerWrap.childNodes[1].offsetLeft - this.DOM.controlInnerWrap.childNodes[0].offsetLeft;
				if (!this.dayNodeOuterWidth)
				{
					return setTimeout(BX.delegate(this.updateMonthTitle, this), 100);
				}
			}

			var
				monthFrom, monthTo, dateMeta, dateValue,
				firstDayNodeIndex = Math.floor(this.DOM.controlStaticWrap.scrollLeft / this.dayNodeOuterWidth),
				lastDayNodeIndex = Math.floor((this.DOM.controlStaticWrap.scrollLeft + this.outerWidth) / this.dayNodeOuterWidth);

			if (this.DOM.controlInnerWrap.childNodes[firstDayNodeIndex])
			{
				dateMeta = this.DOM.controlInnerWrap.childNodes[firstDayNodeIndex].getAttribute('data-bx-resbook-date-meta');
				if (dateMeta && (dateValue = BX.parseDate(dateMeta, false, 'YYYY-MM-DD')))
				{
					monthFrom = monthTo = BX.date.format('f', dateValue);
				}
			}

			if (this.DOM.controlInnerWrap.childNodes[lastDayNodeIndex])
			{
				dateMeta = this.DOM.controlInnerWrap.childNodes[lastDayNodeIndex].getAttribute('data-bx-resbook-date-meta');
				if (dateMeta && (dateValue = BX.parseDate(dateMeta, false, 'YYYY-MM-DD')))
				{
					monthTo = BX.date.format('f', dateValue);
				}
			}

			if (monthFrom && monthTo)
			{
				BX.adjust(this.DOM.monthTitle, {text: monthTo === monthFrom ? monthFrom : monthFrom + ' - ' + monthTo});
			}
		}
	};
	// endregion


	//region 'TimeSelector'
	function TimeSelector(params)
	{
		this.DOM = {
			outerWrap: params.outerWrap,
			wrap: null
		};

		this.data = params.data || {};
		this.setDataConfig();

		this.timeFrom = this.data.timeFrom || params.timeFrom || 7;
		if (params.timeFrom !== undefined)
		{
			this.timeFrom = params.timeFrom;
		}
		this.timeTo = this.data.timeTo || 20;
		if (params.timeTo !== undefined)
		{
			this.timeTo = params.timeTo;
		}
		this.SLOTS_ROW_AMOUNT = 6;
		this.id = 'time-selector-' + Math.round(Math.random() * 1000);
		this.popupSelectId = this.id + '-select-popup';

		this.previewMode = params.previewMode === undefined;
		this.changeValueCallback = params.changeValueCallback;
		this.timezone = params.timezone;
		this.timezoneOffset = params.timezoneOffset;
		this.timezoneOffsetLabel = params.timezoneOffsetLabel;
		this.timeMidday = 12;
		this.timeEvening = 17;
		this.displayed = true;
	}
	BX.extend(TimeSelector, ResourceBookingViewControlAbstract);
	BX.Calendar.UserField.TimeSelector = TimeSelector;

	TimeSelector.prototype.setDataConfig = function ()
	{
		var
			style = this.data.style === 'select' ? 'select' : 'slots', // select|slots
			showOnlyFree = this.data.showOnlyFree !== 'N',
			showFinishTime = this.data.showFinishTime === 'Y',
			scale = parseInt(this.data.scale || 30),
			configWasChanged = this.style !== style || this.showOnlyFree !== showOnlyFree || this.showFinishTime !== showFinishTime || this.scale !== scale;

		this.style = style;
		this.showOnlyFree = showOnlyFree;
		this.showFinishTime = showFinishTime;
		this.scale = scale;

		return configWasChanged;
	};

	TimeSelector.prototype.display = function ()
	{
		this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block'}}));
		this.DOM.innerWrap = this.DOM.wrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-inner'}}));
		if (this.data.label)
		{
			this.DOM.labelWrap = this.DOM.innerWrap.appendChild(BX.create("div", {
				props: {className: 'calendar-resbook-webform-block-title'},
				text: this.data.label + '*'
			}));

			if (this.timezone)
			{
				this.DOM.timezoneLabelWrap = this.DOM.labelWrap.appendChild(BX.create("div", {
					props: {className: 'calendar-resbook-webform-block-title-timezone'}
				}));
				BX.adjust(this.DOM.timezoneLabelWrap, {html: BX.message('USER_TYPE_RESOURCE_TIMEZONE').replace('#TIMEZONE#', this.timezone + ' ' + this.timezoneOffsetLabel)});
			}
		}

		this.displayControl();
		this.setValue(this.getValue());
		this.shown = true;
	};

	TimeSelector.prototype.refresh = function (data, params)
	{
		params = params || {};
		this.setSlotIndex(params.slotIndex);
		this.currentDate = params.currentDate || new Date();
		this.data = data;

		if (!this.isShown())
		{
			this.setDataConfig();
			this.display();
		}
		else
		{
			if (this.DOM.labelWrap && this.data.label)
			{
				BX.adjust(this.DOM.labelWrap, {text: this.data.label + '*'});
			}

			if (this.timezone)
			{
				if (!this.DOM.timezoneLabelWrap || !BX.isNodeInDom(this.DOM.timezoneLabelWrap))
				{
					this.DOM.timezoneLabelWrap = this.DOM.labelWrap.appendChild(BX.create("div", {
						props: {className: 'calendar-resbook-webform-block-title-timezone'}
					}));
				}
				BX.adjust(this.DOM.timezoneLabelWrap, {html: BX.message('USER_TYPE_RESOURCE_TIMEZONE').replace('#TIMEZONE#', this.timezone + ' ' + this.timezoneOffsetLabel)});
			}

			if (this.setDataConfig() || params.slotIndex || params.selectedValue)
			{
				BX.remove(this.DOM.controlWrap);
				this.displayControl();
			}
		}

		this.setCurrentValue(params.selectedValue || this.getValue());
	};

	TimeSelector.prototype.setSlotIndex = function(slotIndex)
	{
		if (BX.type.isPlainObject(slotIndex))
		{
			this.availableSlotIndex = slotIndex;
		}
	};
	TimeSelector.prototype.setCurrentValue = function (timeValue)
	{
		if (timeValue && (this.previewMode || this.availableSlotIndex[timeValue]))
		{
			this.setValue(timeValue);
		}
		else
		{
			this.setValue(null);
		}
	};

	TimeSelector.prototype.showEmptyWarning = function ()
	{
		if (this.DOM.labelWrap)
		{
			this.DOM.labelWrap.style.display = 'none';
		}

		if (!this.DOM.warningWrap)
		{
			this.DOM.warningTextNode = BX.create("span", {props: {className: 'calendar-resbook-webform-block-notice-date'}});
			this.DOM.warningWrap = this.DOM.innerWrap.appendChild(BX.create("div", {
				props: {className: 'calendar-resbook-webform-block-notice'},
				children: [
					BX.create("span", {props: {className: 'calendar-resbook-webform-block-notice-icon'}}),
					this.DOM.warningTextNode,
					BX.create("span", {
						props: {className: 'calendar-resbook-webform-block-notice-detail'},
						text: BX.message('WEBF_RES_BOOKING_BUSY_DAY_WARNING')
					})
				]
			}));
		}

		if (this.DOM.warningWrap)
		{
			BX.adjust(this.DOM.warningTextNode, {text: BX.date.format(BX.message('WEBF_RES_BUSY_DAY_DATE_FORMAT'), this.currentDate)});
			this.DOM.warningWrap.style.display = '';

			this.noSlotsAvailable = true;
		}
	};

	TimeSelector.prototype.hideEmptyWarning = function ()
	{
		this.noSlotsAvailable = false;
		if (this.DOM.labelWrap)
		{
			this.DOM.labelWrap.style.display = '';
		}
		if (this.DOM.warningWrap)
		{
			this.DOM.warningWrap.style.display = 'none';
		}
	};

	TimeSelector.prototype.displayControl = function ()
	{
		var slotsInfo = this.getSlotsInfo();
		this.slots = slotsInfo.slots;

		if (!slotsInfo.freeSlotsCount)
		{
			this.showEmptyWarning();
		}
		else
		{
			this.hideEmptyWarning();
			if (this.style === 'select')
			{
				this.createSelectControl();
			}
			else if (this.style === 'slots')
			{
				this.createSlotsControl();
			}
		}
	};

	TimeSelector.prototype.hide = function ()
	{
		if (this.DOM.innerWrap)
		{
			this.DOM.innerWrap.style.display = 'none';
		}
	};
	TimeSelector.prototype.show = function ()
	{
		if (this.DOM.innerWrap)
		{
			this.DOM.innerWrap.style.display = '';
		}
	};

	TimeSelector.prototype.createSlotsControl = function ()
	{
		if (this.DOM.controlWrap)
		{
			BX.remove(this.DOM.controlWrap);
		}

		this.DOM.controlWrap = this.DOM.innerWrap.appendChild(
			BX.create("div", {
				props: {className: 'calendar-resbook-webform-block-time'},
				events: {click: BX.proxy(this.handleClick, this)}
			}));

		if (!this.showFinishTime && !BX.isAmPmMode())
		{
			BX.addClass(this.DOM.controlWrap, 'calendar-resbook-webform-block-time-sm');
		}
		else if (!this.showFinishTime && BX.isAmPmMode())
		{
			BX.addClass(this.DOM.controlWrap, 'calendar-resbook-webform-block-time-md');
		}
		else if (BX.isAmPmMode())
		{
			BX.addClass(this.DOM.controlWrap, 'calendar-resbook-webform-block-time-lg');
		}

		this.DOM.controlStaticWrap = this.DOM.controlWrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-time-static-wrap'}}));
		this.DOM.controlInnerWrap = this.DOM.controlStaticWrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-time-inner-wrap'}}));

		var
			itemsInColumn,
			maxColumnNumber = 3,
			parts = {},
			itemNumber = 0,
			innerWrap;

		// FilterSlots
		this.slots.forEach(function(slot)
		{
			if (!parts[slot.partOfTheDay])
			{
				parts[slot.partOfTheDay] = {
					items: []
				};
			}

			parts[slot.partOfTheDay].items.push(slot);
		});

		this.slots.forEach(function(slot)
		{
			if (!parts[slot.partOfTheDay].wrap)
			{
				itemNumber = 0;
				itemsInColumn = 6;
				parts[slot.partOfTheDay].wrap = BX.create("div", {
					props: {className: 'calendar-resbook-webform-block-col'},
					html: '<span class="calendar-resbook-webform-block-col-title">'
					+ BX.message('WEBF_RES_PART_OF_THE_DAY_' + slot.partOfTheDay.toUpperCase())
					+ '</span>'
				});

				parts[slot.partOfTheDay].itemsWrap = parts[slot.partOfTheDay].wrap
					.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-col-list'}}));

				if (parts[slot.partOfTheDay].items.length > maxColumnNumber * itemsInColumn)
				{
					itemsInColumn = Math.ceil(parts[slot.partOfTheDay].items.length / maxColumnNumber);
				}
			}

			if (itemNumber % itemsInColumn === 0)
			{
				innerWrap = parts[slot.partOfTheDay].itemsWrap.appendChild(BX.create("div", {props: {className: 'calendar-resbook-webform-block-col-list-inner'}}))
			}

			if (innerWrap && (!slot.booked || !this.showOnlyFree))
			{
				innerWrap.appendChild(BX.create("div", {
					attrs: {
						'data-bx-resbook-time-meta': 'slot' + (slot.booked ? '-off' : ''),
						'data-bx-resbook-slot': slot.time.toString(),
						className: 'calendar-resbook-webform-block-col-item'
						+ (slot.selected ? ' calendar-resbook-webform-block-col-item-select' : '')
						+ (slot.booked ? ' calendar-resbook-webform-block-col-item-off' : '')
					},
					html: '<div class="calendar-resbook-webform-block-col-item-inner">' + '<span class="calendar-resbook-webform-block-col-time">' + slot.fromTime + '</span>' + (this.showFinishTime ? '- <span class="calendar-resbook-webform-block-col-time calendar-resbook-webform-block-col-time-end">' + slot.toTime + '</span>' : ''
					) + '</div>'
				}));
				itemNumber++;
			}

			parts[slot.partOfTheDay].itemsAmount = itemNumber;
		}, this);

		var k;
		for (k in parts)
		{
			if (parts.hasOwnProperty(k) && parts[k].itemsAmount > 0)
			{
				this.DOM.controlInnerWrap.appendChild(parts[k].wrap);
			}
		}

		this.initCustomScrollForSlots();
	};

	TimeSelector.prototype.createSelectControl = function ()
	{
		if (this.DOM.controlWrap)
		{
			BX.remove(this.DOM.controlWrap);
		}

		this.DOM.controlWrap = this.DOM.innerWrap.appendChild(BX.create("div", {
			props: {className: 'calendar-resbook-webform-block-field'},
			events: {click: BX.proxy(this.handleClick, this)}
		}));

		this.DOM.timeSelectWrap = this.DOM.controlWrap.appendChild(BX.create("div", {
			props: {className: 'calendar-resbook-webform-block-strip'}
		}));
		this.DOM.valueInput = this.DOM.timeSelectWrap.appendChild(BX.create("input", {
			attrs: {
				type: 'hidden',
				value: ''
			}
		}));

		this.DOM.previousArrow = this.DOM.timeSelectWrap.appendChild(BX.create("span", {
			attrs: {
				className: 'calendar-resbook-webform-block-strip-arrow calendar-resbook-webform-block-strip-arrow-prev',
				'data-bx-resbook-time-meta': 'previous'
			}
		}));

		this.DOM.stateWrap = this.DOM.timeSelectWrap.appendChild(BX.create("span", {
			attrs: {
				className: 'calendar-resbook-webform-block-strip-text',
				'data-bx-resbook-time-meta': 'select'
			}
		}));
		this.DOM.stateWrap = this.DOM.stateWrap.appendChild(BX.create("span", {props: {className: 'calendar-resbook-webform-block-strip-date'}}));

		this.DOM.nextArrow = this.DOM.timeSelectWrap.appendChild(BX.create("span", {
			attrs: {
				className: 'calendar-resbook-webform-block-strip-arrow calendar-resbook-webform-block-strip-arrow-next',
				'data-bx-resbook-time-meta': 'next'
			}
		}));

		this.setValue(this.getValue());
	};

	TimeSelector.prototype.setValue = function (value)
	{
		var slot = this.getSlotByTime(value);
		if (slot)
		{
			if (this.style === 'select')
			{
				BX.adjust(this.DOM.stateWrap, {text: this.getTimeTextBySlot(slot)});
			}
			else if (this.style === 'slots')
			{
				this.setSelected(this.getSlotNode(slot.time));
			}
			this.value = slot.time;
		}
		else
		{
			this.value = null;
		}

		if (!this.previewMode && BX.type.isFunction(this.changeValueCallback))
		{
			this.changeValueCallback(this.value);
		}
	};

	TimeSelector.prototype.getValue = function ()
	{
		if (!this.value && (this.previewMode || this.style === 'select'))
		{
			this.value = this.slots[0].time;
		}
		return this.value;
	};

	TimeSelector.prototype.hasAvailableSlots = function ()
	{
		return !this.noSlotsAvailable;
	};

	TimeSelector.prototype.getTimeTextBySlot = function (slot)
	{
		return slot.fromTime + (this.showFinishTime ? ' - ' + slot.toTime : '');
	};

	TimeSelector.prototype.getSlotByTime = function(time)
	{
		return BX.type.isArray(this.slots) ? this.slots.find(function(slot){return parseInt(slot.time) === parseInt(time);}) : null;
	};

	TimeSelector.prototype.handleClick = function(e)
	{
		var target = e.target || e.srcElement;
		if (target.hasAttribute('data-bx-resbook-time-meta') ||
			(target = BX.findParent(target, {attribute: 'data-bx-resbook-time-meta'}, this.DOM.wrap))
		)
		{
			var meta = target.getAttribute('data-bx-resbook-time-meta');
			if (this.style === 'select')
			{
				if (meta === 'previous')
				{
					this.setValue(this.getValue() - this.scale);
				}
				else if (meta === 'next')
				{
					this.setValue(this.getValue() + this.scale);
				}
				else if (meta === 'select')
				{
					this.openSelectPopup();
				}
			}
			else if (meta === 'slot')
			{
				this.setValue(parseInt(target.getAttribute('data-bx-resbook-slot')));
			}
		}
	};

	TimeSelector.prototype.getSlotsInfo = function()
	{
		var
			slots = [], slot,
			freeSlotsCount = 0,
			finishTime, hourFrom, minFrom,
			hourTo, minTo,
			part = 'morning',
			num = 0,
			time = this.timeFrom * 60;

		while (time < this.timeTo * 60)
		{
			if (time >= this.timeEvening * 60)
			{
				part = 'evening';
			}
			else if (time >= this.timeMidday * 60)
			{
				part = 'afternoon';
			}

			hourFrom = Math.floor(time / 60);
			minFrom = (time) - hourFrom * 60;
			finishTime = time + this.scale;
			hourTo = Math.floor(finishTime / 60);
			minTo = (finishTime) - hourTo * 60;

			slot = {
				time: time,
				fromTime: BX.Calendar.UserField.ResourceBooking.formatTime(hourFrom, minFrom),
				toTime: BX.Calendar.UserField.ResourceBooking.formatTime(hourTo, minTo),
				partOfTheDay: part
			};

			if (this.previewMode)
			{
				if (!num)
				{
					slot.selected = true;
				}
				else if (Math.round(Math.random() * 10) <= 3)
				{
					slot.booked = true;
				}
			}
			else if(this.availableSlotIndex)
			{
				slot.booked = !this.availableSlotIndex[time];
			}

			if (!slot.booked)
			{
				freeSlotsCount++;
			}

			slots.push(slot);
			time += this.scale;
			num++;
		}

		return {
			slots: slots,
			freeSlotsCount: freeSlotsCount
		};
	};

	TimeSelector.prototype.initCustomScrollForSlots = function()
	{
		var arrowWrap = this.DOM.controlWrap.appendChild(BX.create("div", {
			props : { className : 'calendar-resbook-webform-block-arrow-container'}
		}));

		this.DOM.leftArrow = arrowWrap.appendChild(BX.create("span",
			{
				props : {className : 'calendar-resbook-webform-block-arrow calendar-resbook-webform-block-arrow-prev'},
				events: {click: BX.proxy(this.handlePrevArrowClick, this)}
			}));
		this.DOM.rightArrow = arrowWrap.appendChild(BX.create("span",
			{
				props : { className : 'calendar-resbook-webform-block-arrow calendar-resbook-webform-block-arrow-next'},
				events: {click: BX.proxy(this.handleNextArrowClick, this)}
			}));

		this.outerWidth = parseInt(this.DOM.controlStaticWrap.offsetWidth);
		this.innerWidth = parseInt(this.DOM.controlInnerWrap.offsetWidth);

		if ('onwheel' in document)
			BX.bind(this.DOM.controlStaticWrap, "wheel", BX.proxy(this.mousewheelScrollHandler, this));
		else
			BX.bind(this.DOM.controlStaticWrap, "mousewheel", BX.proxy(this.mousewheelScrollHandler, this));

		this.checkSlotsScroll();
	};

	TimeSelector.prototype.handleNextArrowClick = function()
	{
		this.DOM.controlStaticWrap.scrollLeft = this.DOM.controlStaticWrap.scrollLeft + 100;
		this.checkSlotsScroll();
	};

	TimeSelector.prototype.handlePrevArrowClick = function()
	{
		this.DOM.controlStaticWrap.scrollLeft = Math.max(this.DOM.controlStaticWrap.scrollLeft - 100, 0);
		this.checkSlotsScroll();
	};

	TimeSelector.prototype.mousewheelScrollHandler = function(e)
	{
		e = e || window.event;
		var delta = e.deltaY || e.detail || e.wheelDelta;
		if (Math.abs(delta) > 0)
		{
			if (!BX.browser.IsMac())
			{
				delta = delta * 5;
			}
			this.DOM.controlStaticWrap.scrollLeft = Math.max(this.DOM.controlStaticWrap.scrollLeft + delta, 0);
			this.checkSlotsScroll();
			return BX.PreventDefault(e);
		}
	};

	TimeSelector.prototype.checkSlotsScroll = function()
	{
		if (this.outerWidth <= this.innerWidth)
		{
			this.DOM.leftArrow.style.display = this.DOM.controlStaticWrap.scrollLeft ? '' : 'none';
			if (this.innerWidth - this.outerWidth - 4 <= this.DOM.controlStaticWrap.scrollLeft)
			{
				this.DOM.rightArrow.style.display = 'none';
			}
			else
			{
				this.DOM.rightArrow.style.display = '';
			}
		}
	};

	TimeSelector.prototype.openSelectPopup = function()
	{
		if (this.isSelectPopupShown())
		{
			return this.closeSelectPopup();
		}

		this.popup = BX.PopupMenu.create(
			this.popupSelectId,
			this.DOM.stateWrap,
			this.getTimeSelectItems(),
			{
				className: "calendar-resbook-time-select-popup"	,
				angle: true,
				closeByEsc : true,
				autoHide : true,
				offsetTop: 5,
				offsetLeft: 10
			}
		);

		this.popup.show(true);

		BX.addCustomEvent(this.popup.popupWindow, 'onPopupClose', BX.proxy(function()
		{
			BX.PopupMenu.destroy(this.popupSelectId);
			this.popup = null;
		}, this));
	};

	TimeSelector.prototype.closeSelectPopup = function ()
	{
		if (this.isSelectPopupShown())
		{
			this.popup.close();
			BX.unbind(document, 'click', BX.proxy(this.handleClick, this));
		}
	};

	TimeSelector.prototype.isSelectPopupShown = function ()
	{
		return this.popup && this.popup.popupWindow &&
			this.popup.popupWindow.isShown && this.popup.popupWindow.isShown();
	};

	TimeSelector.prototype.getTimeSelectItems = function()
	{
		var menuItems = [];
		this.slots.forEach(function(slot)
		{
			if (this.showOnlyFree && slot.booked)
			{
				return;
			}
			var className = 'menu-popup-no-icon';
			if (slot.booked)
			{
				className += ' menu-item-booked';
			}
			if (slot.selected)
			{
				className += ' menu-item-selected';
			}

			menuItems.push(
				{
					className: className,
					text: this.getTimeTextBySlot(slot),
					dataset: {
						value: slot.time,
						booked: !!slot.booked
					},
					onclick: BX.proxy(this.menuItemClick, this)
				}
			);
		}, this);
		return menuItems;
	};

	TimeSelector.prototype.menuItemClick = function(e, menuItem)
	{
		if (menuItem && menuItem.dataset && menuItem.dataset.value)
		{
			if (!menuItem.dataset.booked)
			{
				this.setValue(menuItem.dataset.value);
			}
		}
		this.closeSelectPopup();
	};

	TimeSelector.prototype.getSlotNode = function(time)
	{
		var i, slotNodes = this.DOM.controlInnerWrap.querySelectorAll('.calendar-resbook-webform-block-col-item');
		for (i = 0; i < slotNodes.length; i++)
		{
			if (parseInt(slotNodes[i].getAttribute('data-bx-resbook-slot')) === parseInt(time))
			{
				return slotNodes[i];
			}
		}
		return null;
	};

	TimeSelector.prototype.setSelected = function(slotNode)
	{
		if (BX.type.isDomNode(slotNode))
		{
			if (this.currentSelected)
			{
				BX.removeClass(this.currentSelected, 'calendar-resbook-webform-block-col-item-select');
			}
			this.currentSelected = slotNode;
			BX.addClass(slotNode, 'calendar-resbook-webform-block-col-item-select');
		}
	};
	// endregion


	// region 'ViewFormDropDownSelect'
	// Select User, Resource, Duration, Service
	function ViewFormDropDownSelect(params)
	{
		this.id = 'viewform-dropdown-select-' + Math.round(Math.random() * 100000);
		this.DOM = {
			wrap: params.wrap
		};
		this.maxHeight = params.maxHeight;
		this.selectAllMessage = BX.message('WEBF_RES_SELECT_ALL');
		this.setSettings(params);
	}

	ViewFormDropDownSelect.prototype = {
		build: function()
		{
			this.DOM.select = this.DOM.wrap.appendChild(BX.create("div", {
				attrs: {
					className: "calendar-resbook-webform-block-input calendar-resbook-webform-block-input-dropdown"
				},
				events: {click: BX.delegate(this.openPopup, this)}
			}));

			this.setSelectedValues(this.selected);
		},

		setSettings: function(params)
		{
			this.handleChangesCallback = BX.type.isFunction(params.handleChangesCallback) ? params.handleChangesCallback : null;
			this.values = params.values;
			this.selected = !BX.type.isArray(params.selected) ? [params.selected] : params.selected;
			this.multiple = params.multiple;
		},

		openPopup: function ()
		{
			if (this.isPopupShown())
			{
				return this.closePopup();
			}

			var menuItems = [];
			this.values.forEach(function(item)
			{
				var className = 'menu-popup-no-icon';
				if (BX.util.in_array(parseInt(item.id), this.selected))
				{
					className += ' menu-item-selected';
				}

				menuItems.push({
					id: item.id,
					className: className,
					text: BX.util.htmlspecialchars(item.title),
					onclick: BX.proxy(this.menuItemClick, this)
				});
			}, this);

			if (this.multiple && menuItems.length <= 1)
			{
				this.multiple = false;
			}

			if (this.multiple)
			{
				menuItems.push({
					id: 'select-all',
					text: this.selectAllMessage,
					onclick: BX.proxy(this.selectAllItemClick, this)
				});
			}

			this.popup = BX.PopupMenu.create(
				this.id,
				this.DOM.select,
				menuItems,
				{
					className: 'calendar-resbook-form-popup' + (this.multiple ? ' popup-window-resource-select' : ''),
					closeByEsc : true,
					autoHide : !this.multiple,
					offsetTop: 0,
					offsetLeft: 0
				}
			);

			this.popup.show(true);
			this.popupContainer = this.popup.popupWindow.popupContainer;
			this.popupContainer.style.width = parseInt(this.DOM.select.offsetWidth) + 'px';

			BX.addCustomEvent(this.popup.popupWindow, 'onPopupClose', BX.proxy(function()
			{
				BX.PopupMenu.destroy(this.id);
				this.popup = null;
			}, this));

			if (this.multiple)
			{
				this.popup.menuItems.forEach(function(menuItem)
				{
					var checked;
					if (menuItem.id === 'select-all')
					{
						this.selectAllChecked = !this.values.find(function(value){
							return !this.selected.find(function(itemId){return itemId === value.id});
						},this);

						menuItem.layout.item.className = 'menu-popup-item menu-popup-item-resource-all';
						menuItem.layout.item.innerHTML = '<div class="menu-popup-item-inner">' +
								'<div class="menu-popup-item-resource">' +
								'<input class="menu-popup-item-resource-checkbox menu-popup-item-all-resources-checkbox" type="checkbox"' + (this.selectAllChecked ? 'checked="checked"' : '') + ' id="' + menuItem.id + '">' +
								'<label class="menu-popup-item-text" for="' + menuItem.id + '">' + menuItem.text + '</label>' +
								'</div>' +
							'</div>';
					}
					else
					{
						checked = this.selected.find(function(itemId){return itemId === menuItem.id});

						menuItem.layout.item.className = 'menu-popup-item';
						menuItem.layout.item.innerHTML = '<div class="menu-popup-item-inner">' +
							'<div class="menu-popup-item-resource">' +
							'<input class="menu-popup-item-resource-checkbox" type="checkbox"' + (checked ? 'checked="checked"' : '') + ' id="' + menuItem.id + '">' +
							'<label class="menu-popup-item-text" for="' + menuItem.id + '">' + menuItem.text + '</label>' +
							'</div>' +
							'</div>';
					}
				}, this);

				BX.unbind(document, 'click', BX.proxy(this.handleClick, this));
				setTimeout(BX.delegate(function(){
					BX.bind(document, 'click', BX.proxy(this.handleClick, this));
				}, this), 50);
			}
		},

		closePopup: function ()
		{
			if (this.isPopupShown())
			{
				this.popup.close();
				if (this.multiple)
				{
					BX.unbind(document, 'click', BX.proxy(this.handleClick, this));
				}
			}
		},

		isPopupShown: function ()
		{
			return this.popup && this.popup.popupWindow &&
				this.popup.popupWindow.isShown && this.popup.popupWindow.isShown() &&
				this.popup.popupWindow.popupContainer &&
				BX.isNodeInDom(this.popup.popupWindow.popupContainer)
		},


		menuItemClick: function(e, menuItem)
		{
			var
				selectAllcheckbox,
				target = e.target || e.srcElement,
				foundValue, checkbox;


			if (this.multiple)
			{
				foundValue = this.values.find(function(value){return value.id == menuItem.id;});
				checkbox = menuItem.layout.item.querySelector('.menu-popup-item-resource-checkbox');

				if (foundValue && target && (BX.hasClass(target, "menu-popup-item") || BX.hasClass(target, "menu-popup-item-resource-checkbox") || BX.hasClass(target, "menu-popup-item-inner")))
				{
					if (!BX.hasClass(target, "menu-popup-item-resource-checkbox"))
					{
						checkbox.checked = !checkbox.checked;
					}

					if (checkbox.checked)
					{
						this.selectItem(foundValue);
					}
					else
					{
						this.deselectItem(foundValue);
						selectAllcheckbox = this.popupContainer.querySelector('.menu-popup-item-all-resources-checkbox');
						this.selectAllChecked = false;
						if (selectAllcheckbox)
						{
							selectAllcheckbox.checked = false;
						}
					}
					this.setSelectedValues(this.selected);
					this.handleControlChanges();
				}
			}
			else
			{
				this.setSelectedValues([menuItem.id]);
				this.handleControlChanges();
				this.closePopup();
			}


		},
		selectItem: function(value)
		{
			if (!BX.util.in_array(value.id, this.selected))
			{
				this.selected.push(value.id);
			}
		},
		deselectItem: function(value)
		{
			var index = BX.util.array_search(value.id, this.selected);
			if (index >= 0)
			{
				this.selected = BX.util.deleteFromArray(this.selected, index);
			}
		},

		selectAllItemClick: function(e, menuItem)
		{
			var target = e.target || e.srcElement;
			if (target && (BX.hasClass(target, "menu-popup-item") || BX.hasClass(target, "menu-popup-item-resource-checkbox")))
			{
				var checkbox = menuItem.layout.item.querySelector('.menu-popup-item-resource-checkbox');

				if (BX.hasClass(target, "menu-popup-item"))
				{
					checkbox.checked = !checkbox.checked;
				}

				var i, checkboxes = this.popupContainer.querySelectorAll('input.menu-popup-item-resource-checkbox');
				this.selectAllChecked = checkbox.checked;

				for (i = 0; i < checkboxes.length; i++)
				{
					checkboxes[i].checked = this.selectAllChecked;
				}
				this.selected = [];
				if (this.selectAllChecked)
				{
					this.values.forEach(function(value){this.selected.push(value.id);}, this);
				}
				this.setSelectedValues(this.selected);
				this.handleControlChanges();
			}
		},
		handleClick: function(e)
		{
			var target = e.target || e.srcElement;
			if (this.isPopupShown() && !BX.isParentForNode(this.popupContainer, target))
			{
				this.closePopup({animation: true});
			}

			this.handleControlChanges();
		},
		getSelectedValues: function()
		{
			return this.selected;
		},
		setSelectedValues: function(values)
		{
			var i,
				foundValue,
				textValues = [],
				selectedValues = [];

			for (i = 0; i < values.length; i++)
			{
				foundValue = this.values.find(function(value){return value.id === values[i];});
				if (foundValue)
				{
					textValues.push(foundValue.title);
					selectedValues.push(foundValue.id);
				}
			}

			this.selected = selectedValues;
			BX.adjust(this.DOM.select, {text: textValues.length ? textValues.join(', ') : BX.message('USER_TYPE_RESOURCE_LIST_PLACEHOLDER')});
		},

		handleControlChanges: function()
		{
			if (this.handleChangesCallback)
			{
				this.handleChangesCallback(this.getSelectedValues());
			}
		}
	};
	// endregion

	// region *ResourceBookingStatusControl*
	function ResourceBookingStatusControl(params)
	{
		this.DOM = {
			outerWrap: params.outerWrap
		};
		this.timezone = params.timezone;
		this.timezoneOffsetLabel = params.timezoneOffsetLabel;
		this.shown = false;
		this.built = false;
	}
	BX.Calendar.UserField.ResourceBookingStatusControl = ResourceBookingStatusControl;

	ResourceBookingStatusControl.prototype = {
		isShown: function()
		{
			return this.shown;
		},

		build: function()
		{
			this.DOM.wrap = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-block-result'}, style: {display: 'none'}}));
			this.DOM.innerWrap = this.DOM.wrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-result-inner'}}));

			this.DOM.labelWrap = this.DOM.innerWrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-result-text'}, text: BX.message('WEBF_RES_BOOKING_STATUS_LABEL')}));
			this.DOM.statusWrap = this.DOM.innerWrap.appendChild(BX.create("span", {props : { className : 'calendar-resbook-webform-block-result-value'}}));
			this.DOM.statusTimezone = this.DOM.innerWrap.appendChild(BX.create("span", {props: {className: 'calendar-resbook-webform-block-result-timezone'}, text: this.timezoneOffsetLabel || '', style: {display: 'none'}}));

			this.built = true;
		},

		refresh: function(params)
		{
			if (!this.built)
			{
				this.build();
			}

			if (!this.isShown())
			{
				this.show();
			}

			if (params.dateFrom)
			{
				this.DOM.labelWrap.style.display = '';
				BX.removeClass(this.DOM.wrap, 'calendar-resbook-webform-block-result-error');
				if (this.timezone)
				{
					this.DOM.statusTimezone.style.display = '';
				}
				BX.adjust(this.DOM.statusWrap, {text: this.getStatusText(params)});
			}
			else if (!params.dateFrom && params.fullDay)
			{
				this.DOM.labelWrap.style.display = 'none';
				this.DOM.statusTimezone.style.display = 'none';
				BX.addClass(this.DOM.wrap, 'calendar-resbook-webform-block-result-error');
				BX.adjust(this.DOM.statusWrap, {text: BX.message('WEBF_RES_BOOKING_STATUS_DATE_IS_NOT_AVAILABLE')});
			}
			else
			{
				this.DOM.labelWrap.style.display = 'none';
				this.DOM.statusTimezone.style.display = 'none';
				BX.removeClass(this.DOM.wrap, 'calendar-resbook-webform-block-result-error');
				BX.adjust(this.DOM.statusWrap, {text: BX.message('WEBF_RES_BOOKING_STATUS_NO_TIME_SELECTED')});
			}
		},

		getStatusText: function(params)
		{
			var
				dateFrom = params.dateFrom,
				dateTo = new Date(dateFrom.getTime() + params.duration * 60 * 1000 + (params.fullDay ? -1 : 0)),
				text = '';

			if (params.fullDay)
			{
				if (BX.date.format('Y-m-d', dateFrom.getTime() / 1000) === BX.date.format('Y-m-d', dateTo.getTime() / 1000))
				{
					text = BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS'), dateFrom);
				}
				else
				{
					text = BX.message('WEBF_RES_DATE_FORMAT_FROM_TO')
						.replace('#DATE_FROM#', BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS_SHORT'), dateFrom))
						.replace('#DATE_TO#', BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS_SHORT'), dateTo));
				}
			}
			else
			{
				if (BX.date.format('Y-m-d', dateFrom.getTime() / 1000) === BX.date.format('Y-m-d', dateTo.getTime() / 1000))
				{
					text = BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS'), dateFrom)
						+ ' '
						+ BX.message('WEBF_RES_TIME_FORMAT_FROM_TO')
							.replace('#TIME_FROM#', BX.Calendar.UserField.ResourceBooking.formatTime(dateFrom.getHours(), dateFrom.getMinutes()))
							.replace('#TIME_TO#', BX.Calendar.UserField.ResourceBooking.formatTime(dateTo.getHours(), dateTo.getMinutes()));
				}
				else
				{
					text = BX.message('WEBF_RES_DATE_FORMAT_FROM_TO')
							.replace('#DATE_FROM#', BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS_SHORT'), dateFrom) + ' '+ BX.Calendar.UserField.ResourceBooking.formatTime(dateFrom.getHours(), dateFrom.getMinutes()))
							.replace('#DATE_TO#', BX.date.format(BX.message('WEBF_RES_DATE_FORMAT_STATUS_SHORT'), dateTo) + ' '+ BX.Calendar.UserField.ResourceBooking.formatTime(dateTo.getHours(), dateTo.getMinutes()));
				}
			}

			return text;
		},

		hide: function()
		{
			if (this.built && this.shown)
			{
				this.DOM.wrap.style.display = 'none';
				this.shown = false;
			}
		},

		show: function()
		{
			if (this.built && !this.shown)
			{
				this.DOM.wrap.style.display = '';
				this.shown = true;
			}
		},

		setError: function(message)
		{
			if (this.DOM.labelWrap)
			{
				this.DOM.labelWrap.style.display = 'none';
			}
			BX.addClass(this.DOM.wrap, 'calendar-resbook-webform-block-result-error');
			BX.adjust(this.DOM.statusWrap, {text: message});
		}
	};
	//endregion

})();

Anon7 - 2022
AnonSec Team