AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 3.128.172.195
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-live.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;
		this.timeFrom = params.timeFrom || 7;
		this.timeTo = params.timeTo || 20;
		this.scale = parseInt(params.field.settings_data.time.scale) || 60;
		this.inputName = params.field.name + '[]';
		this.DATE_FORMAT = BX.date.convertBitrixFormat(BX.message("FORMAT_DATE"));
		this.DATETIME_FORMAT = BX.date.convertBitrixFormat(BX.message("FORMAT_DATETIME"));

		this.loadedDates = [];

		this.accessibility = {
			user : {},
			resource: {}
		};
		this.busySlotMatrix = {
			user : {},
			resource: {}
		};

		this.DOM = {
			wrap: this.params.wrap,
			valueInputs: []
		};
	}
	BX.Calendar.UserField.CrmFormResourceBookingFieldLiveController = CrmFormResourceBookingFieldLiveController;

	CrmFormResourceBookingFieldLiveController.prototype = {
		init: function()
		{
			this.DOM.outerWrap = this.DOM.wrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-wrapper'}}));

			this.showMainLoader();
			this.requireFormData().then(
				BX.delegate(function()
				{
					this.hideMainLoader();
					this.buildFormControls();
					this.onChangeValues();
				}, this)
			);
		},

		check: function()
		{
			var result = true;
			if (this.usersDisplayed() && !this.getSelectedUser())
			{
				this.userControl.showWarning();
				result = false;
			}

			if (result && this.resourcesDisplayed() && !this.getSelectedResources())
			{
				this.resourceControl.showWarning();
				result = false;
			}

			if (result && !this.getCurrentDuration())
			{
				if (this.durationControl)
				{
					this.durationControl.showWarning();
				}
				else if (this.serviceControl)
				{
					this.serviceControl.showWarning();
				}
				result = false;
			}

			if (result && !this.dateControl.getValue())
			{
				this.dateControl.showWarning();
				result = false;
			}

			if (result && this.timeSelectorDisplayed() && !this.timeControl.getValue())
			{
				this.timeControl.showWarning();
				result = false;
			}

			return result;
		},

		buildFormControls: function()
		{
			this.DOM.innerWrap = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-inner'}}));
			this.DOM.inputsWrap = this.DOM.innerWrap.appendChild(BX.create("div"));

			if (!this.getFieldParams())
			{
				this.statusControl = new BX.Calendar.UserField.ResourceBookingStatusControl({
					outerWrap: this.DOM.innerWrap
				});
				this.statusControl.refresh({});
				this.statusControl.setError('[UF_NOT_FOUND] ' + BX.message('WEBF_RES_BOOKING_UF_WARNING'));
			}
			else
			{
				this.preparaAutoSelectValues();

				this.displayUsersControl();
				this.displayResourcesControl();
				this.displayServicesControl();
				this.displayDurationControl();
				this.displayDateTimeControl();

				if (this.selectedUserId || this.selectedResourceId)
				{
					this.refreshControlsState();
				}
			}
		},

		refreshControlsState: function()
		{
			if (this.selectorCanBeShown('resources')
				&& this.resourceControl
				&& !this.resourceControl.isShown())
			{
				this.resourceControl.display();
			}

			// Show services
			if (this.selectorCanBeShown('services')
				&& this.serviceControl
				&& !this.serviceControl.isShown())
			{
				this.serviceControl.display();
			}

			// Show duration
			if (this.selectorCanBeShown('duration')
				&& this.durationControl
				&& !this.durationControl.isShown())
			{
				this.durationControl.display();
			}

			var settingsData = this.getSettingsData();
			// Show date & time control
			if (this.selectorCanBeShown('date') && this.dateControl)
			{
				if (!this.dateControl.isShown())
				{
					var startValue;
					if (settingsData.date.start === 'free')
					{
						startValue = this.getFreeDate({
							resources: this.getSelectedResources(),
							user: this.getSelectedUser(),
							duration: this.getCurrentDuration()
						});
					}
					else
					{
						startValue = new Date();
					}

					this.dateControl.display({
						selectedValue: startValue,
						availableDateIndex: this.getAvailableDateIndex({
							resources: this.getSelectedResources(),
							user: this.getSelectedUser(),
							duration: this.getCurrentDuration()
						})
					});
				}
				else
				{
					this.dateControl.refresh(
						settingsData.date,
						{
							availableDateIndex: this.getAvailableDateIndex({
								resources: this.getSelectedResources(),
								user: this.getSelectedUser(),
								duration: this.getCurrentDuration()
							})
						}
					);

					if (this.timeControl)
					{
						// this.timeControl.refresh(
						// 	settingsData.time,
						// 	{
						// 		slotIndex: this.getAvailableSlotIndex({
						// 			date: this.dateControl.getValue(),
						// 			resources: this.getSelectedResources(),
						// 			user: this.getSelectedUser(),
						// 			duration: this.getCurrentDuration()
						// 		}),
						// 		currentDate: this.dateControl.getValue()
						// 	});

						this.timeControl.refresh(
							settingsData.time,
							{
								slotIndex: this.getSlotIndex({date: this.dateControl.getValue()}),
								currentDate: this.dateControl.getValue()
							});
					}
				}
			}

			this.updateStatusControl();
			this.onChangeValues();
			BX.onCustomEvent(window, 'crmWebFormFireResize');
		},

		onChangeValues: function()
		{
			var
				allValuesValue = '',
				dateFromValue = '',
				dateFrom = this.getCurrentDate(),
				duration = this.getCurrentDuration() * 60,// Duration in minutes
				serviceName = this.getCurrentServiceName(),
				entries = [];

			// Clear inputs
			BX.cleanNode(this.DOM.inputsWrap);
			this.DOM.valueInputs = [];

			if (BX.type.isDate(dateFrom))
			{
				var resources = this.getSelectedResources();
				if (BX.type.isArray(resources))
				{
					resources.forEach(function(resourceId)
					{
						entries = entries.concat({type: 'resource', id: resourceId});
					});
				}

				var selectedUser = this.getSelectedUser();
				if (selectedUser)
				{
					entries = entries.concat({type: 'user', id: selectedUser});
				}

				dateFromValue = BX.date.format(this.DATETIME_FORMAT, dateFrom.getTime() / 1000);

				entries.forEach(function(entry)
				{
					var value = entry.type + '|' + entry.id + '|' + dateFromValue + '|' + duration + '|' + serviceName;
					allValuesValue += value + '#';

					this.DOM.valueInputs.push(this.DOM.inputsWrap.appendChild(
						BX.create('INPUT', {
							attrs:{
								name: this.inputName,
								value: value,
								type: 'hidden'
							}})));
				}, this);
			}

			if (!entries.length)
			{
				this.DOM.valueInputs.push(this.DOM.inputsWrap.appendChild(
					BX.create('INPUT', {
						attrs:{
							name: this.inputName,
							value: 'empty',
							type: 'hidden'
						}})));
			}
		},

		showMainLoader: function()
		{
			if (this.DOM.wrap)
			{
				this.hideMainLoader();
				this.DOM.mainLoader = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-wrapper-loader-wrap'},
					children: [BX.Calendar.UserField.ResourceBooking.getLoader(160)]
				}));
			}
		},

		hideMainLoader: function()
		{
			BX.remove(this.DOM.mainLoader);
		},


		showStatusLoader: function()
		{
			if (this.DOM.wrap)
			{
				this.hideMainLoader();
				this.DOM.mainLoader = this.DOM.outerWrap.appendChild(BX.create("div", {props : { className : 'calendar-resbook-webform-wrapper-loader-wrap'},
					children: [BX.Calendar.UserField.ResourceBooking.getLoader(160)]
				}));
			}
		},

		hideStatusLoader: function()
		{
			BX.remove(this.DOM.mainLoader);
		},

		requestAccessibilityData: function(params)
		{
			if (!this.requestedFormData)
			{
				this.showStatusLoader();

				this.requestedFormData = true;
				var formDataParams = {
					from: params.date
				};

				this.requireFormData(formDataParams).then(
					BX.delegate(function()
					{
						this.hideStatusLoader();
						this.refreshControlsState();
						this.dateControl.refreshCurrentValue();
						this.onChangeValues();
						this.requestedFormData = false;
					}, this)
				);
			}
		},

		requireFormData: function(params)
		{
			params = params || {};
			var
				data = {
					settingsData: this.getSettingsData() || null
				},
				promise = new BX.Promise();

			if (!this.userFieldParams)
			{
				data.fieldName = this.params.field.entity_field_name;
			}

			var
				dateFrom = BX.type.isDate(params.from) ? params.from : new Date(),
				dateTo;

			if (BX.type.isDate(params.to))
			{
				dateTo = params.to;
			}
			else
			{
				dateTo = new Date(dateFrom.getTime());
				dateTo.setDate(dateFrom.getDate() + 60);
			}

			data.from = BX.date.format(this.DATE_FORMAT, dateFrom);
			data.to = BX.date.format(this.DATE_FORMAT, dateTo);

			this.setLoadedDataLimits(dateFrom, dateTo);

			BX.ajax.runAction('calendar.api.resourcebookingajax.getfillformdata', {
				data: data
			}).then(BX.delegate(function(response)
				{
					if (BX.type.isNumber(response.data.timezoneOffset))
					{
						this.timezoneOffset = response.data.timezoneOffset;
						this.timezoneOffsetLabel = response.data.timezoneOffsetLabel;
					}

					if (response.data.workTimeStart !== undefined && response.data.workTimeEnd !== undefined)
					{
						this.timeFrom = parseInt(response.data.workTimeStart);
						this.timeTo = parseInt(response.data.workTimeEnd);
					}

					if (response.data.fieldSettings)
					{
						this.userFieldParams = response.data.fieldSettings;
					}
					if (response.data.userIndex)
					{
						this.userIndex = response.data.userIndex;
					}

					this.handleAccessibilityData(response.data.usersAccessibility, 'user');
					this.handleAccessibilityData(response.data.resourcesAccessibility, 'resource');

					promise.fulfill(response.data);
				}, this),
				function (response) {
					/**
					 {
						 "status": "error",
						 "errors": [...]
					 }
					 **/
				});

			return promise;
		},

		setLoadedDataLimits: function(from, to)
		{
			this.loadedDataFrom = BX.type.isDate(from) ? from : BX.parseDate(from);
			this.loadedDataTo = BX.type.isDate(to) ? to : BX.parseDate(to);

			this.loadedDates = this.loadedDates || [];
			this.loadedDatesIndex = this.loadedDatesIndex || {};

		 	var
				dateKey,
				date = new Date(this.loadedDataFrom.getTime());

			while (true)
			{
				dateKey = BX.date.format(this.DATE_FORMAT, date);
				this.loadedDatesIndex[dateKey] = this.loadedDates.length;
				this.loadedDates.push({
					key: BX.date.format(this.DATE_FORMAT, date),
					slots: {},
					slotsCount: {}
				});
				date.setDate(date.getDate() + 1);

				if (date.getTime() > this.loadedDataTo.getTime())
				{
					break;
				}
			}
		},

		fillDataIndex: function(date, time, entityType, entityId)
		{
			var dateIndex = this.loadedDatesIndex[date];
			if (this.loadedDates[dateIndex])
			{
				if (!this.loadedDates[dateIndex].slots[time])
				{
					this.loadedDates[dateIndex].slots[time] = {};
				}
				if (this.loadedDates[dateIndex].slotsCount[entityType + entityId] === undefined)
				{
					this.loadedDates[dateIndex].slotsCount[entityType + entityId] = 0;
				}
				this.loadedDates[dateIndex].slots[time][entityType + entityId] = true;
				this.loadedDates[dateIndex].slotsCount[entityType + entityId]++;
			}
		},

		handleAccessibilityData: function(data, entityType)
		{
			if (BX.type.isPlainObject(data) && (entityType === 'user' || entityType === 'resource'))
			{
				// For each entry which has accessibility entries
				for (var entityId in data)
				{
					if (data.hasOwnProperty(entityId))
					{
						data[entityId].forEach(function(entry)
						{
							if (!entry.from)
							{
								entry.from = BX.parseDate(entry.dateFrom);
								if (entry.from)
								{
									entry.from.setSeconds(0,0);
									entry.fromTimestamp = entry.from.getTime();
								}
							}

							if (!entry.to)
							{
								entry.to = BX.parseDate(entry.dateTo);
								if (entry.to)
								{
									if (entry.fullDay)
									{
										entry.to.setHours(23, 59, 0, 0);
									}
									else
									{
										entry.to.setSeconds(0, 0);
									}
									entry.toTimestamp = entry.to.getTime();
								}
							}

							if (entry.from && entry.to)
							{
								this.fillBusySlotMatrix(entry, entityType, entityId);
							}
						}, this);
					}
				}
				this.accessibility[entityType] = BX.mergeEx(this.accessibility[entityType], data);
			}
		},

		fillBusySlotMatrix: function(entry, entityType, entityId)
		{
			if (!this.busySlotMatrix[entityType][entityId])
			{
				this.busySlotMatrix[entityType][entityId] = {};
			}

			var
				fromDate = new Date(entry.from.getTime()),
				dateKey = BX.date.format(this.DATE_FORMAT, fromDate),
				dateToKey = BX.date.format(this.DATE_FORMAT, entry.to),
				timeValueFrom = fromDate.getHours() * 60 + fromDate.getMinutes(),
				duration = Math.round((entry.toTimestamp - entry.fromTimestamp) / 60000), // in minutes
				timeValueTo = timeValueFrom + duration,
				slots = this.getTimeSlots(),
				count = 0,
				i;

			if (duration > 0)
			{
				while (true)
				{
					if (!this.busySlotMatrix[entityType][entityId][dateKey])
					{
						this.busySlotMatrix[entityType][entityId][dateKey] = {};
					}

					for (i = 0; i < slots.length; i++)
					{
						if (timeValueFrom < (slots[i].time + this.scale) && timeValueTo > slots[i].time)
						{
							this.busySlotMatrix[entityType][entityId][dateKey][slots[i].time] = true;
							this.fillDataIndex(dateKey, slots[i].time, entityType, entityId);
						}
					}

					if (dateKey === dateToKey)
					{
						break;
					}
					else
					{
						fromDate.setDate(fromDate.getDate() + 1);
						dateKey = BX.date.format(this.DATE_FORMAT, fromDate);
						timeValueFrom = 0;
						if (dateKey === dateToKey)
						{
							timeValueTo = entry.to.getHours() * 60 + entry.to.getMinutes();
						}
						else
						{
							timeValueTo = 1440; // end of the day - 24 hours
						}
					}

					count++;
					if (count > 10000) // emergency exit
					{
						break;
					}
				}
			}
		},

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

		getSettingsData: function()
		{
			return this.params.field.settings_data;
		},

		getUserIndex: function()
		{
			return this.userIndex;
		},

		getFieldParams: function()
		{
			return this.userFieldParams;
		},

		getSettings: function()
		{
			return {
				caption: this.getCaption(),
				data: this.getSettingsData()
			};
		},

		isUserSelectorInAutoMode: function()
		{
			return this.usersDisplayed() && this.getSettingsData().users.show === "N";
		},

		isResourceSelectorInAutoMode: function()
		{
			return this.resourcesDisplayed() && this.getSettingsData().resources.show === "N";
		},

		autoAdjustUserSelector: function()
		{
			var
				currentDate = this.dateControl.getValue(),
				timeValue = this.timeControl.getValue();

			if (BX.type.isDate(currentDate) && timeValue)
			{
				var i, loadedDate = this.loadedDates[this.loadedDatesIndex[BX.date.format(this.DATE_FORMAT, currentDate)]];
				if (loadedDate.slots[timeValue])
				{
					for (i = 0; i < this.userControl.values.length; i++)
					{
						if (!loadedDate.slots[timeValue]['user' + this.userControl.values[i]])
						{
							this.userControl.setSelectedUser(this.userControl.values[i]);
							break;
						}
					}
				}
			}
		},

		autoAdjustResourceSelector: function()
		{
			var
				currentDate = this.dateControl.getValue(),
				timeValue = this.timeControl.getValue();

			if (BX.type.isDate(currentDate) && timeValue)
			{
				var
					i, id,
					loadedDate = this.loadedDates[this.loadedDatesIndex[BX.date.format(this.DATE_FORMAT, currentDate)]];

				if (loadedDate.slots[timeValue])
				{
					for (i = 0; i < this.resourceControl.resourceList.length; i++)
					{
						id = parseInt(this.resourceControl.resourceList[i].id);
						if (!loadedDate.slots[timeValue]['resource' + id])
						{
							this.resourceControl.setSelectedResource(id);
							break;
						}
					}
				}
			}
		},

		preparaAutoSelectValues: function ()
		{
			var
				settingsData = this.getSettingsData(),
				autoSelectUser = this.usersDisplayed() && (settingsData.users.defaultMode === 'auto' || settingsData.users.show === "N"),
				autoSelectResource = this.resourcesDisplayed() && (settingsData.resources.defaultMode === 'auto' || settingsData.resources.show === "N"),
				autoSelectDate = settingsData.date.start === 'free',
				maxStepsAuto = 60,
				date, i;

			this.selectedUserId = false;
			this.selectedResourceId = false;

			date = new Date();
			// Walk through each date searching for free space
			for (i = 0; i <= maxStepsAuto; i++)
			{
				this.getFreeEntitiesForDate(date, {
					autoSelectUser: autoSelectUser,
					autoSelectResource: autoSelectResource,
					slotsAmount: this.getDefaultDurationSlotsAmount()
				});

				if ((this.selectedUserId || !autoSelectUser)
					&&
					(this.selectedResourceId || !autoSelectResource))
				{
					break;
				}

				if (!autoSelectDate)
				{
					break;
				}
				date.setDate(date.getDate() + 1);
			}
		},

		getFreeEntitiesForDate: function(date, params)
		{
			var
				settingsData = this.getSettingsData(),
				slotsAmount = params.slotsAmount || 1,
				i, userList, resList;

			if (params.autoSelectUser)
			{
				userList = BX.type.isArray(settingsData.users.value) ? settingsData.users.value : settingsData.users.value.split('|');
				for (i = 0; i < userList.length; i++)
				{
					if (this.checkSlotsForDate(date, slotsAmount, {
						user: parseInt(userList[i])
					}))
					{
						this.selectedUserId = parseInt(userList[i]);
						break;
					}
				}
			}

			if (params.autoSelectResource)
			{
				resList = BX.type.isArray(settingsData.resources.value) ? settingsData.resources.value : settingsData.resources.value.split('|');

				for (i = 0; i < resList.length; i++)
				{
					if (this.checkSlotsForDate(date, slotsAmount, {
						resources: [parseInt(resList[i])],
						user: this.selectedUserId || null
					}))
					{
						this.selectedResourceId = parseInt(resList[i]);
						break;
					}
				}
			}
		},

		displayUsersControl: function()
		{
			if (this.usersDisplayed())
			{
				this.userControl = new BX.Calendar.UserField.ViewFormUsersControl({
					outerWrap: this.DOM.innerWrap,
					data: this.getSettingsData().users,
					userIndex: this.getUserIndex(),
					previewMode: false,
					autoSelectDefaultValue: this.selectedUserId,
					changeValueCallback: BX.proxy(function(userId)
					{
						BX.onCustomEvent(this, 'CrmFormResourceBookingFieldLiveController:onUserChanged', [userId]);
						this.refreshControlsState();
					}, this)
				});
				this.userControl.display();
			}
		},

		displayResourcesControl: function()
		{
			var
				valueIndex = {},
				dataValue = [],
				fieldParams = this.getFieldParams(),
				settingsData = this.getSettingsData();

			if (this.resourcesDisplayed())
			{
				settingsData.resources.value.split('|').forEach(function(id)
				{
					id = parseInt(id);
					if (id > 0)
					{
						valueIndex[id] = true;
						dataValue.push(id);
					}
				});

				var resourceList = [];
				fieldParams.SELECTED_RESOURCES.forEach(function(res)
				{
					res.id = parseInt(res.id);
					if (valueIndex[res.id])
					{
						resourceList.push(res);
					}
				}, this);

				this.resourceControl = new BX.Calendar.UserField.ViewFormResourcesControl({
					outerWrap: this.DOM.innerWrap,
					data: {
						show: settingsData.resources.show,
						defaultMode: settingsData.resources.defaultMode,
						label: settingsData.resources.label,
						multiple: settingsData.resources.multiple,
						value: settingsData.resources.value
					},
					resourceList: resourceList,
					autoSelectDefaultValue: this.selectedResourceId,
					changeValueCallback: BX.proxy(function()
					{
						BX.onCustomEvent(this, 'CrmFormResourceBookingFieldLiveController:onResourceChanged', []);
						this.refreshControlsState();
					}, this)
				});

				if (this.selectorCanBeShown('resources'))
				{
					this.resourceControl.display();
				}
			}
		},

		displayServicesControl: function()
		{
			var
				fieldParams = this.getFieldParams(),
				settingsData = this.getSettingsData();

			if (fieldParams.USE_SERVICES === 'Y' && settingsData.services.value)
			{
				var dataValueRaw = BX.type.isArray(settingsData.services.value) ? settingsData.services.value : settingsData.services.value.split('|');

				this.serviceControl = new BX.Calendar.UserField.ViewFormServicesControl({
					outerWrap: this.DOM.innerWrap,
					data: settingsData.services,
					serviceList: fieldParams.SERVICE_LIST,
					selectedValue: (BX.type.isArray(dataValueRaw) && dataValueRaw.length > 0) ? dataValueRaw[0] : null,
					changeValueCallback: BX.proxy(function()
					{
						BX.onCustomEvent(this, 'CrmFormResourceBookingFieldLiveController:onServiceChanged', []);
						this.refreshControlsState();
					}, this)
				});

				if (this.selectorCanBeShown('services'))
				{
					this.serviceControl.display();
				}
			}
		},

		displayDurationControl: function()
		{
			var
				fieldParams = this.getFieldParams(),
				settingsData = this.getSettingsData();

			if (!this.serviceControl)
			{
				this.durationControl = new BX.Calendar.UserField.ViewFormDurationControl({
					outerWrap: this.DOM.innerWrap,
					data: settingsData.duration,
					fullDay: fieldParams.FULL_DAY === 'Y',
					changeValueCallback: BX.proxy(function()
					{
						BX.onCustomEvent(this, 'CrmFormResourceBookingFieldLiveController:onDurationChanged', []);
						this.refreshControlsState();
					}, this)
				});

				if (this.selectorCanBeShown('duration'))
				{
					this.durationControl.display();
				}
			}
		},

		displayDateTimeControl: function()
		{
			var
				startValue = null,
				settingsData = this.getSettingsData(),
				fieldParams = this.getFieldParams();

			this.dateControl = new BX.Calendar.UserField.DateSelector({
				outerWrap: this.DOM.innerWrap,
				data: settingsData.date,
				previewMode: false,
				allowOverbooking: fieldParams.ALLOW_OVERBOOKING === "Y",
				changeValueCallback: BX.proxy(this.handleDateChanging, this),
				requestDataCallback: BX.proxy(this.requestAccessibilityData, this)
			});

			if (this.timeSelectorDisplayed())
			{
				var timezone = false;

				if (fieldParams.USE_USER_TIMEZONE === 'N')
				{
					var userTimezoneOffset = -(new Date).getTimezoneOffset()*60;
					if (userTimezoneOffset !== this.timezoneOffset)
					{
						timezone = fieldParams.TIMEZONE;
					}
				}

				this.timeControl = new BX.Calendar.UserField.TimeSelector({
					outerWrap: this.DOM.innerWrap,
					data: settingsData.time,
					previewMode: false,
					changeValueCallback: BX.proxy(this.handleSelectedDateTimeChanging, this),
					timeFrom: this.timeFrom,
					timeTo: this.timeTo,
					timezone: timezone,
					timezoneOffset: this.timezoneOffset,
					timezoneOffsetLabel: this.timezoneOffsetLabel
				});
			}

			this.statusControl = new BX.Calendar.UserField.ResourceBookingStatusControl({
				outerWrap: this.DOM.innerWrap,
				timezone: timezone,
				timezoneOffsetLabel: this.timezoneOffsetLabel
			});

			if (this.selectorCanBeShown('date'))
			{
				this.statusControl.show();
				if (settingsData.date.start === 'free')
				{
					startValue = this.getFreeDate({
						resources: this.getSelectedResources(),
						user: this.getSelectedUser(),
						duration: this.getCurrentDuration()
					});
				}

				this.dateControl.display({
					selectedValue: startValue
				});

				if (this.timeControl && !this.timeControl.isShown())
				{
					this.timeControl.display();
				}
			}
			else
			{
				this.statusControl.hide();
			}
		},

		handleDateChanging: function(date, realDate)
		{
			BX.onCustomEvent(this, 'CrmFormResourceBookingFieldLiveController:onDateChanged', []);

			if (this.timeSelectorDisplayed())
			{
				if (realDate)
				{
					this.timeControl.show();
					var
						timeValueFrom,
						currentDate = this.getCurrentDate();

					if (currentDate)
					{
						timeValueFrom = currentDate.getHours() * 60 + currentDate.getMinutes();
					}

					this.timeControl.refresh(
						this.getSettingsData().time,
						{
							slotIndex: this.getSlotIndex({date: realDate}),
							currentDate: realDate,
							selectedValue: timeValueFrom
						});

					// this.timeControl.refresh(
					// 	this.getSettingsData().time,
					// 	{
					// 		slotIndex: this.getAvailableSlotIndex({
					// 			date: realDate,
					// 			resources: this.getSelectedResources(),
					// 			user: this.getSelectedUser(),
					// 			duration: this.getCurrentDuration()
					// 		}),
					// 		currentDate: realDate,
					// 		selectedValue: timeValueFrom
					// 	});
				}
			}
			else
			{
				this.handleSelectedDateTimeChanging(null, true);
			}
			this.onChangeValues();
		},

		handleSelectedDateTimeChanging: function(value, useTimeout)
		{
			if (useTimeout !== false)
			{
				if (this.updateTimeStatusTimeout)
				{
					this.updateTimeStatusTimeout = clearTimeout(this.updateTimeStatusTimeout);
				}
				this.updateTimeStatusTimeout = setTimeout(BX.proxy(function(){
					this.handleSelectedDateTimeChanging(value, false);
				}, this), 100);
			}
			else
			{
				if (this.isUserSelectorInAutoMode())
				{
					this.autoAdjustUserSelector();
				}
				if (this.isResourceSelectorInAutoMode())
				{
					this.autoAdjustResourceSelector();
				}

				this.updateStatusControl();
				BX.onCustomEvent(window, 'crmWebFormFireResize');
			}
			this.onChangeValues();
		},

		updateStatusControl: function()
		{
			if (this.statusControl && this.selectorCanBeShown('date'))
			{
				var currentDate = this.getCurrentDate();
				if (this.dateControl.isItPastDate(currentDate))
				{
					this.statusControl.setError(BX.message('WEBF_RES_BOOKING_PAST_DATE_WARNING'));
				}
				else
				{
					if (this.timeSelectorDisplayed())
					{
						if (this.timeControl.hasAvailableSlots())
						{
							var timeValue = this.timeControl.getValue();
							this.statusControl.refresh({
								dateFrom: timeValue ? currentDate : null,
								duration: timeValue ? this.getCurrentDuration() : null,
								fullDay: false
							});
						}
						else
						{
							this.statusControl.hide();
						}
					}
					else
					{
						this.statusControl.refresh({
							dateFrom: this.dateControl.isDateAvailable(currentDate) ? currentDate : null,
							duration: this.getCurrentDuration(),
							fullDay: true
						});
					}
				}
			}
		},

		getFreeDate: function(params)
		{
			var
				slotsAmount = Math.ceil(params.duration / this.scale),
				freeDate = null,
				date = this.loadedDataFrom;

			// Walk through each date searching for free space
			while (true)
			{
				if (this.checkSlotsForDate(date, slotsAmount, {
						resources: params.resources,
						user: params.user
					}))
				{
					freeDate = date;
					break;
				}

				date.setDate(date.getDate() + 1);
				if (date.getTime() >= this.loadedDataTo.getTime())
				{
					break;
				}
			}

			return freeDate;
		},

		getAvailableDateIndex: function(params)
		{
			var
				userIsFree, resourcesAreFree,
				dateIndex = {};

			if (this.timeSelectorDisplayed())
			{
				var slotsAmount = Math.ceil(params.duration / this.scale);

				this.loadedDates.forEach(function(date)
				{
					dateIndex[date.key] = this.checkSlotsForDate(date.key, slotsAmount, {
						resources: params.resources,
						user: params.user
					});
				}, this);
			}
			else
			{
				var
					i, daysGap, date, j,
					userKey = params.user ? 'user' + params.user : null,
 					daysAmount = Math.ceil(params.duration / 1440);

				daysGap = 1;
				for (i = this.loadedDates.length; i--; i >= 0)
				{
					userIsFree = true;
					resourcesAreFree = true;
					date = this.loadedDates[i];

					if (userKey)
					{
						// All day is free for user
						userIsFree = !date.slotsCount[userKey];
					}

					if (userIsFree && params.resources && params.resources.length > 0)
					{
						for (j = 0; j < params.resources.length; j++)
						{
							resourcesAreFree = resourcesAreFree && !date.slotsCount['resource' + params.resources[j]];
							if (!resourcesAreFree)
							{
								break;
							}
						}
					}

					if (userIsFree && resourcesAreFree)
					{
						daysGap++;
					}
					else
					{
						daysGap = 0;
					}

					dateIndex[date.key] = userIsFree && resourcesAreFree && daysAmount <= daysGap;
				}
			}

			return dateIndex;
		},

		getSlotIndex: function(params)
		{
			if (params.date)
			{
				params.date = this.dateControl.getValue();
			}

			var slotIndex = {};

			if (BX.type.isDate(params.date))
			{
				if (this.getFieldParams().ALLOW_OVERBOOKING !== "Y"
					&& (this.isUserSelectorInAutoMode() || this.isResourceSelectorInAutoMode()))
				{
					var
						freeSlot,
						i, j, time,
						settingsData = this.getSettingsData(),
						slotGap = 1,
						todayNowTime = 0,
						timeSlots = this.getTimeSlots(),
						dateKey = BX.date.format(this.DATE_FORMAT, params.date),
						loadedDate = this.loadedDates[this.loadedDatesIndex[dateKey]],
						slotsAmount = Math.ceil(this.getCurrentDuration() / this.scale);

					if (this.checkIsTodayDate(dateKey))
					{
						var today = new Date();
						todayNowTime = today.getHours() * 60 + today.getMinutes();
					}

					// Prefill slotIndex
					timeSlots.forEach(function(slot){slotIndex[slot.time] = true;}, this);

					if (this.isUserSelectorInAutoMode())
					{
						var userList = BX.type.isArray(settingsData.users.value) ? settingsData.users.value : settingsData.users.value.split('|');

						for (i = timeSlots.length; i--; i >= 0)
						{
							time = timeSlots[i].time;
							freeSlot = false;

							if (todayNowTime && time < todayNowTime)
							{
								slotIndex[time] = false;
								continue;
							}

							for (j = 0; j < userList.length; j++)
							{
								if (!loadedDate.slots[time]
									|| !loadedDate.slots[time]['user' + userList[j]])
								{
									freeSlot = true;
									break;
								}
							}

							slotIndex[time] = slotIndex[time] && freeSlot && slotsAmount <= slotGap;
							slotGap = freeSlot ? slotGap + 1 : 1;
						}
					}

					if (this.isResourceSelectorInAutoMode())
					{
						var resList = BX.type.isArray(settingsData.resources.value) ? settingsData.resources.value : settingsData.resources.value.split('|');

						for (i = timeSlots.length; i--; i >= 0)
						{
							time = timeSlots[i].time;
							freeSlot = false;

							if (todayNowTime && time < todayNowTime)
							{
								slotIndex[time] = false;
								continue;
							}

							for (j = 0; j < resList.length; j++)
							{
								if (!loadedDate.slots[time]
									|| !loadedDate.slots[time]['resource' + resList[j]])
								{
									freeSlot = true;
									break;
								}
							}
							slotIndex[time] = slotIndex[time] && freeSlot && slotsAmount <= slotGap;
							slotGap = freeSlot ? slotGap + 1 : 1;
						}
					}
				}
				else
				{
					slotIndex =  this.getAvailableSlotIndex({
						date: params.date || this.dateControl.getValue(),
						resources: this.getSelectedResources(),
						user: this.getSelectedUser(),
						duration: this.getCurrentDuration()
					})
				}
			}

			return slotIndex;
		},

		getAvailableSlotIndex: function(params)
		{
			var
				dateKey, loadedDate, i, j, time,
				todayNowTime = 0,
				slotGap,
				userKey = params.user ? 'user' + params.user : null,
				slotsAmount = Math.ceil(params.duration / this.scale),
				userIsFree, resourcesAreFree,
				timeSlots = this.getTimeSlots(),
				allowOverbooking = this.getFieldParams().ALLOW_OVERBOOKING === "Y",
				slotIndex = {};

			// Prefill slotIndex
			timeSlots.forEach(function(slot){slotIndex[slot.time] = true;}, this);

			if (BX.type.isDate(params.date))
			{
				dateKey = BX.date.format(this.DATE_FORMAT, params.date);
				loadedDate = this.loadedDates[this.loadedDatesIndex[dateKey]];
				slotGap = 1;

				if (this.checkIsTodayDate(dateKey))
				{
					var today = new Date();
					todayNowTime = today.getHours() * 60 + today.getMinutes();
				}

				for (i = timeSlots.length; i--; i >= 0)
				{
					time = timeSlots[i].time;
					if (todayNowTime && time < todayNowTime)
					{
						slotIndex[time] = false;
						continue;
					}

					if (allowOverbooking)
					{
						slotIndex[time] = slotsAmount <= slotGap;
						slotGap++;
					}
					else
					{
						userIsFree = true;
						resourcesAreFree = true;

						if (userKey)
						{
							// Time is free for user
							userIsFree = !loadedDate.slots[time] || !loadedDate.slots[time][userKey];
						}

						if (params.resources && params.resources.length > 0)
						{
							for (j = 0; j < params.resources.length; j++)
							{
								resourcesAreFree = resourcesAreFree && (!loadedDate.slots[time] || !loadedDate.slots[time]['resource' + params.resources[j]]);

								if (!resourcesAreFree)
								{
									break;
								}
							}
						}
						slotIndex[time] = userIsFree && resourcesAreFree && slotsAmount <= slotGap;

						if (userIsFree && resourcesAreFree)
						{
							slotGap++;
						}
						else
						{
							slotGap = 1;
						}
					}
				}
			}

			return slotIndex;
		},

		checkSlotsForDate: function(date, slotsAmount, params)
		{
			var
				userIsFree = true,
				resourcesAreFree = true,
				dateKey = BX.type.isDate(date) ? BX.date.format(this.DATE_FORMAT, date) : date;

			params = params || {};
			if (this.usersDisplayed() && params.user)
			{
				if (this.busySlotMatrix.user[params.user]
					&& !this.entityHasSlotsForDate({
						entityType: 'user',
						entityId: params.user,
						dateKey: dateKey,
						slotsAmount: slotsAmount
					})
				)
				{
					userIsFree = false;
				}
			}

			if (this.resourcesDisplayed() && userIsFree
				&& BX.type.isArray(params.resources) && params.resources.length  > 0)
			{
				params.resources.forEach(function(resourceId)
				{
					if (resourcesAreFree
						&& this.busySlotMatrix.resource[resourceId]
						&& !this.entityHasSlotsForDate({
							entityType: 'resource',
							entityId: resourceId,
							dateKey: dateKey,
							slotsAmount: slotsAmount
						})
					)
					{
						resourcesAreFree = false;
					}
				}, this);
			}

			return userIsFree && resourcesAreFree;
		},

		entityHasSlotsForDate: function(params)
		{
			var
				busySlotList,
				slots, i,
				freeSlotCount = 0,
				hasFreeSlots = false;

			if (this.busySlotMatrix[params.entityType][params.entityId] &&
				this.busySlotMatrix[params.entityType][params.entityId][params.dateKey])
			{
				busySlotList = this.busySlotMatrix[params.entityType][params.entityId][params.dateKey];
				slots = this.getTimeSlots();
				for (i = 0; i < slots.length; i++)
				{
					if (!busySlotList[slots[i].time])
					{
						freeSlotCount++;
						if (freeSlotCount >= params.slotsAmount)
						{
							hasFreeSlots = true;
							break;
						}
					}
					else
					{
						freeSlotCount = 0;
					}
				}
			}
			else
			{
				hasFreeSlots = true;
			}

			return hasFreeSlots;
		},

		getSelectedResources: function()
		{
			var result = null;
			if (this.resourceControl)
			{
				result = this.resourceControl.getSelectedValues();
				if (BX.type.isArray(result) && !result.length)
				{
					result = null;
				}
			}
			return result;
		},


		getSelectedUser: function()
		{
			var result = null;
			if (this.userControl)
			{
				result = this.userControl.getSelectedUser();
			}
			return result;
		},

		getCurrentDuration: function()
		{
			var result = null;
			if (this.durationControl)
			{
				result = this.durationControl.getSelectedValue();
			}
			else if (this.serviceControl)
			{
				var service = this.serviceControl.getSelectedService(true);
				if (service && service.duration)
				{
					result = parseInt(service.duration);
				}
			}
			return result;
		},

		getDefaultDurationSlotsAmount: function()
		{
			var
				settingsData = this.getSettingsData(),
				fieldParams = this.getFieldParams(),
				duration, i, slotsAmount,
				prepareServiceId = function(str) {return BX.translit(str).replace(/[^a-z0-9_]/ig, "_");};

			if (fieldParams.USE_SERVICES === 'Y' && settingsData.services.value)
			{
				var services = settingsData.services.value.split('|');
				if (BX.type.isArray(fieldParams.SERVICE_LIST)
					&& BX.type.isArray(services)
					&& services.length > 0
				)
				{
					for (i = 0; i < fieldParams.SERVICE_LIST.length; i++)
					{
						if (prepareServiceId(fieldParams.SERVICE_LIST[i].name) === services[0])
						{
							duration = parseInt(fieldParams.SERVICE_LIST[i].duration);
							break;
						}
					}
				}
			}
			else
			{
				duration = parseInt(settingsData.duration.defaultValue);
			}

			slotsAmount = Math.ceil(duration / this.scale);
			return slotsAmount;
		},

		getCurrentServiceName: function()
		{
			var result = '';
			if (this.serviceControl)
			{
				var service = this.serviceControl.getSelectedService(true);
				if (service && service.name)
				{
					result = service.name;
				}
			}
			return result;
		},

		getCurrentDate: function()
		{
			var result = null;
			if (this.dateControl && this.dateControl.isShown())
			{
				result = this.dateControl.getValue();
				if (this.timeSelectorDisplayed())
				{
					var
						hour, min,
						timeValue = this.timeControl.getValue();

					if (timeValue)
					{
						hour = Math.floor(timeValue / 60);
						min = timeValue - hour * 60;
						result.setHours(hour, min, 0, 0);
					}
				}
				else
				{
					result.setHours(0, 0, 0, 0);
				}
			}

			return result;
		},

		getTimeSlots: function()
		{
			if (!this.slots)
			{
				this.slots = [];
				var slot,
					finishTime, hourFrom, minFrom, hourTo,
					minTo,
					num = 0,
					time = this.timeFrom * 60;

				while (time < this.timeTo * 60)
				{
					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
					};

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

			return this.slots;
		},

		usersDisplayed: function()
		{
			if (this.useUsers === undefined)
			{
				this.useUsers = !!(this.getFieldParams()['USE_USERS'] === 'Y' && this.getSettingsData().users.value);
			}
			return this.useUsers;
		},

		resourcesDisplayed: function()
		{
			if (this.useResources === undefined)
			{
				var fieldParams = this.getFieldParams();
				this.useResources = !!(fieldParams.USE_RESOURCES === 'Y'
					&& fieldParams.SELECTED_RESOURCES
					&& this.getSettingsData().resources.value);
			}
			return this.useResources;
		},

		timeSelectorDisplayed: function()
		{
			if (this.useTime === undefined)
			{
				this.useTime = this.getFieldParams().FULL_DAY !== 'Y';
			}
			return this.useTime;
		},

		selectorCanBeShown: function(type)
		{
			var result = false;
			if (type === 'resources')
			{
				if (this.resourcesDisplayed() && !this.usersDisplayed())
				{
					result = true;
				}
				else if (this.usersDisplayed())
				{
					result = this.getSelectedUser();
				}
			}
			else if (type === 'date' || type === 'services' || type === 'duration')
			{
				if (this.usersDisplayed() && this.resourcesDisplayed())
				{
					result = this.getSelectedUser() && this.getSelectedResources();
				}
				else if (this.usersDisplayed())
				{
					result = this.getSelectedUser();
				}
				else if (this.resourcesDisplayed())
				{
					result = this.getSelectedResources();
				}
			}
			return result;
		},

		checkIsTodayDate: function(dateKey)
		{
			if (!this.todayDateKey)
			{
				var today = new Date();
				this.todayDateKey = BX.date.format(this.DATE_FORMAT, today);
			}
			return this.todayDateKey === dateKey;
		}
	};
	// endregion
})();

Anon7 - 2022
AnonSec Team