Your IP : 3.21.46.31


Current Path : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/js/im/call/
Upload File :
Current File : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/js/im/call/view.js

;(function()
{
	BX.namespace('BX.Call');

	if (BX.Call.View)
	{
		return;
	}

	var Layouts = {
		Grid: 1,
		Centered: 2
	};

	var UiState = {
		Preparing: 1,
		Initializing: 2,
		Calling: 3,
		Connected: 4,
		Error: 5
	};

	var EventName = {
		onClose: 'onClose',
		onDestroy: 'onDestroy',
		onButtonClick: 'onButtonClick',
		onBodyClick: 'onBodyClick',
		onReplaceCamera: 'onReplaceCamera',
		onReplaceMicrophone: 'onReplaceMicrophone',
		onReplaceSpeaker: 'onReplaceSpeaker',
		onSetCentralUser: 'onSetCentralUser',
		onLayoutChange: 'onLayoutChange',
	};

	var localUserPosition = 1000;
	var addButtonPosition = 1001;

	/**
	 *
	 * @param config
	 * @constructor
	 */
	BX.Call.View = function(config)
	{
		this.title = config.title;
		this.container = config.container;
		this.cameraId = config.cameraId;
		this.microphoneId = config.microphoneId;
		this.speakerId = BX.Call.Hardware.defaultSpeaker;
		this.speakerMuted = false;
		this.showChatButtons = (config.showChatButtons === true);
		this.showShareButton = (config.showShareButton === true);
		this.showButtonPanel = (config.showButtonPanel !== false);

		this.language = config.language || '';

		this.lastPosition = 1;

		this.userData = {};
		if(config.userData)
		{
			this.updateUserData(config.userData);
		}
		this.userLimit = config.userLimit || 1;
		this.userId = BX.message('USER_ID');
		this.localUser = new CallUser({
			id: this.userId,
			state: BX.Call.UserState.Connected,
			localUser: true,
			order: localUserPosition,
			name: this.userData[this.userId] ? this.userData[this.userId].name : '',
			avatar: this.userData[this.userId] ? this.userData[this.userId].avatar_hr : '',
		});

		this.mediaSelectionBlocked = false;

		this.visible = false;
		this.elements = {
			root: null,
			watermark: null,
			container: null,
			overlay: null,
			panel: null,
			audioContainer: null,
			audio: {
				// userId: <audio> for this user's stream
			},
			center: null,
			userBlock: null,
			ear: {
				left: null,
				right: null
			},
			userList: {
				container: null,
				addButton: null
			},
		};

		this.buttons = {
			title: null,
			grid: null,
			add: null,
			share: null,
			microphone: null,
			camera: null,
			speaker: null,
			screen: null,
			chat: null,
			history: null,
			hangup: null,
			fullscreen: null,
			overlay: null,
			status: null
		};

		this.size = BX.Call.View.Size.Full;
		this.isMuted = false;
		this.isCameraOn = false;
		this.isFullScreen = false;

		this.disabledButtons = {};

		this.uiState = config.uiState || UiState.Calling;
		this.layout = config.layout || Layouts.Centered;

		this.users = {}; // Call participants. The key is the user id.

		if(BX.type.isPlainObject(config.userStates))
		{
			this.appendUsers(config.userStates);
		}

		this.eventEmitter = new BX.Event.EventEmitter(this, 'BX.Call.View');

		this.scrollInterval = 0;

		// Event handlers
		this._onFullScreenChangeHandler = this._onFullScreenChange.bind(this);
		//this._onResizeHandler = BX.throttle(this._onResize.bind(this), 500);
		this._onResizeHandler = this._onResize.bind(this);

		this.resizeObserver = new BX.ResizeObserver(this._onResizeHandler);

		// timers
		this.switchPresenterTimeout = 0;

		this.init();
		this.subscribeEvents(config);
	};

	BX.Call.View.prototype.init = function()
	{
		if(this.isFullScreenSupported())
		{
			if (BX.browser.IsChrome() || BX.browser.IsSafari())
			{
				window.addEventListener("webkitfullscreenchange", this._onFullScreenChangeHandler);
			}
			else if (BX.browser.IsFirefox())
			{
				window.addEventListener("mozfullscreenchange", this._onFullScreenChangeHandler);
			}
		}

		this.elements.audioContainer = BX.create("div", {
			props: {className: "bx-messenger-videocall-audio-container"}
		});

		this.container.appendChild(this.elements.audioContainer);
	};

	BX.Call.View.prototype.subscribeEvents = function(config)
	{
		for (var event in EventName)
		{
			if(EventName.hasOwnProperty(event) && BX.type.isFunction(config[event]))
			{
				this.setCallback(event, config[event]);
			}
		}
	};

	BX.Call.View.prototype.setCallback = function(name, cb)
	{
		if(BX.type.isFunction(cb) && EventName.hasOwnProperty(name))
		{
			this.eventEmitter.subscribe(name, function(event) {
				cb(event.data);
			});
		}
	};

	BX.Call.View.prototype.subscribe = function(eventName, listener)
	{
		return this.eventEmitter.subscribe(eventName, listener);
	};

	BX.Call.View.prototype.unsubscribe = function(eventName, listener)
	{
		return this.eventEmitter.unsubscribe(eventName, listener);
	};

	BX.Call.View.prototype.getNextPosition = function()
	{
		return this.lastPosition++;
	};

	/**
	 * @param {object} userStates {userId -> state}
	 */
	BX.Call.View.prototype.appendUsers = function(userStates)
	{
		if(!BX.type.isPlainObject(userStates))
		{
			return;
		}

		var userIds = Object.keys(userStates);

		for (var i = 0; i < userIds.length; i++)
		{
			var userId = userIds[i];
			this.users[userId] = new CallUser({
				id: userId,
				state : userStates[userId] ? userStates[userId] : BX.Call.UserState.Idle,
				order: this.getNextPosition(),
				onClick: this._onUserClick.bind(this),
				name: this.userData[userId] ? this.userData[userId].name : '',
				avatar: this.userData[userId] ? this.userData[userId].avatar_hr : '',
			});
		}
	};

	BX.Call.View.prototype.setCentralUser = function(userId)
	{
		if (this.centralUser.userId == userId)
		{
			return;
		}
		if (userId == this.userId && this.getUsersWithVideo().length > 0)
		{
			return;
		}
		if (!this.users[userId] && userId != this.userId)
		{
			return;
		}

		this.centralUser.setUserId(userId, this.userData[userId] || {});
		if(this.layout == Layouts.Centered)
		{
			this.updateUserList();
		}
		this.eventEmitter.emit(EventName.onSetCentralUser, {
			userId: userId,
			stream: userId == this.userId ? this.localUser.stream : this.users[userId].stream
		})
	};

	BX.Call.View.prototype.getUserCount = function()
	{
		return Object.keys(this.users).length;
	};

	BX.Call.View.prototype.getConnectedUserCount = function()
	{
		return this.getConnectedUsers().length;
	};

	BX.Call.View.prototype.getUsersWithVideo = function()
	{
		var result = [];

		for (var userId in this.users)
		{
			if(this.users[userId].hasVideo())
			{
				result.push(userId);
			}
		}
		return result;
	};

	BX.Call.View.prototype.getConnectedUsers = function()
	{
		var result = [];
		for (var userId in this.users)
		{
			if(this.users[userId].state == BX.Call.UserState.Connected)
			{
				result.push(userId);
			}
		}
		return result;
	};

	BX.Call.View.prototype.getPresenterUserId = function()
	{
		var currentPresenterId = this.layout === BX.Call.View.Layout.Centered ? parseInt(this.centralUser.userId, 10) : 0;
		var userId; // for usage in iterators

		// 1. Current user, who is sharing screen has top priority
		if (currentPresenterId && this.users[currentPresenterId].screenState === true)
		{
			return currentPresenterId;
		}

		// 2. If current user is not sharing screen, but someone is sharing - he should become presenter
		for (userId in this.users)
		{
			if(this.users.hasOwnProperty(userId) && this.users[userId].screenState === true)
			{
				return parseInt(userId, 10);
			}
		}

		// 3. If current user is talking, or stopped talking less then one second ago - he should stay presenter
		if (currentPresenterId && this.users[currentPresenterId].wasTalkingAgo() < 1000)
		{
			return currentPresenterId;
		}

		// 4. Return currently talking user
		for (userId in this.users)
		{
			if(this.users.hasOwnProperty(userId) && this.users[userId].wasTalkingAgo() < 1000)
			{
				return parseInt(userId, 10);
			}
		}

		// 5. Return last talking user
		var maxTalkingTime = 0;
		var maxTalkingTimeUserId = 0;
		for (userId in this.users)
		{
			if(this.users.hasOwnProperty(userId) && this.users[userId].talkingStop > maxTalkingTime)
			{
				maxTalkingTime = this.users[userId].talkingStop;
				maxTalkingTimeUserId = parseInt(userId, 10);
			}
		}
		return maxTalkingTimeUserId;
	};

	BX.Call.View.prototype.switchPresenter = function()
	{
		var newPresenterId = this.getPresenterUserId();

		if (!newPresenterId || newPresenterId == this.centralUser.userId)
		{
			return;
		}

		this.setCentralUser(newPresenterId);
	};

	BX.Call.View.prototype.switchPresenterDeferred = function()
	{
		clearTimeout(this.switchPresenterTimeout);
		this.switchPresenterTimeout = setTimeout(this.switchPresenter.bind(this), 1000);
	};

	BX.Call.View.prototype.cancelSwitchPresenter = function()
	{
		clearTimeout(this.switchPresenterTimeout);
	};

	BX.Call.View.prototype.setUiState = function(uiState)
	{
		if(this.uiState == uiState)
		{
			return;
		}

		this.uiState = uiState;
		if(this.uiState == UiState.Error && this.elements.container)
		{
			this.elements.container.textContent = '';
		}
		if(this.elements.root)
		{
			this.updateButtons();
		}
	};

	BX.Call.View.prototype.setLayout = function(newLayout)
	{
		if(newLayout == this.layout)
		{
			return;
		}

		this.layout = newLayout;

		if(this.layout == Layouts.Centered)
		{
			this.elements.root.classList.remove("bx-messenger-videocall-grid");
			this.elements.root.classList.add("bx-messenger-videocall-centered");

			this.elements.userBlock.appendChild(this.elements.userList.container);
			this.elements.container.appendChild(this.elements.center);
			this.elements.container.appendChild(this.elements.userBlock);

			this.switchPresenter();
			this.centralUser.playVideo();
			this.centralUser.updateAvatarWidth();
		}
		else if (this.layout == Layouts.Grid)
		{
			this.elements.root.classList.remove("bx-messenger-videocall-centered");
			this.elements.root.classList.add("bx-messenger-videocall-grid");

			this.elements.container.removeChild(this.elements.center);
			this.elements.container.removeChild(this.elements.userBlock);
			this.elements.container.appendChild(this.elements.userList.container);
		}
		this.renderUserList();
		this.toggleEars();
		this.eventEmitter.emit(EventName.onLayoutChange, {
			layout: this.layout
		});
	};

	BX.Call.View.prototype.setCameraState = function(newCameraState)
	{
		newCameraState = !!newCameraState;
		if(this.isCameraOn == newCameraState)
		{
			return;
		}

		this.isCameraOn = newCameraState;

		if(this.buttons.camera)
		{
			if(this.isCameraOn)
			{
				this.buttons.camera.enable();
			}
			else
			{
				this.buttons.camera.disable();
			}
		}
	};

	BX.Call.View.prototype.setMuted = function(isMuted)
	{
		isMuted = !!isMuted;
		if(this.isMuted == isMuted)
		{
			return;
		}

		this.isMuted = isMuted;
		if(this.buttons.microphone)
		{
			if(this.isMuted)
			{
				this.buttons.microphone.disable();
			}
			else
			{
				this.buttons.microphone.enable();
			}
		}

		this.localUser.setMicrophoneState(!isMuted);
	};

	BX.Call.View.prototype.addUser = function(userId, state)
	{
		if(this.users[userId])
		{
			return;
		}

		this.users[userId] = new CallUser({
			id: userId,
			state : state || BX.Call.UserState.Idle,
			order: this.getNextPosition(),
			onClick: this._onUserClick.bind(this),
			name: this.userData[userId] ? this.userData[userId].name : '',
			avatar: this.userData[userId] ? this.userData[userId].avatar_hr : '',
		});

		this.updateUserList();
		this.updateButtons();
	};

	BX.Call.View.prototype.setUserState = function(userId, newState)
	{
		/** @type {CallUser} */
		var user = this.users[userId];
		if(!user)
		{
			return;
		}

		user.setState(newState);

		// maybe switch central user
		if(this.centralUser.userId == this.userId && newState == BX.Call.UserState.Connected)
		{
			this.setCentralUser(userId);
		}
		else if(userId == this.centralUser.userId)
		{
			if(newState == BX.Call.UserState.Connecting || newState == BX.Call.UserState.Failed)
			{
				this.centralUser.blurVideo();
			}
			else if (newState == BX.Call.UserState.Connected)
			{
				this.centralUser.blurVideo(false);
			}
			else if (newState == BX.Call.UserState.Idle)
			{
				var usersWithVideo = this.getUsersWithVideo();
				var connectedUsers = this.getConnectedUsers();
				if (usersWithVideo.length > 0)
				{
					this.setCentralUser(usersWithVideo[0]);
				}
				else if (connectedUsers.length > 0)
				{
					this.setCentralUser(connectedUsers[0]);
				}
				else
				{
					this.centralUser.blurVideo();
				}
			}
		}

		this.updateUserList();
		this.updateButtons();
	};

	BX.Call.View.prototype.setTitle = function(title)
	{
		this.title = title;
	};

	BX.Call.View.prototype.setUserTalking = function(userId, talking)
	{
		/** @type {CallUser} */
		var user = this.users[userId];
		if(!user)
		{
			return;
		}

		user.setTalking(talking);

		if (this.centralUser.userId == userId && !talking)
		{
			this.switchPresenterDeferred();
		}
		else
		{
			this.switchPresenter();
		}
	};

	BX.Call.View.prototype.setUserMicrophoneState = function(userId, isMicrophoneOn)
	{
		/** @type {CallUser} */
		var user = this.users[userId];
		if(!user)
		{
			return;
		}

		user.setMicrophoneState(isMicrophoneOn);
	};

	BX.Call.View.prototype.setUserScreenState = function(userId, screenState)
	{
		/** @type {CallUser} */
		var user = this.users[userId];
		if(!user)
		{
			return;
		}

		user.setScreenState(screenState);

		if(screenState === true && this.layout === BX.Call.View.Layout.Grid)
		{
			this.setLayout(BX.Call.View.Layout.Centered);
		}
		this.switchPresenter();
	};

	BX.Call.View.prototype.setLocalStream = function(mediaStream, flipVideo)
	{
		this.localUser.stream = mediaStream;
		this.localUser.flipVideo = !!flipVideo;
		this.setCameraState(this.localUser.hasVideo());

		var videoTracks = mediaStream.getVideoTracks();
		if(videoTracks.length > 0)
		{
			var videoTrackSettings = videoTracks[0].getSettings();
			this.cameraId = videoTrackSettings.deviceId || '';
		}
		else
		{
			this.cameraId = '';
		}

		var audioTracks = mediaStream.getAudioTracks();
		if(audioTracks.length > 0)
		{
			var audioTrackSettings = audioTracks[0].getSettings();
			this.microphoneId = audioTrackSettings.deviceId || '';
		}

		/*if(!this.localUser.hasVideo())
		{
			return false;
		}*/

		if(this.layout == Layouts.Centered && this.centralUser.userId == this.userId)
		{
			if(this.localUser.hasVideo() || Object.keys(this.users).length === 0)
			{
				this.centralUser.setStream(mediaStream);
			}
			else
			{
				this.setCentralUser(Object.keys(this.users)[0]);
			}
		}
		else
		{
			this.updateUserList();
		}
	};

	BX.Call.View.prototype.setSpeakerId = function(speakerId)
	{
		if (!('setSinkId' in HTMLMediaElement.prototype))
		{
			console.error("Speaker selection is not supported");
		}

		this.speakerId = speakerId;
		for (var userId in this.elements.audio)
		{
			this.elements.audio[userId].setSinkId(this.speakerId);
		}
	};

	BX.Call.View.prototype.muteSpeaker = function(mute)
	{
		this.speakerMuted = !!mute;

		for (var userId in this.elements.audio)
		{
			this.elements.audio[userId].volume = this.speakerMuted ? 0 : 1;
		}

		if(this.speakerMuted)
		{
			this.buttons.speaker.disable();
			this.buttons.speaker.hideArrow()
		}
		else
		{
			this.buttons.speaker.enable();
			if(BX.Call.Hardware.canSelectSpeaker())
			{
				this.buttons.speaker.showArrow()
			}
		}
	};

	BX.Call.View.prototype.setStream = function(userId, mediaStream)
	{
		if(this.uiState == UiState.Calling)
		{
			this.setUiState(UiState.Connected);
		}

		if(!this.users[userId])
		{
			throw Error("User " + userId + " is not a part of this call");
		}

		if(!this.elements.audio[userId])
		{
			this.elements.audio[userId] = BX.create("audio");
			this.elements.audioContainer.appendChild(this.elements.audio[userId]);
		}

		this.elements.audio[userId].volume = this.speakerMuted ? 0 : 1;

		if(mediaStream.getAudioTracks().length > 0 && mediaStream != this.elements.audio[userId].srcObject)
		{
			if(this.speakerId && this.elements.audio[userId].setSinkId)
			{
				this.elements.audio[userId].setSinkId(this.speakerId).then(function()
				{
					this.elements.audio[userId].srcObject = mediaStream;
					this.elements.audio[userId].play().catch(BX.DoNothing);
				}.bind(this)).catch(console.error);
			}
			else
			{
				this.elements.audio[userId].srcObject = mediaStream;
				this.elements.audio[userId].play().catch(BX.DoNothing);
			}
		}

		this.users[userId].stream = mediaStream;

		if(this.users[userId].hasVideo())
		{
			if(this.centralUser.userId == this.userId)
			{
				this.setCentralUser(userId);
			}
		}
		else
		{
			// no video
			if(this.centralUser.userId == userId)
			{
				var usersWithVideo = this.getUsersWithVideo();
				if(usersWithVideo.length > 0)
				{
					this.setCentralUser(usersWithVideo[0]);
				}
				/*else if (this.localUser.hasVideo())
				{
					this.setCentralUser(this.userId);
				}*/
			}
		}
		if(this.centralUser.userId == userId)
		{
			this.centralUser.setStream(mediaStream);
		}

		this.updateUserList();
	};

	BX.Call.View.prototype.show = function()
	{
		if(!this.elements.root)
		{
			this.render();
		}
		this.container.appendChild(this.elements.root);

		this.updateButtons();
		this.updateUserList();
		this.resumeVideo();

		this.toggleEars();
		this.visible = true;
	};

	BX.Call.View.prototype.hide = function()
	{
		BX.remove(this.elements.root);
		this.visible = false;
	};

	/**
	 * @param {Object} params
	 * @param {string} params.text
	 * @param {string} [params.subText]
	 */
	BX.Call.View.prototype.showMessage = function(params)
	{
		if(!this.elements.root)
		{
			this.render();
			this.container.appendChild(this.elements.root);
		}
		var statusNode = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-status bx-messenger-videocall-user-status-wide"},
		});

		if(BX.type.isNotEmptyString(params.text))
		{
			var textNode = BX.create("div", {
				props: {className: "bx-messenger-videocall-status-text"},
				text: params.text
			});
			statusNode.appendChild(textNode);
		}

		if(this.elements.overlay.childElementCount)
		{
			BX.cleanNode(this.elements.overlay);
		}
		this.elements.overlay.appendChild(statusNode);
	};

	BX.Call.View.prototype.hideMessage = function()
	{
		this.elements.overlay.textContent = '';
	};

	/**
	 * @param {Object} params
	 * @param {string} params.text
	 * @param {string} [params.subText]
	 */
	BX.Call.View.prototype.showFatalError = function(params)
	{
		this.showMessage(params);
		this.setUiState(UiState.Error);
	};

	BX.Call.View.prototype.close = function()
	{
		BX.cleanNode(this.container);

		this.visible = false;
		this.eventEmitter.emit(EventName.onClose);
	};

	BX.Call.View.prototype.setSize = function(size)
	{
		if(this.size == size)
		{
			return;
		}

		this.size = size;

		if(this.size == BX.Call.View.Size.Folded)
		{
			if(this.elements.panel)
			{
				this.elements.panel.classList.add('bx-messenger-videocall-panel-folded');
			}
			BX.remove(this.elements.container);
			BX.remove(this.elements.watermark);
			this.updateButtons();
		}
		else
		{
			if(this.elements.panel)
			{
				this.elements.panel.classList.remove('bx-messenger-videocall-panel-folded');
			}
			this.elements.wrap.appendChild(this.elements.watermark);
			this.elements.wrap.appendChild(this.elements.container);

			this.updateButtons();
			this.updateUserList();
			this.resumeVideo();
		}
	};

	BX.Call.View.prototype.toggleFullScreen = function()
	{
		if(this.isFullScreen)
		{
			this.exitFullScreen();
		}
		else
		{
			this.enterFullScreen();
		}
	};

	BX.Call.View.prototype.isButtonDisabled = function(buttonName)
	{
		return this.disabledButtons.hasOwnProperty(buttonName);
	};

	BX.Call.View.prototype.disableAddUser = function()
	{
		this.disabledButtons['add'] = true;

		if(this.elements.userList.addButton)
		{
			BX.remove(this.elements.userList.addButton);
			this.elements.userList.addButton = null;
		}
	};

	BX.Call.View.prototype.disableSwitchCamera = function()
	{
		this.disabledButtons['camera'] = true;
	};

	BX.Call.View.prototype.enableSwitchCamera = function()
	{
		delete this.disabledButtons['camera'];
	};

	BX.Call.View.prototype.disableScreenSharing = function()
	{
		this.disabledButtons['screen'] = true;
	};

	BX.Call.View.prototype.disableHistoryButton = function()
	{
		this.disabledButtons['history'] = true;
	};

	BX.Call.View.prototype.disableMediaSelection = function()
	{
		this.mediaSelectionBlocked = true;
	};

	BX.Call.View.prototype.isMediaSelectionAllowed = function()
	{
		return (this.uiState == UiState.Preparing || this.uiState == UiState.Connected) && !this.mediaSelectionBlocked && !this.isFullScreen;
	};

	BX.Call.View.prototype.getButtonList = function()
	{
		if(this.uiState == UiState.Error)
		{
			return ['close'];
		}
		if(this.uiState == UiState.Initializing)
		{
			return ['hangup'];
		}

		if(this.size == BX.Call.View.Size.Folded)
		{
			return ['title', 'hangup', 'fullscreen'];
		}

		var result = [];

		if(this.uiState === UiState.Connected)
		{
			result.push('grid');
		}

		if(this.getConnectedUserCount() < this.userLimit - 1 && !this.isFullScreen && this.uiState === UiState.Connected)
		{
			result.push('add');
		}

		if(this.showShareButton && !this.isFullScreen)
		{
			result.push('share');
		}

		result.push('microphone');
		if(this.uiState === UiState.Preparing || this.uiState === UiState.Connected)
		{
			result.push('camera');
		}

		result.push('speaker');

		if(this.isScreenSharingSupported() && !this.isFullScreen && this.uiState === UiState.Connected)
		{
			result.push('screen')
		}

		if(this.showChatButtons && !this.isFullScreen)
		{
			result.push('chat');
			if(this.uiState != UiState.Preparing)
			{
				result.push('history');
			}
		}

		if(this.uiState == UiState.Preparing)
		{
			result.push('close');
		}
		else
		{
			result.push('hangup');
		}
		if(this.uiState != UiState.Preparing && this.isFullScreenSupported())
		{
			result.push('fullscreen');
		}

		result = result.filter(function(buttonName)
		{
			return !this.isButtonDisabled(buttonName);
		}, this);

		return result;
	};

	BX.Call.View.prototype.getWatermarkUrl = function(language)
	{
		switch (language)
		{
			case 'ua':
				return '/bitrix/js/im/images/watermark-white-ua.svg';
			case 'ru':
			case 'kz':
			case 'by':
				return '/bitrix/js/im/images/watermark-white-ru.svg';
			default:
				return '/bitrix/js/im/images/watermark-white-en.svg';
		}
	};

	BX.Call.View.prototype.render = function()
	{
		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall"},
			children: [
				this.elements.wrap = BX.create("div", {
					props: {className: "bx-messenger-videocall-wrap"},
					children: [
						this.elements.watermark = BX.create("div", {
							props: {className: "bx-messenger-videocall-watermark"},
							children: [
								BX.create("img", {
									props: {
										className: "bx-messenger-videocall-watermark-img",
										src: this.getWatermarkUrl(this.language)
									},
								})
							]
						}),
						this.elements.container = BX.create("div", {
							props: {className: "bx-messenger-videocall-inner"},
						}),
						this.elements.overlay = BX.create("div", {
							props: {className: "bx-messenger-videocall-overlay"}
						}),
					]
				})
			],
			events: {
				click: this._onBodyClick.bind(this)
			}
		});

		if(this.showButtonPanel)
		{
			this.elements.panel = BX.create("div", {
				props: {className: "bx-messenger-videocall-panel"},
			});
			this.elements.wrap.appendChild(this.elements.panel);
		}
		else
		{
			this.elements.root.classList.add("bx-messenger-videocall-no-button-panel");
		}

		this.centralUser  = new CentralUser({
			parent: this,
			video: false,
			stream: false,
			userId: this.userId, // Show local user until someone is connected,
			language: this.language
		});

		this.elements.center = this.centralUser.render();

		this.elements.userBlock = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-block"},
			children: [
				this.elements.ear.left = BX.create("div", {
					props: {className: "bx-messenger-videocall-ear bx-messenger-videocall-ear-left"},
					events: {
						mouseenter: this.scrollUserBlockLeft.bind(this),
						mouseleave: this.stopScroll.bind(this)
					}
				}),
				this.elements.ear.right = BX.create("div", {
					props: {className: "bx-messenger-videocall-ear bx-messenger-videocall-ear-right"},
					events: {
						mouseenter: this.scrollUseBlockRight.bind(this),
						mouseleave: this.stopScroll.bind(this)
					}
				})
			]
		});

		this.elements.userList.container = BX.create("div", {
			props: {
				className: "bx-messenger-videocall-user-list"
			},
			children: [
				BX.create("div", {
					props: {className: "bx-messenger-videocall-user-extra"}
				})
			],
			events: {
				scroll: this.toggleEars.bind(this)
			}
		});

		this.elements.userList.addButton = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-add"},
			children: [
				BX.create("div", {
					props: {className: "bx-messenger-videocall-user-add-inner"}
				})
			],
			style: {
				order: addButtonPosition
			},
			events: {
				click: this._onAddButtonClick.bind(this)
			}
		});

		if(this.layout == Layouts.Centered)
		{
			this.elements.container.appendChild(this.elements.center);
			this.elements.container.appendChild(this.elements.userBlock);
			this.elements.root.classList.add("bx-messenger-videocall-centered");
		}
		else if (this.layout == Layouts.Grid)
		{
			this.elements.root.classList.add("bx-messenger-videocall-grid");
		}

		this.resizeObserver.observe(this.elements.root);
		return this.elements.root;
	};

	BX.Call.View.prototype.renderUserList = function()
	{
		var containerSize = this.elements.userList.container.getBoundingClientRect();
		var showLocalUser = this.localUser.hasVideo() && (this.layout == Layouts.Grid || this.centralUser.userId != this.userId);
		var userCount = 0;

		for (var userId in this.users)
		{
			/** @type {CallUser} */
			var user = this.users[userId];
			if(this.layout == Layouts.Centered && userId == this.centralUser.userId)
			{
				user.dismount();
				continue;
			}
			if(user.state == BX.Call.UserState.Idle
				|| user.state == BX.Call.UserState.Declined
				|| user.state == BX.Call.UserState.Unavailable
				|| user.state == BX.Call.UserState.Busy
			)
			{
				user.dismount();
				continue;
			}
			user.mount(this.elements.userList.container);
			userCount++;
		}
		if(showLocalUser)
		{
			// using force true to always move self to the end of the list
			this.localUser.mount(this.elements.userList.container, true);
			userCount++;
		}
		else
		{
			this.localUser.dismount();
		}

		if (this.layout == Layouts.Grid)
		{
			var userSize = BX.Call.Util.findBestElementSize(containerSize.width, containerSize.height, userCount);

			this.elements.userList.container.style.setProperty('--grid-user-width', userSize.width + 'px');
			this.elements.userList.container.style.setProperty('--grid-user-height', userSize.height + 'px');
		}

		var showAdd = this.layout == Layouts.Centered && userCount > 0 && !this.isFullScreen && this.uiState === UiState.Connected && !this.isButtonDisabled("add") && this.getConnectedUserCount() < this.userLimit - 1;
		if (showAdd && !this.isFullScreen)
		{
			this.elements.userList.container.appendChild(this.elements.userList.addButton);
		}
		else
		{
			BX.remove(this.elements.userList.addButton);
		}
	};

	/**
	 * @return {Element}
	 */
	BX.Call.View.prototype.renderButtons = function(buttons)
	{
		var panelInner;
		var leftSubPanel;
		var middleSubPanel;
		var rightSubPanel;

		panelInner = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-inner"},
			children: [
				leftSubPanel = BX.create("div", {
					props: {className: "bx-messenger-videocall-panel-block"},
				}),
				middleSubPanel = BX.create("div", {
					props: {className: "bx-messenger-videocall-panel-block"},
				}),
				rightSubPanel = BX.create("div", {
					props: {className: "bx-messenger-videocall-panel-block"},
				}),
			]
		});

		for (var i = 0; i < buttons.length; i++)
		{
			switch (buttons[i])
			{
				case "title":
					this.buttons.title = new TitleButton({
						text: this.title,
						isGroupCall: Object.keys(this.users).length > 1
					});
					leftSubPanel.appendChild(this.buttons.title.render());
					break;
				case "grid":
					this.buttons.grid = new SimpleButton({
						class: "grid",
						text: BX.message("IM_M_CALL_BTN_GRID"),
						onClick: this._onGridButtonClick.bind(this)
					});
					leftSubPanel.appendChild(this.buttons.grid.render());
					break;
				case "add":
					this.buttons.add = new SimpleButton({
						class: "add",
						text: BX.message("IM_M_CALL_BTN_ADD"),
						onClick: this._onAddButtonClick.bind(this)
					});
					leftSubPanel.appendChild(this.buttons.add.render());
					break;
				case "share":
					this.buttons.share = new SimpleButton({
						class: "share",
						text: BX.message("IM_M_CALL_BTN_LINK"),
						onClick: this._onShareButtonClick.bind(this)
					});
					leftSubPanel.appendChild(this.buttons.share.render());
					break;
				case "microphone":
					this.buttons.microphone = new ButtonWithArrow({
						class: "microphone",
						text: BX.message("IM_M_CALL_BTN_MIC"),
						enabled: !this.isMuted,
						arrowEnabled: this.isMediaSelectionAllowed(),
						onClick: this._onMicrophoneButtonClick.bind(this),
						onArrowClick: this._onMicrophoneArrowClick.bind(this)
					});
					middleSubPanel.appendChild(this.buttons.microphone.render());
					break;
				case "camera":
					this.buttons.camera = new ButtonWithArrow({
						class: "camera",
						text: BX.message("IM_M_CALL_BTN_CAMERA"),
						enabled: this.isCameraOn,
						arrowEnabled: this.isMediaSelectionAllowed(),
						onClick: this._onCameraButtonClick.bind(this),
						onArrowClick: this._onCameraArrowClick.bind(this)
					});
					middleSubPanel.appendChild(this.buttons.camera.render());
					break;
				case "speaker":
					this.buttons.speaker = new ButtonWithArrow({
						class: "speaker",
						text: BX.message("IM_M_CALL_BTN_SPEAKER"),
						enabled: !this.speakerMuted,
						arrowEnabled: BX.Call.Hardware.canSelectSpeaker(),
						onClick: this._onSpeakerButtonClick.bind(this),
						onArrowClick: this._onSpeakerArrowClick.bind(this)
					});
					middleSubPanel.appendChild(this.buttons.speaker.render());
					break;
				case "screen":
					if(!this.buttons.screen)
					{
						this.buttons.screen = new SimpleButton({
							class: "screen",
							text: BX.message("IM_M_CALL_BTN_SCREEN"),
							onClick: this._onScreenButtonClick.bind(this)
						});
					}
					middleSubPanel.appendChild(this.buttons.screen.render());
					break;
				case "chat":
					if(!this.buttons.chat)
					{
						this.buttons.chat = new SimpleButton({
							class: "chat",
							text: BX.message("IM_M_CALL_BTN_CHAT"),
							onClick: this._onChatButtonClick.bind(this)
						});
					}
					middleSubPanel.appendChild(this.buttons.chat.render());
					break;
				case "history":
					this.buttons.history = new SimpleButton({
						class: "history",
						text: BX.message("IM_M_CALL_BTN_HISTORY"),
						onClick: this._onHistoryButtonClick.bind(this)
					});
					middleSubPanel.appendChild(this.buttons.history.render());
					break;
				case "hangup":
					this.buttons.hangup = new HangupButton({
						text: Object.keys(this.users).length > 1 ?  BX.message("IM_M_CALL_BTN_DISCONNECT") : BX.message("IM_M_CALL_BTN_HANGUP"),
						onClick: this._onHangupButtonClick.bind(this)
					});
					if(this.uiState == UiState.Initializing)
					{
						middleSubPanel.appendChild(this.buttons.hangup.render());
					}
					else
					{
						rightSubPanel.appendChild(this.buttons.hangup.render());
					}
					break;
				case "close":
					this.buttons.close = new HangupButton({
						text: BX.message("IM_M_CALL_BTN_CLOSE"),
						onClick: this._onCloseButtonClick.bind(this)
					});
					if(this.uiState == UiState.Initializing)
					{
						middleSubPanel.appendChild(this.buttons.close.render());
					}
					else
					{
						rightSubPanel.appendChild(this.buttons.close.render());
					}
					break;
				case "fullscreen":
					this.buttons.fullscreen = new SimpleButton({
						class: "resize",
						text: "",
						onClick: this._onFullScreenButtonClick.bind(this)
					});
					rightSubPanel.appendChild(this.buttons.fullscreen.render());
					break;
			}
		}

		return panelInner;
	};

	BX.Call.View.prototype.setButtonActive = function(buttonName, isActive)
	{
		if(!this.buttons[buttonName])
		{
			return;
		}

		this.buttons[buttonName].setActive(isActive);
	};

	BX.Call.View.prototype.setButtonCounter = function(buttonName, counter)
	{
		if(!this.buttons[buttonName])
		{
			return;
		}

		this.buttons[buttonName].setCounter(counter);
	};

	BX.Call.View.prototype.updateUserList = function()
	{
		this.renderUserList();

		if (this.layout == Layouts.Centered)
		{
			if(!this.elements.userList.container.parentElement)
			{
				this.elements.userBlock.appendChild(this.elements.userList.container);
			}
			this.centralUser.setFullSize(this.elements.userList.container.childElementCount === 0);

		}
		else if (this.layout == Layouts.Grid)
		{
			if(!this.elements.userList.container.parentElement)
			{
				this.elements.container.appendChild(this.elements.userList.container);
			}
		}
		this.toggleEars();
	};

	BX.Call.View.prototype.resumeVideo = function()
	{
		for (var userId in this.users)
		{
			/** @type {CallUser} */
			var user = this.users[userId];
			user.playVideo()
		}
		this.localUser.playVideo(true);
		if(this.layout == Layouts.Centered)
		{
			this.centralUser.playVideo();
		}
	};

	BX.Call.View.prototype.updateButtons = function()
	{
		if(!this.elements.panel)
		{
			return;
		}
		var buttons = this.getButtonList();
		BX.cleanNode(this.elements.panel);
		this.elements.panel.appendChild(this.renderButtons(buttons))
	};

	BX.Call.View.prototype.updateUserData = function(userData)
	{
		for(var userId in userData)
		{
			if(!this.userData[userId])
			{
				this.userData[userId] = {
					name: '',
					avatar_hr: '',
				}
			}
			if(userData[userId].name)
			{
				this.userData[userId].name = userData[userId].name;
			}
			if(userData[userId].avatar_hr)
			{
				this.userData[userId].avatar_hr = BX.Call.Util.isAvatarBlank(userData[userId].avatar_hr) ? '' : userData[userId].avatar_hr;
			}

			if(userId == this.userId)
			{
				this.localUser.updateName(this.userData[userId].name);
				this.localUser.updateAvatar(this.userData[userId].avatar_hr);
			}
			if(userId == this.centralUser.userId)
			{
				this.centralUser.name = this.userData[userId].name;
				this.centralUser.avatar = this.userData[userId].avatar_hr;
				this.centralUser.updateUserInfo();
			}
			if (this.users[userId])
			{
				this.users[userId].updateName(this.userData[userId].name);
				this.users[userId].updateAvatar(this.userData[userId].avatar_hr);
			}
		}
	};

	BX.Call.View.prototype.isScreenSharingSupported = function()
	{
		return navigator.mediaDevices && typeof(navigator.mediaDevices.getDisplayMedia) === "function" || typeof(BXDesktopSystem) !== "undefined";
	};

	BX.Call.View.prototype.isFullScreenSupported = function()
	{
		if (BX.browser.IsChrome() || BX.browser.IsSafari())
		{
			return document.webkitFullscreenEnabled === true;
		}
		else if (BX.browser.IsFirefox())
		{
			return document.fullscreenEnabled === true;
		}
		else
		{
			return false;
		}
	};

	BX.Call.View.prototype.enterFullScreen = function()
	{
		if (BX.browser.IsChrome() || BX.browser.IsSafari())
		{
			this.elements.root.webkitRequestFullScreen();
		}
		else if (BX.browser.IsFirefox())
		{
			this.elements.root.requestFullscreen();
		}
	};

	BX.Call.View.prototype.exitFullScreen = function()
	{
		if (document.cancelFullScreen)
		{
			document.cancelFullScreen();
		}
		else if (document.mozCancelFullScreen)
		{
			document.mozCancelFullScreen();
		}
		else if (document.webkitCancelFullScreen)
		{
			document.webkitCancelFullScreen();
		}
		else if (document.document.exitFullscreen())
		{
			document.exitFullscreen()
		}
	};

	BX.Call.View.prototype.toggleEars = function()
	{
		this.toggleRightEar();
		this.toggleLeftEar();
	};

	BX.Call.View.prototype.toggleRightEar = function()
	{
		if (
			this.layout == Layouts.Centered
			&& this.elements.userList.container.scrollWidth > this.elements.userList.container.offsetWidth
			&& (this.elements.userList.container.offsetWidth + this.elements.userList.container.scrollLeft) < this.elements.userList.container.scrollWidth
	  	   )
		{
			this.elements.ear.right.classList.add("bx-messenger-videocall-ear-show");
		}
		else
		{
			this.elements.ear.right.classList.remove("bx-messenger-videocall-ear-show");
		}
	};

	BX.Call.View.prototype.toggleLeftEar = function()
	{
		if (
			this.layout == Layouts.Centered
			&& this.elements.userList.container.scrollLeft > 0
		   )
		{
			this.elements.ear.left.classList.add("bx-messenger-videocall-ear-show");
		}
		else
		{
			this.elements.ear.left.classList.remove("bx-messenger-videocall-ear-show");
		}
	};

	BX.Call.View.prototype.scrollUserBlockLeft = function()
	{
		this.stopScroll();
		this.scrollInterval = setInterval(
			function()
			{
				this.elements.userList.container.scrollLeft -= 10;
			}.bind(this),
			20
		);
	};

	BX.Call.View.prototype.scrollUseBlockRight = function()
	{
		this.stopScroll();
		this.scrollInterval = setInterval(
			function()
			{
				this.elements.userList.container.scrollLeft += 10;
			}.bind(this),
			20
		);
	};

	BX.Call.View.prototype.stopScroll = function()
	{
		if(this.scrollInterval)
		{
			clearInterval(this.scrollInterval);
			this.scrollInterval = 0;
		}
	};

	// event handlers

	BX.Call.View.prototype._onBodyClick = function(e)
	{
		this.eventEmitter.emit(EventName.onBodyClick);
	};

	BX.Call.View.prototype._onFullScreenChange = function(e)
	{
		if (BX.browser.IsChrome() || BX.browser.IsSafari())
		{
			this.isFullScreen = (document.webkitFullscreenElement == this.elements.root);
		}
		else if (BX.browser.IsFirefox())
		{
			this.isFullScreen = (document.fullscreenElement == this.elements.root);
		}

		this.updateUserList();
		this.updateButtons();
	};

	BX.Call.View.prototype._onResize = function()
	{
		if(!this.elements.root)
		{
			return;
		}
		if(this.centralUser)
		{
			this.centralUser.updateAvatarWidth();
		}
		if(this.layout == Layouts.Grid)
		{
			this.updateUserList();
		}
		else
		{
			this.toggleEars();
		}
	};

	BX.Call.View.prototype._onUserClick = function(e)
	{
		if(this.layout == Layouts.Grid)
		{
			return;
		}

		var userId = e.userId;

		if(userId == this.userId)
		{
			return;
		}

		this.setCentralUser(userId);
	};

	BX.Call.View.prototype._onGridButtonClick = function(e)
	{
		this.setLayout(this.layout == Layouts.Centered ? Layouts.Grid : Layouts.Centered);
	};

	BX.Call.View.prototype._onAddButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: "inviteUser",
			node: e.currentTarget
		});
	};

	BX.Call.View.prototype._onShareButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: "share",
			node: e.currentTarget
		});
	};

	BX.Call.View.prototype._onMicrophoneButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: "toggleMute",
			muted: !this.isMuted
		});
	};

	BX.Call.View.prototype._onMicrophoneArrowClick = function(e)
	{
		e.stopPropagation();

		DeviceSelector.create({
			parentElement: e.currentTarget,
			deviceList: BX.Call.Hardware.getMicrophoneList(),
			current: this.microphoneId,
			onSelect: this._onMicrophoneSelected.bind(this)
		}).show();
	};

	BX.Call.View.prototype._onMicrophoneSelected = function(deviceInfo)
	{
		if(deviceInfo.deviceId === this.microphoneId)
		{
			return;
		}

		this.eventEmitter.emit(EventName.onReplaceMicrophone, deviceInfo);
	};

	BX.Call.View.prototype._onCameraButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: "toggleVideo",
			video: !this.isCameraOn
		});
	};

	BX.Call.View.prototype._onCameraArrowClick = function(e)
	{
		e.stopPropagation();
		if(!BX.Call.Hardware.hasCamera())
		{
			return false;
		}

		DeviceSelector.create({
			parentElement: e.currentTarget,
			deviceList: BX.Call.Hardware.getCameraList(),
			current: this.cameraId,
			onSelect: this._onCameraSelected.bind(this)
		}).show();
	};

	BX.Call.View.prototype._onCameraSelected = function(deviceInfo)
	{
		if(deviceInfo.deviceId === this.cameraId)
		{
			return;
		}

		this.eventEmitter.emit(EventName.onReplaceCamera, deviceInfo);
	};

	BX.Call.View.prototype._onSpeakerButtonClick = function(e)
	{
		this.muteSpeaker(!this.speakerMuted);
	};

	BX.Call.View.prototype._onSpeakerArrowClick = function(e)
	{
		e.stopPropagation();

		DeviceSelector.create({
			parentElement: e.currentTarget,
			deviceList: BX.Call.Hardware.getSpeakerList(),
			current: this.speakerId,
			onSelect: this._onSpeakerSelected.bind(this)
		}).show();
	};

	BX.Call.View.prototype._onSpeakerSelected = function(deviceInfo)
	{
		this.setSpeakerId(deviceInfo.deviceId);

		this.eventEmitter.emit(EventName.onReplaceSpeaker, deviceInfo);
	};

	BX.Call.View.prototype._onScreenButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'toggleScreenSharing',
			node: e.target
		});
	};

	BX.Call.View.prototype._onChatButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'showChat',
			node: e.target
		});
	};

	BX.Call.View.prototype._onHistoryButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'showHistory',
			node: e.target
		});
	};

	BX.Call.View.prototype._onHangupButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'hangup',
			node: e.target
		});
	};

	BX.Call.View.prototype._onCloseButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'close',
			node: e.target
		});
	};

	BX.Call.View.prototype._onFullScreenButtonClick = function(e)
	{
		e.stopPropagation();
		this.eventEmitter.emit(EventName.onButtonClick, {
			buttonName: 'fullscreen',
			node: e.target
		});
	};

	BX.Call.View.prototype.releaseLocalMedia = function()
	{
		this.localUser.releaseStream();
		if(this.centralUser.userId == this.userId)
		{
			this.centralUser.releaseStream();
		}
	};

	BX.Call.View.prototype.destroy = function()
	{
		if(this.elements.root)
		{
			BX.cleanNode(this.elements.root, true);
			this.elements.root = null;
		}
		this.visible = false;

		window.removeEventListener("webkitfullscreenchange", this._onFullScreenChangeHandler);
		window.removeEventListener("mozfullscreenchange", this._onFullScreenChangeHandler);
		this.resizeObserver.disconnect();
		for(var userId in this.users)
		{
			if(this.users.hasOwnProperty(userId))
			{
				this.users[userId].destroy();
			}
		}
		this.userData = null;
		this.centralUser.destroy();

		clearTimeout(this.switchPresenterTimeout);

		this.eventEmitter.emit(EventName.onDestroy);
		this.eventEmitter.unsubscribeAll();
	};

	var CentralUser = function (config)
	{
		this.parent = config.parent;
		this.stream = config.stream || null;
		this.userId = config.userId;
		this.language = config.language;
		this.name = config.name || '';
		this.avatar = config.avatar && !BX.Call.Util.isAvatarBlank(config.avatar) ? config.avatar : '';

		this.hasVideo = false;

		this.elements = {
			container: null,
			inner: null,
			video: null,
			user:null,
			userBlock: null,
			avatar: null,
			nameBlock: null,
			name: null
		};
		this.loader = null;

		this.checkAspectInterval = setInterval(this.checkVideoAspect.bind(this), 500);
	};

	CentralUser.prototype.render = function()
	{
		this.elements.container = BX.create("div", {
			props: {className: "bx-messenger-videocall-video-block"},
		});

		this.elements.video = BX.create("video", {
			props: {
				className: "bx-messenger-videocall-video",
				autoplay: true,
				volume: 0
			}
		});

		this.elements.user = BX.create("div", {
			props: {className: "bx-messenger-audiocall"},
			children: [
				BX.create("div", {
					props: {className: "bx-messenger-audiocall-wrap"},
					children: [
						this.elements.inner = BX.create("div", {
							props: {className: "bx-messenger-audiocall-inner"},
							children: [
								this.elements.userBlock = BX.create("div", {
									props: {className: "bx-messenger-audiocall-user-block"},
									children: [
										BX.create("div", {
											props: {className: "bx-messenger-audiocall-user-block-inner"},
											children: [
												BX.create("div", {
													props: {className: "bx-messenger-audiocall-user"},
													children: [
														this.elements.avatar = BX.create("div", {
															props: {className: "bx-messenger-audiocall-user-item"},
														})
													]
												})
											]
										})
									]
								}),
								this.elements.nameBlock = BX.create("div", {
									props: {className: "bx-messenger-audiocall-user-name"},
									children: [
										this.elements.name = BX.create("span", {
											props: {className: "bx-messenger-audiocall-user-link"},
										})
									]
								})
							]
						})
					]
				})
			]
		});

		if(this.stream && BX.Call.Util.containsVideoTrack(this.stream))
		{
			this.elements.container.appendChild(this.elements.video);
			this.elements.container.classList.remove("bx-messenger-videocall-audio");
			if(this.userId == this.parent.userId && this.parent.localUser.flipVideo)
			{
				this.elements.video.classList.add("bx-messenger-videocall-video-flipped")
			}
			else
			{
				this.elements.video.classList.remove("bx-messenger-videocall-video-flipped")
			}
		}
		else
		{
			this.elements.container.classList.add("bx-messenger-videocall-audio");
		}

		this.loader = new BX.Loader({
			target: this.elements.container
		});

		this.updateUserInfo();
		return this.elements.container;
	};

	CentralUser.prototype.blurVideo = function(blur)
	{
		blur = blur !== false;

		if(blur)
		{
			this.elements.video.pause();
			this.elements.video.classList.add("bx-messenger-videocall-video-blurred");
			this.loader.show();
		}
		else
		{
			this.elements.video.play().catch(BX.DoNothing);
			this.elements.video.classList.remove("bx-messenger-videocall-video-blurred");
			this.loader.hide();
		}
	};

	CentralUser.prototype.updateUserInfo = function()
	{
		if(this.elements.name)
		{
			this.elements.name.innerText = this.name;
		}

		if(this.elements.avatar)
		{
			if (this.avatar != '')
			{
				this.elements.avatar.style.backgroundImage = "url('" + this.avatar + "')";
			}
			else
			{
				this.elements.avatar.style.removeProperty("background-image");
			}
		}

		this.updateAvatarWidth();
	};

	CentralUser.prototype.updateAvatarWidth = function()
	{
		this.elements.userBlock.style.maxWidth = Math.min(200, (this.elements.inner.offsetHeight - this.elements.nameBlock.offsetHeight)) + 'px';
	};

	CentralUser.prototype.setUserId = function(userId, userData)
	{
		if (this.userId == userId)
		{
			return;
		}

		if (userId == this.parent.userId && this.parent.localUser.flipVideo)
		{
			this.setStream(this.parent.localUser.stream);
			this.elements.video.classList.add("bx-messenger-videocall-video-flipped");
		}
		else
		{
			this.setStream(this.parent.users[userId].stream);
			this.elements.video.classList.remove("bx-messenger-videocall-video-flipped");
		}

		this.userId = userId;
		this.name =  userData.name || '';
		this.avatar = userData.avatar_hr || '';
		this.updateUserInfo();
	};

	CentralUser.prototype.setStream = function(stream)
	{
		this.stream = stream;
		var hasVideo = BX.Call.Util.containsVideoTrack(stream);

		if(this.stream)
		{
			if(this.hasVideo && !hasVideo)
			{
				BX.remove(this.elements.video);
				this.elements.container.appendChild(this.elements.user);
				this.elements.container.classList.add("bx-messenger-videocall-audio");
				this.updateAvatarWidth();
			}
			else if(!this.hasVideo && hasVideo)
			{
				BX.remove(this.elements.user);
				this.elements.container.appendChild(this.elements.video);
				this.elements.container.classList.remove("bx-messenger-videocall-audio");
			}
		}
		else
		{
			BX.remove(this.elements.video);
			this.elements.container.appendChild(this.elements.user);
			this.elements.container.classList.add("bx-messenger-videocall-audio");
			this.updateAvatarWidth();
		}

		this.hasVideo = hasVideo;
		if(this.hasVideo)
		{
			if (this.elements.video.srcObject != stream)
			{
				this.elements.video.srcObject = stream;
			}
			this.blurVideo(false);
			if(this.userId == this.parent.userId && this.parent.localUser.flipVideo)
			{
				this.elements.video.classList.add("bx-messenger-videocall-video-flipped")
			}
			else
			{
				this.elements.video.classList.remove("bx-messenger-videocall-video-flipped")
			}
		}
	};

	CentralUser.prototype.playVideo = function()
	{
		if(this.elements.video)
		{
			this.elements.video.play().catch(BX.DoNothing);
		}
	};

	CentralUser.prototype.releaseStream = function()
	{
		this.elements.video.srcObject = null;
		this.stream = null;
	};

	CentralUser.prototype.setFullSize = function(fullSize)
	{
		if(fullSize)
		{
			this.elements.container.classList.add('bx-messenger-videocall-video-block-full');
		}
		else
		{
			this.elements.container.classList.remove('bx-messenger-videocall-video-block-full');
		}
		this.updateAvatarWidth();
	};

	CentralUser.prototype.isVideo = function()
	{

	};

	CentralUser.prototype.checkVideoAspect = function()
	{
		if(!this.elements.video)
		{
			return;
		}

		if(this.elements.video.videoHeight > this.elements.video.videoWidth)
		{
			this.elements.video.classList.add("bx-messenger-videocall-video-vertical");
		}
		else
		{
			this.elements.video.classList.remove("bx-messenger-videocall-video-vertical");
		}
	};

	CentralUser.prototype.destroy = function()
	{
		this.stream = null;
		if(this.loader)
		{
			this.loader.destroy();
			this.loader = null;
		}
		clearInterval(this.checkAspectInterval);
	};

	var CallUser = function(config)
	{
		this.id = config.id;
		this.name = config.name || '';
		this.avatar = config.avatar && !BX.Call.Util.isAvatarBlank(config.avatar) ? config.avatar : '';
		this.state = config.state;
		this.order = config.order;

		this._stream = config.stream;
		Object.defineProperty(this, "stream", {
			get: function()
			{
				return this._stream;
			},
			set: function(stream)
			{
				this._stream = stream;
				this.update()
			}
		});
		this._flipVideo = false;
		Object.defineProperty(this, "flipVideo", {
			get: function()
			{
				return this._flipVideo;
			},
			set: function(flipVideo)
			{
				this._flipVideo = flipVideo;
				this.update()
			}
		});

		this.talking = false;
		this.talkingStart = 0;
		this.talkingStop = (new Date()).getTime();
		this.isMicrophoneOn = config.isMicrophoneOn !== false;

		this.localUser = config.localUser === true;
		this.hidden = false;

		this.screenState = false;

		this.elements = {
			root: null,
			container: null,
			videoContainer: null,
			video: null,
			videoBorder: null,
			avatar: null,
			nameContainer: null,
			name: null,
			overlay: null,
			state: null,
			removeButton: null,
			micState: null
		};

		this.callBacks = {
			onClick: BX.type.isFunction(config.onClick) ?  config.onClick : BX.DoNothing
		};

		this.checkAspectInterval = setInterval(this.checkVideoAspect.bind(this), 500);
	};

	CallUser.prototype.render = function()
	{
		var self = this;
		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall-user"},
			dataset: {userId: this.id, order: this.order},
			children: [
				this.elements.container = BX.create("div", {
					props: {className: "bx-messenger-videocall-user-inner"},
				}),
			],
			style: {
				order: this.order
			},
			events: {
				click: function(e)
				{
					e.stopPropagation();
					this.callBacks.onClick({
						userId: this.id
					});
				}.bind(this)
			}
		});

		if (this.talking)
		{
			this.elements.root.classList.add("bx-messenger-videocall-user-talking");
		}

		if (this.localUser)
		{
			this.elements.root.classList.add("bx-messenger-videocall-user-self");
		}

		this.elements.avatar = BX.create("div", {
			props: {className: "bx-message-videocall-user-avatar"}
		});

		if (this.avatar !== '')
		{
			this.elements.avatar.style.backgroundImage = "url('" + this.avatar + "')";
		}
		else
		{
			self.elements.avatar.style.removeProperty("background-image");
		}

		if(!this.hasVideo())
		{
			this.elements.container.appendChild(this.elements.avatar);
		}

		this.elements.videoContainer = BX.create("div", {
			props: {
				className: "bx-messenger-videocall-video-container",
			},
			children: [
				this.elements.video = BX.create("video", {
					props: {
						className: "bx-messenger-videocall-video",
						volume: 0,
						autoplay: true
					}
				}),
				this.elements.videoBorder = BX.create("div", {
					props: {
						className: "bx-messenger-videocall-video-border",
					}
				})
			]
		});
		this.elements.container.appendChild(this.elements.videoContainer);

		if(this.stream && this.stream.active)
		{
			this.elements.video.srcObject = this.stream;
		}
		if(this.flipVideo)
		{
			this.elements.video.classList.add("bx-messenger-videocall-video-flipped");
		}

		this.elements.overlay = BX.create("div", {props: {className: "bx-messenger-videocall-overlay"}});
		this.elements.root.appendChild(this.elements.overlay);

		this.elements.state = this.renderState();
		if(this.elements.state)
		{
			this.elements.overlay.appendChild(this.elements.state);
		}

		this.elements.nameContainer = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-name"},
			children: [
				this.elements.name = BX.create("span", {
					props: {className: "bx-messenger-videocall-user-text"},
					text: this.name
				}),
				BX.create("div", {
					props: {className: "bx-messenger-videocall-user-shadow"}
				})
			]
		});
		this.elements.videoContainer.appendChild(this.elements.nameContainer);

		this.elements.micState = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-mic-state" + (this.isMicrophoneOn ? " hidden" : "")},
			children: [
				BX.create("div", {
					props: {className: "bx-messenger-videocall-user-mic-state-icon"},
				}),
			]
		});
		this.elements.nameContainer.insertBefore(this.elements.micState, this.elements.name);

		// todo: show button only if user have the permission to remove user
		/*this.elements.removeButton = BX.create("div", {
			props: {className: "bx-messenger-videocall-user-close"}
		});

		this.elements.container.appendChild(this.elements.removeButton);*/

		return this.elements.root;
	};

	CallUser.prototype.updateAvatar = function(avatar)
	{
		this.avatar = avatar;

		if(this.elements.avatar)
		{
			if (this.avatar !== '')
			{
				this.elements.avatar.style.backgroundImage = "url('" + this.avatar + "')";
			}
			else
			{
				this.elements.avatar.style.removeProperty("background-image");
			}
		}
	};

	CallUser.prototype.updateName = function(name)
	{
		this.name = name;
		if(this.elements.name)
		{
			this.elements.name.innerText = this.name;
		}
	};

	CallUser.prototype.update = function()
	{
		if(!this.elements.root)
		{
			return;
		}
		if(this.hasVideo())
		{
			if(this.elements.video.srcObject != this.stream)
			{
				this.elements.video.srcObject = this.stream;
			}
			BX.remove(this.elements.avatar);
			if(this.flipVideo)
			{
				this.elements.video.classList.add("bx-messenger-videocall-video-flipped");
			}
			else
			{
				this.elements.video.classList.remove("bx-messenger-videocall-video-flipped");
			}
		}
		else
		{
			this.elements.video.srcObject = null;
			this.elements.container.appendChild(this.elements.avatar);
		}
	};

	CallUser.prototype.playVideo = function()
	{
		if(this.elements.video)
		{
			this.elements.video.play().catch(BX.DoNothing);
		}
	};

	CallUser.prototype.renderState = function()
	{
		var stateNode;

		switch (this.state)
		{
			case BX.Call.UserState.Idle:
				// We should never get here. Idle users are not to be painted
				break;
			case BX.Call.UserState.Calling:
				stateNode = BX.create("div", {
					props: {className: "bx-messenger-videocall-user-status"},
					children: [
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-pic"},
							children: [
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}}),
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}}),
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}})
							]
						}),
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-text"},
							text: BX.message("IM_M_CALL_STATUS_WAIT_ANSWER")
						})
					]
				});
				break;
			case BX.Call.UserState.Declined:
				stateNode = BX.create("div", {
					props: {className: "bx-messenger-videocall-user-status bx-messenger-videocall-user-status-wide"},
					children: [
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-pic"},
							children: [
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-cross"}}),
							]
						}),
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-text"},
							text: BX.message("IM_M_CALL_STATUS_DECLINED")
						})
					]
				});
				break;
			case BX.Call.UserState.Ready:
			case BX.Call.UserState.Connecting:
				stateNode = BX.create("div", {
					props: {className: "bx-messenger-videocall-user-status"},
					children: [
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-pic"},
							children: [
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}}),
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}}),
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-dot"}})
							]
						}),
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-text"},
							text: BX.message("IM_M_CALL_STATUS_WAIT_CONNECT")
						})
					]
				});
				break;
			case BX.Call.UserState.Connected:
				break;
			case BX.Call.UserState.Failed:
			case BX.Call.UserState.Unavailable:
				stateNode = BX.create("div", {
					props: {className: "bx-messenger-videocall-user-status bx-messenger-videocall-user-status-wide"},
					children: [
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-pic"},
							children: [
								BX.create("span", {props: {className: "bx-messenger-videocall-user-status-cross"}}),
							]
						}),
						BX.create("span", {
							props: {className: "bx-messenger-videocall-user-status-text"},
							text: this.state == BX.Call.UserState.Failed ? BX.message("IM_M_CALL_STATUS_CONNECTION_ERROR") : BX.message("IM_M_CALL_STATUS_UNAVAILABLE")
						})
					]
				});
				break;
		}

		return stateNode ? stateNode : null;
	};

	CallUser.prototype.mount = function(parent, force)
	{
		force = force === true;
		if(!this.elements.root)
		{
			this.render();
		}

		if(this.isMounted() && !force)
		{
			return false;
		}

		parent.appendChild(this.elements.root);
		this.update();
	};

	CallUser.prototype.dismount = function()
	{
		if(!this.isMounted())
		{
			return false;
		}

		this.elements.video.srcObject = null;
		BX.remove(this.elements.root);
	};

	CallUser.prototype.isMounted = function()
	{
		return !!(this.elements.root && this.elements.root.parentElement);
	};

	CallUser.prototype.setState = function(newState)
	{
		if(this.state == newState)
		{
			return;
		}

		this.state = newState;

		if(!this.elements.root)
		{
			// not rendered yet
			return;
		}

		if(this.elements.state)
		{
			BX.cleanNode(this.elements.overlay);
			this.elements.state = null;
		}

		this.elements.state = this.renderState();
		if(this.elements.state)
		{
			this.elements.overlay.appendChild(this.elements.state);
		}
	};

	CallUser.prototype.setTalking = function(talking)
	{
		if(this.talking == talking)
		{
			return;
		}

		this.talking = talking;
		if(!this.elements.root)
		{
			return;
		}
		if(this.talking)
		{
			this.elements.root.classList.add("bx-messenger-videocall-user-talking");
			this.talkingStart = (new Date()).getTime();
		}
		else
		{
			this.talkingStop = (new Date()).getTime();
			this.elements.root.classList.remove("bx-messenger-videocall-user-talking");
		}
	};

	CallUser.prototype.wasTalkingAgo = function()
	{
		if (this.talking)
		{
			return 0;
		}

		return ((new Date()).getTime() - this.talkingStop);
	};

	CallUser.prototype.setMicrophoneState = function(isMicrophoneOn)
	{
		if(this.isMicrophoneOn == isMicrophoneOn)
		{
			return;
		}
		this.isMicrophoneOn = isMicrophoneOn;
		if(!this.elements.root)
		{
			return;
		}
		if(this.isMicrophoneOn)
		{
			this.elements.micState.classList.add("hidden");
		}
		else
		{
			this.elements.micState.classList.remove("hidden");
		}
	};

	CallUser.prototype.setScreenState = function(screenState)
	{
		this.screenState = screenState;
	};

	CallUser.prototype.hide = function()
	{
		if(!this.elements.root)
		{
			return;
		}

		this.elements.root.dataset.hidden = 1;
	};

	CallUser.prototype.show = function()
	{
		if(!this.elements.root)
		{
			return;
		}

		delete this.elements.root.dataset.hidden;
	};

	CallUser.prototype.hasVideo = function()
	{
		return this.state == BX.Call.UserState.Connected && BX.Call.Util.containsVideoTrack(this.stream);
	};

	CallUser.prototype.checkVideoAspect = function()
	{
		if(!this.elements.video)
		{
			return;
		}

		if(this.elements.video.videoHeight > this.elements.video.videoWidth)
		{
			this.elements.video.classList.add("bx-messenger-videocall-video-vertical");
		}
		else
		{
			this.elements.video.classList.remove("bx-messenger-videocall-video-vertical");
		}
	};

	CallUser.prototype.releaseStream = function()
	{
		if(this.elements.video)
		{
			this.elements.video.srcObject = null;
		}
		this.stream = null;
	};


	CallUser.prototype.destroy = function()
	{
		this.releaseStream();
		clearInterval(this.checkAspectInterval);
	};

	var TitleButton = function(config)
	{
		this.elements = {
			root: null
		};

		this.text = BX.type.isNotEmptyString(config.text) ? config.text : '';
		this.isGroupCall = config.isGroupCall;
	};

	TitleButton.prototype.render = function()
	{
		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-title"},
			html: this.getTitle(this.text)
		});

		return this.elements.root;
	};

	TitleButton.prototype.getTitle = function(name)
	{
		var prettyName = '<span class="bx-messenger-videocall-panel-title-name">' + this.text + '</span>';

		if(this.isGroupCall)
		{
			return BX.message("IM_M_GROUP_CALL_WITH").replace("#CHAT_NAME#", prettyName);
		}
		else
		{
			return BX.message("IM_M_CALL_WITH").replace("#USER_NAME#", prettyName);
		}
	};

	var SimpleButton = function(config)
	{
		this.class = config.class;
		this.text = BX.type.isNotEmptyString(config.text) ? config.text : '';
		this.isActive = false;
		this.counter = config.counter || 0;

		this.elements = {
			root: null,
			counter: null,
			counterValue: null
		};

		this.callbacks = {
			onClick: BX.type.isFunction(config.onClick) ? config.onClick : BX.DoNothing
		}
	};

	SimpleButton.prototype.render = function()
	{
		if(this.elements.root)
		{
			return this.elements.root;
		}

		var textNode;
		if(this.text !== '')
		{
			textNode = BX.create("div", {props: {className: "bx-messenger-videocall-panel-text"}, text: this.text});
		}
		else
		{
			textNode = null;
		}

		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-item"},
			children: [
				BX.create("div", {
					props: {className: "bx-messenger-videocall-panel-icon bx-messenger-videocall-panel-icon-" + this.class},
					children: [
						this.elements.counter = BX.create("span", {
							props: {className: "bx-messenger-cl-count"},
							style: {
								display: this.counter > 0 ? "inline-block" : "none",
								position: "absolute",
								right: "-19px",
								top: "-13px"
							},
							children: [
								this.elements.counterValue = BX.create("span", {
									props: {className: "bx-messenger-cl-count-digit"},
									text: this.counter
								})
							]
						}),
					]
				}),

				textNode
			],
			events: {
				click: this.callbacks.onClick
			}
		});
		if(this.isActive)
		{
			this.elements.root.classList.add("active");
		}

		return this.elements.root;
	};

	SimpleButton.prototype.setActive = function (isActive)
	{
		if(this.isActive == isActive)
		{
			return;
		}
		this.isActive = isActive;
		if (!this.elements.root)
		{
			return;
		}
		if(this.isActive)
		{
			this.elements.root.classList.add("active");
		}
		else
		{
			this.elements.root.classList.remove("active");
		}
	};

	SimpleButton.prototype.setCounter = function (counter)
	{
		this.counter = counter;
		if(this.counter == 0)
		{
			this.elements.counter.style.display = "none";
		}
		else
		{
			this.elements.counter.style.removeProperty("display");
		}
		this.elements.counterValue.innerText = counter;
	};

	var HangupButton = function (config)
	{
		this.text = config.text;

		this.elements = {
			root: null
		};

		this.callbacks = {
			onClick: BX.type.isFunction(config.onClick) ? config.onClick : BX.DoNothing
		}
	};

	HangupButton.prototype.render = function()
	{
		if(this.elements.root)
		{
			return this.elements.root;
		}

		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-item bx-messenger-videocall-panel-item-btn"},
			children: [
				BX.create("button", {
					props: {className: "ui-btn ui-btn-round bx-messenger-videocall-panel-btn ui-btn-icon-phone-down"},
					text: this.text
				})
			],
			events: {
				click: this.callbacks.onClick
			}
		});
		return this.elements.root;
	};


	var ButtonWithArrow = function(config)
	{
		this.class = config.class;
		this.text = config.text;

		this.enabled = (config.enabled == true);
		this.arrowEnabled = (config.arrowEnabled == true);

		this.elements = {
			root: null,
			icon: null,
			arrow: null
		};

		this.callbacks = {
			onClick: BX.type.isFunction(config.onClick) ? config.onClick : BX.DoNothing,
			onArrowClick: BX.type.isFunction(config.onArrowClick) ? config.onArrowClick : BX.DoNothing
		}
	};

	ButtonWithArrow.prototype.render = function()
	{
		if(this.elements.root)
		{
			return this.elements.root;
		}

		this.elements.root = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-item"},
			children: [
				this.elements.icon = BX.create("div", {
					props: {className: this.getIconClass()},
				}),
				BX.create("div", {
					props: {className: "bx-messenger-videocall-panel-text"},
					text: this.text
				})
			],
			events: {
				click: this.callbacks.onClick
			}
		});

		this.elements.arrow = BX.create("div", {
			props: {className: "bx-messenger-videocall-panel-arrow"},
			events: {
				click: function(e)
				{
					this.callbacks.onArrowClick.apply(this, arguments);
					e.stopPropagation();
				}.bind(this)
			}
		});

		if(this.arrowEnabled)
		{
			this.elements.icon.appendChild(this.elements.arrow);
		}

		return this.elements.root;
	};

	ButtonWithArrow.prototype.getIconClass = function()
	{
		return "bx-messenger-videocall-panel-icon bx-messenger-videocall-panel-icon-" + this.class + (this.enabled ? "" : "-off");
	};

	ButtonWithArrow.prototype.enable = function()
	{
		if(this.enabled)
		{
			return;
		}
		this.enabled = true;
		this.elements.icon.className = this.getIconClass();
	};

	ButtonWithArrow.prototype.disable = function()
	{
		if(!this.enabled)
		{
			return;
		}
		this.enabled = false;
		this.elements.icon.className = this.getIconClass();
	};

	ButtonWithArrow.prototype.showArrow = function()
	{
		if(this.arrowEnabled)
		{
			return;
		}
		this.arrowEnabled = true;
		this.elements.icon.appendChild(this.elements.arrow);
	};

	ButtonWithArrow.prototype.hideArrow = function()
	{
		if(!this.arrowEnabled)
		{
			return;
		}
		this.arrowEnabled = false;
		this.elements.icon.removeChild(this.elements.arrow);
	};

	var DeviceSelector = function(config)
	{
		this.deviceList = config.deviceList;
		this.current = config.current;
		this.parentElement = config.parentElement;

		this.menu = null;

		this.callbacks = {
			onSelect: BX.type.isFunction(config.onSelect) ? config.onSelect : BX.DoNothing
		}
	};

	DeviceSelector.create = function(config)
	{
		return new DeviceSelector(config);
	};

	DeviceSelector.prototype.show = function()
	{
		var self = this;
		var menuItems = [];

		this.deviceList.forEach(function(deviceInfo)
		{
			menuItems.push({
				id: deviceInfo.deviceId,
				text: deviceInfo.label || "(" + BX.message("IM_M_CALL_DEVICE_NO_NAME") + ")",
				className: (self.current == deviceInfo.deviceId ?  "menu-popup-item-accept" : "device-selector-empty"),
				onclick: function()
				{
					self.menu.close();
					self.callbacks.onSelect(deviceInfo);
				}
			})
		});

		this.menu = BX.PopupMenu.create(
			'call-view-select-device',
			this.parentElement,
			menuItems,
			{
				autoHide: true,
				zIndex: window['BX'] && BX.MessengerCommon ? (BX.MessengerCommon.getDefaultZIndex() + 500) : 500,
				closeByEsc: true,
				offsetTop: 0,
				offsetLeft: 0,
				bindOptions: {
					position: 'top'
				},
				angle: {position: "bottom"},
				overlay: {
					backgroundColor: 'white',
					opacity: 0
				},
				events: {
					onPopupClose : function()
					{
						self.menu.popupWindow.destroy();
						BX.PopupMenu.destroy('call-view-select-device');
					},
					onPopupDestroy: function ()
					{
						self.menu = null;
					}
				}
			}
		);
		this.menu.popupWindow.show();
	};

	BX.Call.View.Layout = Layouts;

	BX.Call.View.Size = {
		Folded: 'folded',
		Full: 'full'
	};

	BX.Call.View.UiState = UiState;
	BX.Call.View.Event = EventName;


})();