Current Path : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/js/im/call/ |
Current File : /var/www/axolotl/data/www/yar.axolotls.ru/bitrix/js/im/call/floating_video.js |
;(function () { BX.namespace('BX.Call'); if (BX.Call.FloatingVideo) { return; } var Events = { setStream: "FloatingVideo::setStream", setAudioMuted: "FloatingVideo::setAudioMuted", setTitle: "FloatingVideo::setTitle", setAvatars: "FloatingVideo::setAvatars", setTalking: "FloatingVideo::setTalking", onMainAreaClick: "FloatingVideo::onMainAreaClick", onMicButtonClick: "FloatingVideo::onMicButtonClick", onHangupButtonClick: "FloatingVideo::onHangupButtonClick", connectionOffer: "FloatingVideo::connectionOffer", connectionAnswer: "FloatingVideo::connectionAnswer", connectionClose: "FloatingVideo::connectionClose", connectionIceCandidate: "FloatingVideo::connectionIceCandidate", }; var VIDEO_WIDTH = 320; var VIDEO_HEIGHT = 180; var AUDIO_WIDTH = 320; var AUDIO_HEIGHT = 70; /** * * @param {object} config * @param {MediaStream} [config.stream] * @param {bool} [config.audioMuted] * @param {function} [config.onMainAreaClick] * @param {function} [config.onButtonClick] * @constructor */ BX.Call.FloatingVideo = function (config) { if(typeof(config) !== "object") { config = {}; } this.stream = config.stream && BX.Call.Util.containsVideoTrack(config.stream) ? config.stream : null; this.audioMuted = config.audioMuted || false; this.title = config.title || ""; this.avatars = config.avatars || {}; this.window = null; this.visible = false; this.peerConnection = null; this.callbacks = { onMainAreaClick: BX.type.isFunction(config.onMainAreaClick) ? config.onMainAreaClick : BX.DoNothing, onButtonClick: BX.type.isFunction(config.onButtonClick) ? config.onButtonClick : BX.DoNothing }; this._onContentMainAreaClickHandler = this._onContentMainAreaClick.bind(this); this._onContentMicButtonClickHandler = this._onContentMicButtonClick.bind(this); this._onContentHangupButtonClickHandler = this._onContentHangupButtonClick.bind(this); this._onPCNegotiationNeededHandler = this._onPCNegotiationNeeded.bind(this); this._onPCIceCandidateHandler = this._onPCIceCandidate.bind(this); this._onConnectionAnswerHandler = this._onConnectionAnswer.bind(this); this._onConnectionIceCandidateHandler = this._onConnectionIceCandidate.bind(this); this.bindEventHandlers(); }; BX.Call.FloatingVideo.prototype = { bindEventHandlers: function () { BX.desktop.addCustomEvent(Events.onMainAreaClick, this._onContentMainAreaClickHandler); BX.desktop.addCustomEvent(Events.onMicButtonClick, this._onContentMicButtonClickHandler); BX.desktop.addCustomEvent(Events.onHangupButtonClick, this._onContentHangupButtonClickHandler); BX.desktop.addCustomEvent(Events.connectionAnswer, this._onConnectionAnswerHandler); BX.desktop.addCustomEvent(Events.connectionIceCandidate, this._onConnectionIceCandidateHandler); }, _onContentMainAreaClick: function() { this.callbacks.onMainAreaClick(); }, _onContentMicButtonClick: function() { this.callbacks.onButtonClick({ buttonName: "toggleMute", muted: !this.audioMuted }) }, _onContentHangupButtonClick: function() { this.callbacks.onButtonClick({ buttonName: "hangup" }) }, setStream: function(stream) { if(BX.Call.Util.containsVideoTrack(stream)) { this.stream = stream; } else { this.stream = null; } if(this.window && this.visible) { if(this.stream) { this.sendVideo(); } else { this.stopSendingVideo(); } } }, setTitle: function(title) { this.title = title; if(this.window) { BX.desktop.onCustomEvent(this.window, Events.setTitle, [this.title]); } }, setAvatars: function(avatarList) { this.avatars = avatarList; if(this.window && this.visible) { BX.desktop.onCustomEvent(this.window, Events.setAvatars, [this.avatars]); } }, setTalking: function(talking) { if(this.window) { BX.desktop.onCustomEvent(this.window, Events.setTalking, [talking]); } }, sendVideo: function() { if(!this.peerConnection) { this.peerConnection = new RTCPeerConnection(); this.peerConnection.addEventListener("negotiationneeded", this._onPCNegotiationNeededHandler); this.peerConnection.addEventListener("icecandidate", this._onPCIceCandidateHandler); } this.peerConnection.addTrack(this.stream.getVideoTracks()[0], this.stream); }, _onPCNegotiationNeeded: function() { var connectionOffer; this.peerConnection.createOffer().then(function(offer) { connectionOffer = offer; return this.peerConnection.setLocalDescription(offer) }.bind(this)).then(function() { BX.desktop.onCustomEvent(this.window, Events.connectionOffer, [connectionOffer.sdp]); }.bind(this)); }, _onPCIceCandidate: function(e) { var candidate = e.candidate; if(candidate) { BX.desktop.onCustomEvent(this.window, Events.connectionIceCandidate, [candidate.toJSON()]); } }, _onConnectionAnswer: function(sdp) { if(this.peerConnection) { var sessionDescription = new RTCSessionDescription({ type: "answer", sdp: sdp }); this.peerConnection.setRemoteDescription(sessionDescription); } }, _onConnectionIceCandidate: function(candidate) { if(this.peerConnection) { this.peerConnection.addIceCandidate(candidate); } }, stopSendingVideo: function() { if(this.peerConnection) { this.peerConnection.close(); this.peerConnection.removeEventListener("negotiationneeded", this._onPCNegotiationNeededHandler); this.peerConnection.removeEventListener("icecandidate", this._onPCIceCandidateHandler); this.peerConnection = null; } BX.desktop.onCustomEvent(this.window, Events.connectionClose, []); }, setAudioMuted: function(audioMuted) { if(this.audioMuted == audioMuted) { return; } this.audioMuted = audioMuted; if(this.window && this.visible) { BX.desktop.onCustomEvent(this.window, Events.setAudioMuted, [this.audioMuted]); } }, show: function () { if (!BX.desktop) { return; } if(this.window) { this.window.BXDesktopWindow.ExecuteCommand("show"); BX.desktop.onCustomEvent(this.window, Events.setAudioMuted, [this.audioMuted]); BX.desktop.onCustomEvent(this.window, Events.setAvatars, [this.avatars]); if(this.stream) { this.sendVideo(); } } else { var params = { audioMuted: this.audioMuted, title: this.title, avatars: this.avatars }; this.window = BXDesktopSystem.ExecuteCommand('topmost.show.html', BX.desktop.getHtmlPage("", "window.FVC = new BX.Call.FloatingVideoContent(" + JSON.stringify(params) + ");")); setTimeout(function() { if(this.stream && this.visible) { this.sendVideo(); } }.bind(this), 2000) } this.visible = true; }, hide: function () { if (!this.window || !this.window.document) { return false; } this.stopSendingVideo(); this.window.BXDesktopWindow.ExecuteCommand("hide"); this.visible = false; }, close: function () { if (!this.window || !this.window.document) { return false; } this.window.BXDesktopWindow.ExecuteCommand("close"); this.window = null; this.visible = false; }, destroy: function () { if(this.window) { this.window.BXDesktopWindow.ExecuteCommand("close"); this.window = null; } this.stream = null; BX.desktop.removeCustomEvents(Events.onMainAreaClick); BX.desktop.removeCustomEvents(Events.onMicButtonClick); BX.desktop.removeCustomEvents(Events.onHangupButtonClick); } }; BX.Call.FloatingVideoContent = function (config) { this.stream = config.stream; this.audioMuted = config.audioMuted; this.avatars = config.avatars || {}; this.title = config.title || ""; this.talking = []; this.elements = { container: null, video: null, avatars: null, title: null, micButton: null, micButtonText: null, hangupButton: null }; this.render(); this.adjustWindow(); this.bindEventHandlers(); this.callAspectHorizontal = true; if (this.stream) { this.callAspectCheckInterval = setInterval(this.checkVideoAspect.bind(this), 500); } this.slavePeerConnection = null; this._onSlavePCIceCandidateHandler = this._onSlavePCIceCandidate.bind(this); this._onSlavePCIceConnectionStateChangeHandler = this._onSlavePCIceConnectionStateChange.bind(this); }; BX.Call.FloatingVideoContent.prototype = { bindEventHandlers: function () { BX.desktop.addCustomEvent(Events.setStream, this.setStream.bind(this)); BX.desktop.addCustomEvent(Events.setAudioMuted, this.setAudioMuted.bind(this)); BX.desktop.addCustomEvent(Events.setTitle, this.setTitle.bind(this)); BX.desktop.addCustomEvent(Events.setAvatars, this.setAvatars.bind(this)); BX.desktop.addCustomEvent(Events.setTalking, this.setTalking.bind(this)); BX.desktop.addCustomEvent(Events.connectionOffer, this._onConnectionOfferFromMain.bind(this)); BX.desktop.addCustomEvent(Events.connectionIceCandidate, this._onIceCandidateFromMain.bind(this)); BX.desktop.addCustomEvent(Events.connectionClose, this._onRTCconnectionClose.bind(this)); window.addEventListener("beforeunload", this.destroy.bind(this)); }, render: function () { var minCallWidth = this.stream ? VIDEO_WIDTH : AUDIO_WIDTH; var minCallHeight = this.stream ? VIDEO_HEIGHT : AUDIO_HEIGHT; var callOverlayStyle = { width: minCallWidth + 'px', height: minCallHeight + 'px' }; this.elements.container = BX.create("div", { props: {className: 'bx-messenger-call-float' + (this.stream ? '' : ' bx-messenger-call-float-audio')}, style: callOverlayStyle, events: { click: this.onMainAreaClick.bind(this) }, children: [ BX.create("div", { props: { className: 'bx-messenger-call-float-audio-unfold' }, children: [ this.elements.avatars = BX.create("div", { props: {className: 'bx-messenger-call-float-avatars'}, }), this.elements.title = BX.create("span", { props: {className: 'bx-messenger-call-float-button-text'}, text: this.title }) ] }), BX.create("div", { props: {className: 'bx-messenger-call-float-buttons'}, children: [ this.elements.micButton = BX.create("div", { props: {className: 'bx-messenger-call-float-button bx-messenger-call-float-button-mic' + (this.audioMuted ? ' bx-messenger-call-float-button-mic-disabled' : '')}, events: { click: this.onMicButtonClick.bind(this) }, children: [ BX.create("span", {props: {className: 'bx-messenger-call-float-button-icon'}}), this.elements.micButtonText = BX.create("span", { props: {className: 'bx-messenger-call-float-button-text'}, text: BX.message('IM_M_CALL_BTN_MIC') + ' ' + BX.message('IM_M_CALL_BTN_MIC_' + (this.audioMuted ? 'OFF' : 'ON')) }) ] }), this.elements.hangupButton = BX.create("div", { props: {className: 'bx-messenger-call-float-button bx-messenger-call-float-button-decline'}, events: { click: this.onHangupButtonClick.bind(this) }, children: [ BX.create("span", {props: {className: 'bx-messenger-call-float-button-icon'}}), BX.create("span", { props: {className: 'bx-messenger-call-float-button-text'}, text: BX.message('IM_M_CALL_BTN_HANGUP') }) ] }) ] }) ] }); this.elements.video = BX.create("video", { attrs: { autoplay: true, src: this.stream, }, props: {className: 'bx-messenger-call-float-video', volume: 0}, }); if(this.stream) { BX.prepend(this.elements.video, this.elements.container); } this.setAvatars(this.avatars); document.body.appendChild(this.elements.container); }, adjustWindow: function (width, height) { var minCallWidth = this.stream ? VIDEO_WIDTH : AUDIO_WIDTH; var minCallHeight = this.stream ? VIDEO_HEIGHT : AUDIO_HEIGHT; width = width || minCallWidth; height = height || minCallHeight; if(!this.stream) { var rows = Math.ceil(Object.keys(this.avatars).length / 4); height += rows * 74 + 10; // avatar height + top padding } this.elements.container.style.width = width +"px"; this.elements.container.style.height = height +"px"; BX.desktop.setWindowMinSize({Width: width, Height: height}); BX.desktop.setWindowResizable(false); BX.desktop.setWindowClosable(false); BX.desktop.setWindowResizable(false); BX.desktop.setWindowTitle(this.title); if (BXDesktopSystem.QuerySettings('global_topmost_x', null)) { BX.desktop.setWindowPosition({ X: parseInt(BXDesktopSystem.QuerySettings('global_topmost_x', STP_RIGHT)), Y: parseInt(BXDesktopSystem.QuerySettings('global_topmost_y', STP_TOP)), Width: width, Height: height, Mode: STP_FRONT }); if (!BX.browser.IsMac()) BX.desktop.setWindowPosition({ X: parseInt(BXDesktopSystem.QuerySettings('global_topmost_x', STP_RIGHT)), Y: parseInt(BXDesktopSystem.QuerySettings('global_topmost_y', STP_TOP)), Width: width, Height: height, Mode: STP_FRONT }); } else { BX.desktop.setWindowPosition({ X: STP_RIGHT, Y: STP_TOP, Width: width, Height: height, Mode: STP_FRONT }); if (!BX.browser.IsMac()) BX.desktop.setWindowPosition({ X: STP_RIGHT, Y: STP_TOP, Width: width, Height: height, Mode: STP_FRONT }); } }, checkVideoAspect: function() { if (this.elements.video.videoWidth < this.elements.video.videoHeight) { if (this.callAspectHorizontal) { this.callAspectHorizontal = false; BX.addClass(this.elements.container, 'bx-messenger-call-overlay-aspect-vertical'); BX.desktop.setWindowSize({ Width: VIDEO_HEIGHT, Height: VIDEO_WIDTH }); } } else { if (!this.callAspectHorizontal) { this.callAspectHorizontal = true; BX.removeClass(this.elements.container, 'bx-messenger-call-overlay-aspect-vertical'); BX.desktop.setWindowSize({ Width: VIDEO_WIDTH, Height: VIDEO_HEIGHT }); } } }, _onConnectionOfferFromMain: function(sdp) { if(this.slavePeerConnection) { //remove events this.slavePeerConnection.removeEventListener("icecandidate", this._onSlavePCIceCandidateHandler); this.slavePeerConnection.removeEventListener("iceconnectionstatechange", this._onSlavePCIceConnectionStateChangeHandler); this.slavePeerConnection.close(); } this.slavePeerConnection = new RTCPeerConnection(); this.slavePeerConnection.addEventListener("icecandidate", this._onSlavePCIceCandidateHandler); this.slavePeerConnection.addEventListener("iceconnectionstatechange", this._onSlavePCIceConnectionStateChangeHandler); var sessionDescription = new RTCSessionDescription({ type: "offer", sdp: sdp }); this.slavePeerConnection.setRemoteDescription(sessionDescription).then( function() { if(this.slavePeerConnection) { this.slavePeerConnection.createAnswer(/*{offerToReceiveVideo: true}*/).then( function(answer) { this.slavePeerConnection.setLocalDescription(answer); BX.desktop.onCustomEvent("main", Events.connectionAnswer, [answer.sdp]); }.bind(this) ) } }.bind(this) ) }, _onIceCandidateFromMain: function(candidate) { setTimeout(function() { if(this.slavePeerConnection) { this.slavePeerConnection.addIceCandidate(candidate); } }.bind(this), 200); }, _onRTCconnectionClose: function() { if(this.slavePeerConnection) { this.slavePeerConnection.removeEventListener("icecandidate", this._onSlavePCIceCandidateHandler); this.slavePeerConnection.removeEventListener("iceconnectionstatechange", this._onSlavePCIceConnectionStateChangeHandler); this.slavePeerConnection.close(); this.slavePeerConnection = null; } this.setStream(null); }, _onSlavePCIceCandidate: function(e) { var candidate = e.candidate; if (candidate) { BX.desktop.onCustomEvent("main", Events.connectionIceCandidate, [candidate.toJSON()]); } }, _onSlavePCIceConnectionStateChange: function(e) { if(this.slavePeerConnection.iceConnectionState === "connected") { this.setStream(this.slavePeerConnection.getRemoteStreams()[0]); } else if(this.slavePeerConnection.iceConnectionState === "disconnected") { this.setStream(null); this.slavePeerConnection.removeEventListener("icecandidate", this._onSlavePCIceCandidateHandler); this.slavePeerConnection.removeEventListener("iceconnectionstatechange", this._onSlavePCIceConnectionStateChangeHandler); } }, setStream: function(stream) { clearInterval(this.callAspectCheckInterval); this.stream = stream; var minCallWidth = this.stream ? VIDEO_WIDTH : AUDIO_WIDTH; var minCallHeight = this.stream ? VIDEO_HEIGHT : AUDIO_HEIGHT; var callOverlayStyle = { width: minCallWidth + 'px', height: minCallHeight + 'px' }; if(this.elements.container) { if(this.stream) { if(!this.elements.video.parentNode) { BX.prepend(this.elements.video, this.elements.container); BX.removeClass(this.elements.container, "bx-messenger-call-float-audio"); } this.elements.video.srcObject = this.stream; } else { BX.remove(this.elements.video); BX.addClass(this.elements.container, "bx-messenger-call-float-audio"); } BX.adjust(this.elements.container, {style: callOverlayStyle}); } if(this.stream) { this.callAspectCheckInterval = setInterval(this.checkVideoAspect.bind(this), 500); } this.adjustWindow(); }, setAudioMuted: function(audioMuted) { this.audioMuted = audioMuted; if(this.audioMuted) { BX.addClass(this.elements.micButton, "bx-messenger-call-float-button-mic-disabled"); BX.adjust(this.elements.micButtonText, {text: BX.message('IM_M_CALL_BTN_MIC') + ' ' + BX.message('IM_M_CALL_BTN_MIC_OFF')}); } else { BX.removeClass(this.elements.micButton, "bx-messenger-call-float-button-mic-disabled"); BX.adjust(this.elements.micButtonText, {text: BX.message('IM_M_CALL_BTN_MIC') + ' ' + BX.message('IM_M_CALL_BTN_MIC_ON')}); } }, setTitle: function(title) { this.title = title; this.elements.title.innerText = title; BX.desktop.setWindowTitle(this.title); }, setAvatars: function(avatars) { this.avatars = avatars; this.elements.avatars.innerHTML = ""; for (var userId in this.avatars) { var avatar = this.avatars[userId]; var a = BX.create("div", { props: {className: "bx-messenger-call-float-avatar"}, dataset: { userId: userId } }); if (avatar != '') { a.style.backgroundImage = "url('" + avatar + "')"; } this.elements.avatars.appendChild(a); } this.adjustWindow(); }, setTalking: function(talking) { this.talking = talking; if(this.elements.avatars) { for(var i = 0; i < this.elements.avatars.children.length; i++) { var element = this.elements.avatars.children[i]; var userId = Number(element.dataset.userId); if (this.talking.includes(userId)) { element.classList.add("talking"); } else { //element.classList.add("talking"); element.classList.remove("talking"); } } } }, onMainAreaClick: function(e) { BX.desktop.onCustomEvent("main", Events.onMainAreaClick, []); e.stopPropagation(); }, onMicButtonClick: function(e) { BX.desktop.onCustomEvent("main", Events.onMicButtonClick, [this.audioMuted]); e.stopPropagation(); }, onHangupButtonClick: function(e) { BX.desktop.onCustomEvent("main", Events.onHangupButtonClick, []); e.stopPropagation(); }, destroy: function() { this.stream = null; BX.desktop.removeCustomEvents(Events.setStream); BX.desktop.removeCustomEvents(Events.setAudioMuted); } }; })();