'use strict';
// Last time updated: 2018-07-28 4:49:52 AM UTC
// _________________________
// RTCMultiConnection v3.4.5
// Open-Sourced: https://github.com/muaz-khan/RTCMultiConnection
// --------------------------------------------------
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// --------------------------------------------------
"use strict";
var socketUsers = [];
window.RTCMultiConnection = function (roomid, forceOptions) {
function SocketConnection(connection, connectCallback) {
function onMessageEvent(message) {
if (message.remoteUserId == connection.userid) {
if (connection.peers[message.sender] && connection.peers[message.sender].extra != message.message.extra && (connection.peers[message.sender].extra = message.extra, connection.onExtraDataUpdated({
userid: message.sender,
extra: message.extra
})), "next-possible-initiator" === message.message) {
if (connection.nextPossibleInitiatorIfThisUserLeave) return;
return void(connection.nextPossibleInitiatorIfThisUserLeave = message.sender)
}
if (message.message.streamSyncNeeded && connection.peers[message.sender]) {
var stream = connection.streamEvents[message.message.streamid];
if (!stream || !stream.stream) return;
var action = message.message.action;
if ("ended" === action || "inactive" === action || "stream-removed" === action) return connection.peersBackup[stream.userid] && (stream.extra = connection.peersBackup[stream.userid].extra), void connection.onstreamended(stream);
var type = "both" != message.message.type ? message.message.type : null;
return void("function" == typeof stream.stream[action] && stream.stream[action](type))
}
if ("connectWithAllParticipants" === message.message) return connection.broadcasters.indexOf(message.sender) === -1 && connection.broadcasters.push(message.sender), void mPeer.onNegotiationNeeded({allParticipants: connection.getAllParticipants(message.sender)}, message.sender);
if ("removeFromBroadcastersList" === message.message) return void(connection.broadcasters.indexOf(message.sender) !== -1 && (delete connection.broadcasters[connection.broadcasters.indexOf(message.sender)], connection.broadcasters = removeNullEntries(connection.broadcasters)));
if ("dropPeerConnection" === message.message) return void connection.deletePeer(message.sender);
if (message.message.allParticipants) return message.message.allParticipants.indexOf(message.sender) === -1 && message.message.allParticipants.push(message.sender), void message.message.allParticipants.forEach(function (participant) {
mPeer[connection.peers[participant] ? "renegotiatePeer" : "createNewPeer"](participant, {
localPeerSdpConstraints: {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: !!connection.session.oneway || "one-way" === connection.direction,
isDataOnly: isData(connection.session)
})
});
if (message.message.newParticipant) {
if (message.message.newParticipant == connection.userid) return;
if (connection.peers[message.message.newParticipant]) return;
return void mPeer.createNewPeer(message.message.newParticipant, message.message.userPreferences || {
localPeerSdpConstraints: {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: !!connection.session.oneway || "one-way" === connection.direction,
isDataOnly: isData(connection.session)
})
}
if (message.message.readyForOffer || message.message.addMeAsBroadcaster) {
if (connection.attachStreams.length && (connection.waitingForLocalMedia = !1), connection.waitingForLocalMedia) return void setTimeout(function () {
onMessageEvent(message)
}, 1e3);
connection.addNewBroadcaster(message.sender)
}
if (message.message.newParticipationRequest && message.sender !== connection.userid) {
connection.peers[message.sender] && connection.deletePeer(message.sender);
var userPreferences = {
extra: message.extra || {},
localPeerSdpConstraints: message.message.remotePeerSdpConstraints || {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: message.message.localPeerSdpConstraints || {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: "undefined" != typeof message.message.isOneWay ? message.message.isOneWay : !!connection.session.oneway || "one-way" === connection.direction,
isDataOnly: "undefined" != typeof message.message.isDataOnly ? message.message.isDataOnly : isData(connection.session),
dontGetRemoteStream: "undefined" != typeof message.message.isOneWay ? message.message.isOneWay : !!connection.session.oneway || "one-way" === connection.direction,
dontAttachLocalStream: !!message.message.dontGetRemoteStream,
connectionDescription: message,
successCallback: function () {
("undefined" != typeof message.message.isOneWay ? message.message.isOneWay : !!connection.session.oneway || "one-way" === connection.direction) && connection.addNewBroadcaster(message.sender, userPreferences), (connection.session.oneway || "one-way" === connection.direction || isData(connection.session)) && connection.addNewBroadcaster(message.sender, userPreferences)
}
};
return void connection.onNewParticipant(message.sender, userPreferences)
}
return message.message.shiftedModerationControl ? void connection.onShiftedModerationControl(message.sender, message.message.broadcasters) : (message.message.changedUUID && connection.peers[message.message.oldUUID] && (connection.peers[message.message.newUUID] = connection.peers[message.message.oldUUID], delete connection.peers[message.message.oldUUID]), message.message.userLeft ? (mPeer.onUserLeft(message.sender), void(message.message.autoCloseEntireSession && connection.leave())) : void mPeer.addNegotiatedMessage(message.message, message.sender))
}
}
var parameters = "";
parameters += "?userid=" + connection.userid, parameters += "&sessionid=" + connection.sessionid, parameters += "&msgEvent=" + connection.socketMessageEvent, parameters += "&socketCustomEvent=" + connection.socketCustomEvent, parameters += "&autoCloseEntireSession=" + !!connection.autoCloseEntireSession, connection.session.broadcast === !0 && (parameters += "&oneToMany=true"), parameters += "&maxParticipantsAllowed=" + connection.maxParticipantsAllowed, connection.enableScalableBroadcast && (parameters += "&enableScalableBroadcast=true", parameters += "&maxRelayLimitPerUser=" + (connection.maxRelayLimitPerUser || 2)), connection.socketCustomParameters && (parameters += connection.socketCustomParameters);
try {
io.sockets = {}
} catch (e) {
}
if (connection.socketURL || (connection.socketURL = "/"), "/" != connection.socketURL.substr(connection.socketURL.length - 1, 1)) throw'"socketURL" MUST end with a slash.';
connection.enableLogs && ("/" == connection.socketURL ? console.info("socket.io is connected at: ", location.origin + "/") : console.info("socket.io is connected at: ", connection.socketURL));
try {
connection.socket = io(connection.socketURL + parameters)
} catch (e) {
connection.socket = io.connect(connection.socketURL + parameters, connection.socketOptions)
}
connection.socket.isIO = !0;
var mPeer = connection.multiPeersHandler;
connection.socket.on("extra-data-updated", function (remoteUserId, extra) {
connection.peers[remoteUserId] && (connection.peers[remoteUserId].extra = extra, connection.onExtraDataUpdated({
userid: remoteUserId,
extra: extra
}), connection.peersBackup[remoteUserId] || (connection.peersBackup[remoteUserId] = {
userid: remoteUserId,
extra: {}
}), connection.peersBackup[remoteUserId].extra = extra)
}), connection.socket.on(connection.socketMessageEvent, onMessageEvent), connection.socket.on("user-left", function (userid) {
onUserLeft(userid), connection.onUserStatusChanged({
userid: userid,
status: "offline",
extra: connection.peers[userid] ? connection.peers[userid].extra || {} : {}
});
var eventObject = {userid: userid, extra: {}};
connection.peersBackup[eventObject.userid] && (eventObject.extra = connection.peersBackup[eventObject.userid].extra), connection.onleave(eventObject), connection.nextPossibleInitiatorIfThisUserLeave === userid && (connection.nextPossibleInitiatorIfThisUserLeave = null, connection.open(connection.sessionid))
});
var alreadyConnected = !1;
connection.socket.resetProps = function () {
alreadyConnected = !1
}, connection.socket.on("connect", function () {
alreadyConnected || (alreadyConnected = !0, connection.enableLogs && console.info("socket.io connection is opened."), setTimeout(function () {
connection.socket.emit("extra-data-updated", connection.extra), connectCallback && connectCallback(connection.socket)
}, 1e3))
}), connection.socket.on("disconnect", function () {
connection.enableLogs && console.warn("socket.io connection is closed")
}), connection.socket.on("join-with-password", function (remoteUserId) {
connection.onJoinWithPassword(remoteUserId)
}), connection.socket.on("invalid-password", function (remoteUserId, oldPassword) {
connection.onInvalidPassword(remoteUserId, oldPassword)
}), connection.socket.on("password-max-tries-over", function (remoteUserId) {
connection.onPasswordMaxTriesOver(remoteUserId)
}), connection.socket.on("user-disconnected", function (remoteUserId) {
remoteUserId !== connection.userid && (connection.onUserStatusChanged({
userid: remoteUserId,
status: "offline",
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra || {} : {}
}), connection.deletePeer(remoteUserId))
}), connection.socket.on("user-connected", function (userid) {
console.log('user connected');
userid !== connection.userid && connection.onUserStatusChanged({
userid: userid,
status: "online",
extra: connection.peers[userid] ? connection.peers[userid].extra || {} : {}
})
}), connection.socket.on("closed-entire-session", function (sessionid, extra) {
connection.leave(), connection.onEntireSessionClosed({
sessionid: sessionid,
userid: sessionid,
extra: extra
})
}), connection.socket.on("userid-already-taken", function (useridAlreadyTaken, yourNewUserId) {
connection.isInitiator = !1, connection.userid = yourNewUserId, connection.onUserIdAlreadyTaken(useridAlreadyTaken, yourNewUserId)
}), connection.socket.on("logs", function (log) {
connection.enableLogs && console.debug("server-logs", log)
}), connection.socket.on("number-of-broadcast-viewers-updated", function (data) {
connection.onNumberOfBroadcastViewersUpdated(data)
}), connection.socket.on("room-full", function (roomid) {
connection.onRoomFull(roomid)
}), connection.socket.on("become-next-modrator", function (sessionid) {
sessionid == connection.sessionid && setTimeout(function () {
connection.open(sessionid), connection.socket.emit("shift-moderator-control-on-disconnect")
}, 1e3)
})
}
function MultiPeers(connection) {
function initFileBufferReader() {
connection.fbr = new FileBufferReader, connection.fbr.onProgress = function (chunk) {
connection.onFileProgress(chunk)
}, connection.fbr.onBegin = function (file) {
connection.onFileStart(file)
}, connection.fbr.onEnd = function (file) {
connection.onFileEnd(file)
}
}
var self = this, skipPeers = ["getAllParticipants", "getLength", "selectFirst", "streams", "send", "forEach"];
connection.peersBackup = {}, connection.peers = {
getLength: function () {
var numberOfPeers = 0;
for (var peer in this) skipPeers.indexOf(peer) == -1 && numberOfPeers++;
return numberOfPeers
}, selectFirst: function () {
var firstPeer;
for (var peer in this) skipPeers.indexOf(peer) == -1 && (firstPeer = this[peer]);
return firstPeer
}, getAllParticipants: function (sender) {
var allPeers = [];
for (var peer in this) skipPeers.indexOf(peer) == -1 && peer != sender && allPeers.push(peer);
return allPeers
}, forEach: function (callbcak) {
this.getAllParticipants().forEach(function (participant) {
callbcak(connection.peers[participant])
})
}, send: function (data, remoteUserId) {
var that = this;
if (!isNull(data.size) && !isNull(data.type)) return void self.shareFile(data, remoteUserId);
if (!("text" === data.type || data instanceof ArrayBuffer || data instanceof DataView)) return void TextSender.send({
text: data,
channel: this,
connection: connection,
remoteUserId: remoteUserId
});
if ("text" === data.type && (data = JSON.stringify(data)), remoteUserId) {
var remoteUser = connection.peers[remoteUserId];
if (remoteUser) return remoteUser.channels.length ? void remoteUser.channels.forEach(function (channel) {
channel.send(data)
}) : (connection.peers[remoteUserId].createDataChannel(), connection.renegotiate(remoteUserId), void setTimeout(function () {
that.send(data, remoteUserId)
}, 3e3))
}
this.getAllParticipants().forEach(function (participant) {
return that[participant].channels.length ? void that[participant].channels.forEach(function (channel) {
channel.send(data)
}) : (connection.peers[participant].createDataChannel(), connection.renegotiate(participant), void setTimeout(function () {
that[participant].channels.forEach(function (channel) {
channel.send(data)
})
}, 3e3))
})
}
}, this.uuid = connection.userid, this.getLocalConfig = function (remoteSdp, remoteUserId, userPreferences) {
return userPreferences || (userPreferences = {}), {
streamsToShare: userPreferences.streamsToShare || {},
rtcMultiConnection: connection,
connectionDescription: userPreferences.connectionDescription,
userid: remoteUserId,
localPeerSdpConstraints: userPreferences.localPeerSdpConstraints,
remotePeerSdpConstraints: userPreferences.remotePeerSdpConstraints,
dontGetRemoteStream: !!userPreferences.dontGetRemoteStream,
dontAttachLocalStream: !!userPreferences.dontAttachLocalStream,
renegotiatingPeer: !!userPreferences.renegotiatingPeer,
peerRef: userPreferences.peerRef,
channels: userPreferences.channels || [],
onLocalSdp: function (localSdp) {
self.onNegotiationNeeded(localSdp, remoteUserId)
},
onLocalCandidate: function (localCandidate) {
localCandidate = OnIceCandidateHandler.processCandidates(connection, localCandidate), localCandidate && self.onNegotiationNeeded(localCandidate, remoteUserId)
},
remoteSdp: remoteSdp,
onDataChannelMessage: function (message) {
if (!connection.fbr && connection.enableFileSharing && initFileBufferReader(), "string" == typeof message || !connection.enableFileSharing) return void self.onDataChannelMessage(message, remoteUserId);
var that = this;
return message instanceof ArrayBuffer || message instanceof DataView ? void connection.fbr.convertToObject(message, function (object) {
that.onDataChannelMessage(object)
}) : message.readyForNextChunk ? void connection.fbr.getNextChunk(message, function (nextChunk, isLastChunk) {
connection.peers[remoteUserId].channels.forEach(function (channel) {
channel.send(nextChunk)
})
}, remoteUserId) : message.chunkMissing ? void connection.fbr.chunkMissing(message) : void connection.fbr.addChunk(message, function (promptNextChunk) {
connection.peers[remoteUserId].peer.channel.send(promptNextChunk)
})
},
onDataChannelError: function (error) {
self.onDataChannelError(error, remoteUserId)
},
onDataChannelOpened: function (channel) {
self.onDataChannelOpened(channel, remoteUserId)
},
onDataChannelClosed: function (event) {
self.onDataChannelClosed(event, remoteUserId)
},
onRemoteStream: function (stream) {
connection.peers[remoteUserId] && connection.peers[remoteUserId].streams.push(stream), self.onGettingRemoteMedia(stream, remoteUserId)
},
onRemoteStreamRemoved: function (stream) {
self.onRemovingRemoteMedia(stream, remoteUserId)
},
onPeerStateChanged: function (states) {
self.onPeerStateChanged(states), "new" === states.iceConnectionState && self.onNegotiationStarted(remoteUserId, states), "connected" === states.iceConnectionState && self.onNegotiationCompleted(remoteUserId, states), states.iceConnectionState.search(/closed|failed/gi) !== -1 && (self.onUserLeft(remoteUserId), self.disconnectWith(remoteUserId))
}
}
}, this.createNewPeer = function (remoteUserId, userPreferences) {
if (!(connection.maxParticipantsAllowed <= connection.getAllParticipants().length)) {
if (userPreferences = userPreferences || {}, connection.isInitiator && connection.session.audio && "two-way" === connection.session.audio && !userPreferences.streamsToShare && (userPreferences.isOneWay = !1, userPreferences.isDataOnly = !1, userPreferences.session = connection.session), !userPreferences.isOneWay && !userPreferences.isDataOnly) return userPreferences.isOneWay = !0, void this.onNegotiationNeeded({
enableMedia: !0,
userPreferences: userPreferences
}, remoteUserId);
userPreferences = connection.setUserPreferences(userPreferences, remoteUserId);
var localConfig = this.getLocalConfig(null, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig), this.checkIfNextPossibleInitiator(remoteUserId)
}
}, this.createAnsweringPeer = function (remoteSdp, remoteUserId, userPreferences) {
userPreferences = connection.setUserPreferences(userPreferences || {}, remoteUserId);
var localConfig = this.getLocalConfig(remoteSdp, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig), this.checkIfNextPossibleInitiator(remoteUserId)
}, this.renegotiatePeer = function (remoteUserId, userPreferences, remoteSdp) {
if (!connection.peers[remoteUserId]) return void(connection.enableLogs && console.error("Peer (" + remoteUserId + ") does not exist. Renegotiation skipped."));
userPreferences || (userPreferences = {}), userPreferences.renegotiatingPeer = !0, userPreferences.peerRef = connection.peers[remoteUserId].peer, userPreferences.channels = connection.peers[remoteUserId].channels;
var localConfig = this.getLocalConfig(remoteSdp, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig), this.checkIfNextPossibleInitiator(remoteUserId)
}, this.replaceTrack = function (track, remoteUserId, isVideoTrack) {
if (!connection.peers[remoteUserId]) throw"This peer (" + remoteUserId + ") does not exist.";
var peer = connection.peers[remoteUserId].peer;
return peer.getSenders && "function" == typeof peer.getSenders && peer.getSenders().length ? void peer.getSenders().forEach(function (rtpSender) {
isVideoTrack && rtpSender.track instanceof VideoStreamTrack && (connection.peers[remoteUserId].peer.lastVideoTrack = rtpSender.track, rtpSender.replaceTrack(track)), !isVideoTrack && rtpSender.track instanceof AudioStreamTrack && (connection.peers[remoteUserId].peer.lastAudioTrack = rtpSender.track, rtpSender.replaceTrack(track))
}) : (console.warn("RTPSender.replaceTrack is NOT supported."), void this.renegotiatePeer(remoteUserId))
}, this.onNegotiationNeeded = function (message, remoteUserId) {
}, this.addNegotiatedMessage = function (message, remoteUserId) {
if (message.type && message.sdp) return "answer" == message.type && connection.peers[remoteUserId] && connection.peers[remoteUserId].addRemoteSdp(message), "offer" == message.type && (message.renegotiatingPeer ? this.renegotiatePeer(remoteUserId, null, message) : this.createAnsweringPeer(message, remoteUserId)), void(connection.enableLogs && console.log("Remote peer's sdp:", message.sdp));
if (message.candidate) return connection.peers[remoteUserId] && connection.peers[remoteUserId].addRemoteCandidate(message), void(connection.enableLogs && console.log("Remote peer's candidate pairs:", message.candidate));
if (message.enableMedia) {
connection.session = message.userPreferences.session || connection.session, connection.session.oneway && connection.attachStreams.length && (connection.attachStreams = []), message.userPreferences.isDataOnly && connection.attachStreams.length && (connection.attachStreams.length = []);
var streamsToShare = {};
connection.attachStreams.forEach(function (stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
}
}), message.userPreferences.streamsToShare = streamsToShare, self.onNegotiationNeeded({
readyForOffer: !0,
userPreferences: message.userPreferences
}, remoteUserId)
}
message.readyForOffer && connection.onReadyForOffer(remoteUserId, message.userPreferences)
}, this.connectNewParticipantWithAllBroadcasters = function (newParticipantId, userPreferences, broadcastersList) {
if (!connection.socket.isIO && (broadcastersList = (broadcastersList || "").split("|-,-|"), broadcastersList.length)) {
var firstBroadcaster, remainingBroadcasters = [];
broadcastersList.forEach(function (list) {
list = (list || "").replace(/ /g, ""), list.length && (firstBroadcaster ? remainingBroadcasters.push(list) : firstBroadcaster = list)
}), firstBroadcaster && (self.onNegotiationNeeded({
newParticipant: newParticipantId,
userPreferences: userPreferences || !1
}, firstBroadcaster), remainingBroadcasters.length && setTimeout(function () {
self.connectNewParticipantWithAllBroadcasters(newParticipantId, userPreferences, remainingBroadcasters.join("|-,-|"))
}, 3e3))
}
}, this.onGettingRemoteMedia = function (stream, remoteUserId) {
}, this.onRemovingRemoteMedia = function (stream, remoteUserId) {
}, this.onGettingLocalMedia = function (localStream) {
}, this.onLocalMediaError = function (error, constraints) {
connection.onMediaError(error, constraints)
}, this.shareFile = function (file, remoteUserId) {
if (!connection.enableFileSharing) throw'"connection.enableFileSharing" is false.';
initFileBufferReader(), connection.fbr.readAsArrayBuffer(file, function (uuid) {
var arrayOfUsers = connection.getAllParticipants();
remoteUserId && (arrayOfUsers = [remoteUserId]), arrayOfUsers.forEach(function (participant) {
connection.fbr.getNextChunk(uuid, function (nextChunk) {
connection.peers[participant].channels.forEach(function (channel) {
channel.send(nextChunk)
})
}, participant)
})
}, {
userid: connection.userid,
chunkSize: "Firefox" === DetectRTC.browser.name ? 15e3 : connection.chunkSize || 0
})
};
var textReceiver = new TextReceiver(connection);
this.onDataChannelMessage = function (message, remoteUserId) {
textReceiver.receive(JSON.parse(message), remoteUserId, connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {})
}, this.onDataChannelClosed = function (event, remoteUserId) {
event.userid = remoteUserId, event.extra = connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {}, connection.onclose(event)
}, this.onDataChannelError = function (error, remoteUserId) {
error.userid = remoteUserId, event.extra = connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {}, connection.onerror(error)
}, this.onDataChannelOpened = function (channel, remoteUserId) {
return connection.peers[remoteUserId].channels.length ? void(connection.peers[remoteUserId].channels = [channel]) : (connection.peers[remoteUserId].channels.push(channel), void connection.onopen({
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
channel: channel
}))
}, this.onPeerStateChanged = function (state) {
connection.onPeerStateChanged(state)
}, this.onNegotiationStarted = function (remoteUserId, states) {
}, this.onNegotiationCompleted = function (remoteUserId, states) {
}, this.getRemoteStreams = function (remoteUserId) {
return remoteUserId = remoteUserId || connection.peers.getAllParticipants()[0], connection.peers[remoteUserId] ? connection.peers[remoteUserId].streams : []
}, this.checkIfNextPossibleInitiator = function (remoteUserId) {
connection.sessionid !== remoteUserId && (connection.autoCloseEntireSession || connection.isInitiator && connection.getAllParticipants().length > 1 || connection.socket.emit(connection.socketMessageEvent, {
remoteUserId: remoteUserId,
message: "next-possible-initiator",
sender: connection.userid
}))
}
}
function fireEvent(obj, eventName, args) {
if ("undefined" != typeof CustomEvent) {
var eventDetail = {arguments: args, __exposedProps__: args},
event = new CustomEvent(eventName, eventDetail);
obj.dispatchEvent(event)
}
}
function setHarkEvents(connection, streamEvent) {
if (streamEvent.stream && streamEvent.stream.getAudioTracks && streamEvent.stream.getAudioTracks().length) {
if (!connection || !streamEvent) throw"Both arguments are required.";
if (connection.onspeaking && connection.onsilence) {
if ("undefined" == typeof hark) throw"hark.js not found.";
hark(streamEvent.stream, {
onspeaking: function () {
connection.onspeaking(streamEvent)
}, onsilence: function () {
connection.onsilence(streamEvent)
}, onvolumechange: function (volume, threshold) {
connection.onvolumechange && connection.onvolumechange(merge({
volume: volume,
threshold: threshold
}, streamEvent))
}
})
}
}
}
function setMuteHandlers(connection, streamEvent) {
streamEvent.stream && streamEvent.stream && streamEvent.stream.addEventListener && (streamEvent.stream.addEventListener("mute", function (event) {
event = connection.streamEvents[streamEvent.streamid], event.session = {
audio: "audio" === event.muteType,
video: "video" === event.muteType
}, connection.onmute(event)
}, !1), streamEvent.stream.addEventListener("unmute", function (event) {
event = connection.streamEvents[streamEvent.streamid], event.session = {
audio: "audio" === event.unmuteType,
video: "video" === event.unmuteType
}, connection.onunmute(event)
}, !1))
}
function getRandomString() {
if (window.crypto && window.crypto.getRandomValues && navigator.userAgent.indexOf("Safari") === -1) {
for (var a = window.crypto.getRandomValues(new Uint32Array(3)), token = "", i = 0, l = a.length; i < l; i++) token += a[i].toString(36);
return token
}
return (Math.random() * (new Date).getTime()).toString(36).replace(/\./g, "")
}
function getRMCMediaElement(stream, callback, connection) {
if (!connection.autoCreateMediaElement) return void callback({});
var isAudioOnly = !1;
!stream.getVideoTracks || stream.getVideoTracks().length || stream.isVideo || stream.isScreen || (isAudioOnly = !0), "Firefox" === DetectRTC.browser.name && (connection.session.video || connection.session.screen) && (isAudioOnly = !1);
var mediaElement = document.createElement(isAudioOnly ? "audio" : "video");
mediaElement.srcObject = stream;
try {
mediaElement.setAttributeNode(document.createAttribute("autoplay")), mediaElement.setAttributeNode(document.createAttribute("playsinline")), mediaElement.setAttributeNode(document.createAttribute("controls"))
} catch (e) {
mediaElement.setAttribute("autoplay", !0), mediaElement.setAttribute("playsinline", !0), mediaElement.setAttribute("controls", !0)
}
if ("Firefox" === DetectRTC.browser.name) {
var streamEndedEvent = "ended";
"oninactive" in mediaElement && (streamEndedEvent = "inactive"), mediaElement.addEventListener(streamEndedEvent, function () {
if (currentUserMediaRequest.remove(stream.idInstance), "local" === stream.type) {
streamEndedEvent = "ended", "oninactive" in stream && (streamEndedEvent = "inactive"), StreamsHandler.onSyncNeeded(stream.streamid, streamEndedEvent), connection.attachStreams.forEach(function (aStream, idx) {
stream.streamid === aStream.streamid && delete connection.attachStreams[idx]
});
var newStreamsArray = [];
connection.attachStreams.forEach(function (aStream) {
aStream && newStreamsArray.push(aStream)
}), connection.attachStreams = newStreamsArray;
var streamEvent = connection.streamEvents[stream.streamid];
if (streamEvent) return void connection.onstreamended(streamEvent);
this.parentNode && this.parentNode.removeChild(this)
}
}, !1)
}
var played = mediaElement.play();
if ("undefined" != typeof played) {
var cbFired = !1;
setTimeout(function () {
cbFired || (cbFired = !0, callback(mediaElement))
}, 1e3), played.then(function () {
cbFired || (cbFired = !0, callback(mediaElement))
})["catch"](function (error) {
cbFired || (cbFired = !0, callback(mediaElement))
})
} else callback(mediaElement)
}
function listenEventHandler(eventName, eventHandler) {
window.removeEventListener(eventName, eventHandler), window.addEventListener(eventName, eventHandler, !1)
}
function removeNullEntries(array) {
var newArray = [];
return array.forEach(function (item) {
item && newArray.push(item)
}), newArray
}
function isData(session) {
return !session.audio && !session.video && !session.screen && session.data
}
function isNull(obj) {
return "undefined" == typeof obj
}
function isString(obj) {
return "string" == typeof obj
}
function isAudioPlusTab(connection, audioPlusTab) {
return (!connection.session.audio || "two-way" !== connection.session.audio) && ("Firefox" === DetectRTC.browser.name && audioPlusTab !== !1 || !("Chrome" !== DetectRTC.browser.name || DetectRTC.browser.version < 50) && (typeof audioPlusTab === !0 || !("undefined" != typeof audioPlusTab || !connection.session.audio || !connection.session.screen || connection.session.video) && (audioPlusTab = !0, !0)))
}
function getAudioScreenConstraints(screen_constraints) {
return "Firefox" === DetectRTC.browser.name || "Chrome" === DetectRTC.browser.name && {
mandatory: {
chromeMediaSource: screen_constraints.mandatory.chromeMediaSource,
chromeMediaSourceId: screen_constraints.mandatory.chromeMediaSourceId
}
}
}
function setCordovaAPIs() {
if ("undefined" != typeof cordova && "undefined" != typeof cordova.plugins && "undefined" != typeof cordova.plugins.iosrtc) {
var iosrtc = cordova.plugins.iosrtc;
window.webkitRTCPeerConnection = iosrtc.RTCPeerConnection, window.RTCSessionDescription = iosrtc.RTCSessionDescription, window.RTCIceCandidate = iosrtc.RTCIceCandidate, window.MediaStream = iosrtc.MediaStream, window.MediaStreamTrack = iosrtc.MediaStreamTrack, navigator.getUserMedia = navigator.webkitGetUserMedia = iosrtc.getUserMedia, iosrtc.debug.enable("iosrtc*"), "function" == typeof iosrtc.selectAudioOutput && iosrtc.selectAudioOutput(window.iOSDefaultAudioOutputDevice || "speaker"), iosrtc.registerGlobals()
}
}
function setSdpConstraints(config) {
var sdpConstraints = {
OfferToReceiveAudio: !!config.OfferToReceiveAudio,
OfferToReceiveVideo: !!config.OfferToReceiveVideo
}, oldBrowser = !window.enableAdapter;
return "Chrome" === DetectRTC.browser.name && DetectRTC.browser.version >= 60, "Firefox" === DetectRTC.browser.name && DetectRTC.browser.version >= 54 && (oldBrowser = !1), oldBrowser && (sdpConstraints = {
mandatory: sdpConstraints,
optional: [{VoiceActivityDetection: !1}]
}), sdpConstraints
}
function PeerInitiator(config) {
function oldAddRemoteSdp(remoteSdp, cb) {
cb = cb || function () {
}, "Safari" !== DetectRTC.browser.name && (remoteSdp.sdp = connection.processSdp(remoteSdp.sdp)), peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), cb, function (error) {
connection.enableLogs && console.error("setRemoteDescription failed", "\n", error, "\n", remoteSdp.sdp), cb()
})
}
function setChannelEvents(channel) {
channel.binaryType = "arraybuffer", channel.onmessage = function (event) {
config.onDataChannelMessage(event.data)
}, channel.onopen = function () {
config.onDataChannelOpened(channel)
}, channel.onerror = function (error) {
config.onDataChannelError(error)
}, channel.onclose = function (event) {
config.onDataChannelClosed(event)
}, channel.internalSend = channel.send, channel.send = function (data) {
"open" === channel.readyState && channel.internalSend(data)
}, peer.channel = channel
}
function oldCreateOfferOrAnswer(_method) {
peer[_method](function (localSdp) {
"Safari" !== DetectRTC.browser.name && (localSdp.sdp = connection.processSdp(localSdp.sdp)), peer.setLocalDescription(localSdp, function () {
connection.trickleIce && (config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || !1,
renegotiatingPeer: !!config.renegotiatingPeer || !1,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
}), connection.onSettingLocalDescription(self))
}, function (error) {
connection.enableLogs && console.error("setLocalDescription-error", error)
})
}, function (error) {
connection.enableLogs && console.error("sdp-" + _method + "-error", error)
}, defaults.sdpConstraints)
}
function createOfferOrAnswer(_method) {
return window.enableAdapter ? void peer[_method](defaults.sdpConstraints).then(function (localSdp) {
"Safari" !== DetectRTC.browser.name && (localSdp.sdp = connection.processSdp(localSdp.sdp)), peer.setLocalDescription(localSdp).then(function () {
connection.trickleIce && (config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || !1,
renegotiatingPeer: !!config.renegotiatingPeer || !1,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
}), connection.onSettingLocalDescription(self))
}, function (error) {
connection.enableLogs && console.error("setLocalDescription error", error)
})
}, function (error) {
connection.enableLogs && console.error("sdp-error", error)
}) : oldCreateOfferOrAnswer(_method)
}
if ("undefined" != typeof window.RTCPeerConnection ? RTCPeerConnection = window.RTCPeerConnection : "undefined" != typeof mozRTCPeerConnection ? RTCPeerConnection = mozRTCPeerConnection : "undefined" != typeof webkitRTCPeerConnection && (RTCPeerConnection = webkitRTCPeerConnection), RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription, RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate, MediaStreamTrack = window.MediaStreamTrack, !RTCPeerConnection) throw"WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.";
var connection = config.rtcMultiConnection;
this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra, this.userid = config.userid, this.streams = [], this.channels = config.channels || [], this.connectionDescription = config.connectionDescription, this.addStream = function (session) {
connection.addStream(session, self.userid)
}, this.removeStream = function (streamid) {
connection.removeStream(streamid, self.userid);
};
var self = this;
config.remoteSdp && (this.connectionDescription = config.remoteSdp.connectionDescription);
var allRemoteStreams = {};
defaults.sdpConstraints = setSdpConstraints({OfferToReceiveAudio: !0, OfferToReceiveVideo: !0});
var peer, renegotiatingPeer = !!config.renegotiatingPeer;
config.remoteSdp && (renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer);
var localStreams = [];
if (connection.attachStreams.forEach(function (stream) {
stream && localStreams.push(stream)
}), renegotiatingPeer) peer = config.peerRef; else {
var iceTransports = "all";
(connection.candidates.turn || connection.candidates.relay) && (connection.candidates.stun || connection.candidates.reflexive || connection.candidates.host || (iceTransports = "relay"));
try {
var params = {};
"Chrome" !== DetectRTC.browser.name && (params.iceServers = connection.iceServers), "Chrome" === DetectRTC.browser.name && (params = {
iceServers: connection.iceServers,
iceTransportPolicy: connection.iceTransportPolicy || iceTransports
}), connection.iceServers.length || (params = null, connection.optionalArgument = null), peer = new RTCPeerConnection(params, connection.optionalArgument)
} catch (e) {
try {
var params = {iceServers: connection.iceServers};
peer = new RTCPeerConnection(params)
} catch (e) {
peer = new RTCPeerConnection
}
}
}
!peer.getRemoteStreams && peer.getReceivers && (peer.getRemoteStreams = function () {
var stream = new MediaStream;
return peer.getReceivers().forEach(function (receiver) {
stream.addTrack(receiver.track)
}), [stream]
}), !peer.getLocalStreams && peer.getSenders && (peer.getLocalStreams = function () {
var stream = new MediaStream;
return peer.getSenders().forEach(function (sender) {
stream.addTrack(sender.track)
}), [stream]
}), peer.onicecandidate = function (event) {
if (event.candidate) connection.trickleIce && config.onLocalCandidate({
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex
}); else if (!connection.trickleIce) {
var localSdp = peer.localDescription;
config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || !1,
renegotiatingPeer: !!config.renegotiatingPeer || !1,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
})
}
}, localStreams.forEach(function (localStream) {
if (!(config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) && !config.dontAttachLocalStream && (localStream = connection.beforeAddingStream(localStream, self))) if (peer.getLocalStreams().forEach(function (stream) {
localStream && stream.id == localStream.id && (localStream = null)
}), localStream && "function" == typeof peer.addTrack) localStream.getTracks().forEach(function (track) {
try {
peer.addTrack(track, localStream)
} catch (e) {
}
}); else if (localStream && "function" == typeof peer.addStream) peer.addStream(localStream); else try {
peer.addStream(localStream)
} catch (e) {
localStream && localStream.getTracks().forEach(function (track) {
try {
peer.addTrack(track, localStream)
} catch (e) {
}
})
}
}), peer.oniceconnectionstatechange = peer.onsignalingstatechange = function () {
var extra = self.extra;
connection.peers[self.userid] && (extra = connection.peers[self.userid].extra || extra), peer && (config.onPeerStateChanged({
iceConnectionState: peer.iceConnectionState,
iceGatheringState: peer.iceGatheringState,
signalingState: peer.signalingState,
extra: extra,
userid: self.userid
}), peer && peer.iceConnectionState && peer.iceConnectionState.search(/closed|failed/gi) !== -1 && self.streams instanceof Array && self.streams.forEach(function (stream) {
var streamEvent = connection.streamEvents[stream.id] || {
streamid: stream.id,
stream: stream,
type: "remote"
};
connection.onstreamended(streamEvent)
}))
};
var sdpConstraints = {OfferToReceiveAudio: !!localStreams.length, OfferToReceiveVideo: !!localStreams.length};
config.localPeerSdpConstraints && (sdpConstraints = config.localPeerSdpConstraints), defaults.sdpConstraints = setSdpConstraints(sdpConstraints);
var dontDuplicate = {}, incomingStreamEvent = "track";
window.enableAdapter || (incomingStreamEvent = "addstream"), peer.addEventListener(incomingStreamEvent, function (event) {
if (event && ("track" === incomingStreamEvent && (event.stream = event.streams[event.streams.length - 1]), !dontDuplicate[event.stream.id] || "Safari" === DetectRTC.browser.name)) {
dontDuplicate[event.stream.id] = event.stream.id;
var streamsToShare = {};
config.remoteSdp && config.remoteSdp.streamsToShare ? streamsToShare = config.remoteSdp.streamsToShare : config.streamsToShare && (streamsToShare = config.streamsToShare);
var streamToShare = streamsToShare[event.stream.id];
streamToShare ? (event.stream.isAudio = streamToShare.isAudio, event.stream.isVideo = streamToShare.isVideo, event.stream.isScreen = streamToShare.isScreen) : (event.stream.isVideo = !!event.stream.getVideoTracks().length, event.stream.isAudio = !event.stream.isVideo, event.stream.isScreen = !1), event.stream.streamid = event.stream.id, "Firefox" != DetectRTC.browser.name && event.stream.stop || (event.stream.stop = function () {
var streamEndedEvent = "ended";
"oninactive" in event.stream && (streamEndedEvent = "inactive"), fireEvent(event.stream, streamEndedEvent)
}), allRemoteStreams[event.stream.id] = event.stream, config.onRemoteStream(event.stream)
}
}, !1), peer.onremovestream = function (event) {
event.stream.streamid = event.stream.id, allRemoteStreams[event.stream.id] && delete allRemoteStreams[event.stream.id], config.onRemoteStreamRemoved(event.stream)
}, this.addRemoteCandidate = function (remoteCandidate) {
peer.addIceCandidate(new RTCIceCandidate(remoteCandidate))
}, this.addRemoteSdp = function (remoteSdp, cb) {
return cb = cb || function () {
}, window.enableAdapter ? ("Safari" !== DetectRTC.browser.name && (remoteSdp.sdp = connection.processSdp(remoteSdp.sdp)), void peer.setRemoteDescription(new RTCSessionDescription(remoteSdp)).then(cb, function (error) {
connection.enableLogs && console.error("setRemoteDescription failed", "\n", error, "\n", remoteSdp.sdp), cb()
})) : oldAddRemoteSdp(remoteSdp, cb)
};
var isOfferer = !0;
config.remoteSdp && (isOfferer = !1), this.createDataChannel = function () {
var channel = peer.createDataChannel("sctp", {});
setChannelEvents(channel)
}, connection.session.data !== !0 || renegotiatingPeer || (isOfferer ? this.createDataChannel() : peer.ondatachannel = function (event) {
var channel = event.channel;
setChannelEvents(channel)
}), config.remoteSdp && (config.remoteSdp.remotePeerSdpConstraints && (sdpConstraints = config.remoteSdp.remotePeerSdpConstraints), defaults.sdpConstraints = setSdpConstraints(sdpConstraints), this.addRemoteSdp(config.remoteSdp, function () {
createOfferOrAnswer("createAnswer")
})), "two-way" != connection.session.audio && "two-way" != connection.session.video && "two-way" != connection.session.screen || (defaults.sdpConstraints = setSdpConstraints({
OfferToReceiveAudio: "two-way" == connection.session.audio || config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio,
OfferToReceiveVideo: "two-way" == connection.session.video || "two-way" == connection.session.screen || config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio
}));
var streamsToShare = {};
peer.getLocalStreams().forEach(function (stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
}
}), isOfferer && createOfferOrAnswer("createOffer"), peer.nativeClose = peer.close, peer.close = function () {
if (peer) {
try {
peer.nativeClose !== peer.close && peer.nativeClose()
} catch (e) {
}
peer = null, self.peer = null
}
}, this.peer = peer
}
function setStreamType(constraints, stream) {
constraints.mandatory && constraints.mandatory.chromeMediaSource ? stream.isScreen = !0 : constraints.mozMediaSource || constraints.mediaSource ? stream.isScreen = !0 : constraints.video ? stream.isVideo = !0 : constraints.audio && (stream.isAudio = !0)
}
function getUserMediaHandler(options) {
function streaming(stream, returnBack) {
setStreamType(options.localMediaConstraints, stream);
var streamEndedEvent = "ended";
"oninactive" in stream && (streamEndedEvent = "inactive"), stream.addEventListener(streamEndedEvent, function () {
delete currentUserMediaRequest.streams[idInstance], currentUserMediaRequest.mutex = !1, currentUserMediaRequest.queueRequests.indexOf(options) && (delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)], currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests))
}, !1), currentUserMediaRequest.streams[idInstance] = {stream: stream}, currentUserMediaRequest.mutex = !1, currentUserMediaRequest.queueRequests.length && getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()), options.onGettingLocalMedia(stream, returnBack)
}
if (currentUserMediaRequest.mutex === !0) return void currentUserMediaRequest.queueRequests.push(options);
currentUserMediaRequest.mutex = !0;
var idInstance = JSON.stringify(options.localMediaConstraints);
if (currentUserMediaRequest.streams[idInstance]) streaming(currentUserMediaRequest.streams[idInstance].stream, !0); else {
var isBlackBerry = !!/BB10|BlackBerry/i.test(navigator.userAgent || "");
if (isBlackBerry || "undefined" == typeof navigator.mediaDevices || "function" != typeof navigator.mediaDevices.getUserMedia) return navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia, void navigator.getUserMedia(options.localMediaConstraints, function (stream) {
stream.streamid = stream.streamid || stream.id || getRandomString(), stream.idInstance = idInstance, streaming(stream)
}, function (error) {
options.onLocalMediaError(error, options.localMediaConstraints)
});
if ("undefined" == typeof navigator.mediaDevices) {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var getUserMediaStream, getUserMediaError, getUserMediaSuccess = function () {
}, getUserMediaFailure = function () {
};
navigator.mediaDevices = {
getUserMedia: function (hints) {
return navigator.getUserMedia(hints, function (getUserMediaSuccess) {
getUserMediaSuccess(stream), getUserMediaStream = stream
}, function (error) {
getUserMediaFailure(error), getUserMediaError = error
}), {
then: function (successCB) {
return getUserMediaStream ? void successCB(getUserMediaStream) : (getUserMediaSuccess = successCB, {
then: function (failureCB) {
return getUserMediaError ? void failureCB(getUserMediaError) : void(getUserMediaFailure = failureCB)
}
})
}
}
}
}
}
navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function (stream) {
stream.streamid = stream.streamid || stream.id || getRandomString(), stream.idInstance = idInstance, streaming(stream)
})["catch"](function (error) {
options.onLocalMediaError(error, options.localMediaConstraints)
})
}
}
function onMessageCallback(data) {
if ("PermissionDeniedError" == data) {
if (chromeMediaSource = "PermissionDeniedError", screenCallback) return screenCallback("PermissionDeniedError");
throw new Error("PermissionDeniedError: User rejected to share his screen.")
}
"rtcmulticonnection-extension-loaded" == data && (chromeMediaSource = "desktop"), data.sourceId && screenCallback && (sourceId = data.sourceId, screenCallback(sourceId))
}
function isChromeExtensionAvailable(callback) {
if (callback) {
if ("Firefox" === DetectRTC.browser.name) return isFirefoxExtensionAvailable(callback);
if (window.IsAndroidChrome) return chromeMediaSource = "screen", void callback(!0);
if ("desktop" == chromeMediaSource) return void callback(!0);
window.postMessage("are-you-there", "*"), setTimeout(function () {
callback("screen" == chromeMediaSource ? !1 : !0)
}, 2e3)
}
}
function isFirefoxExtensionAvailable(callback) {
function messageCallback(event) {
var addonMessage = event.data;
addonMessage && "undefined" != typeof addonMessage.isScreenCapturingEnabled && (isFirefoxAddonResponded = !0, callback(addonMessage.isScreenCapturingEnabled === !0 ? !0 : !1), window.removeEventListener("message", messageCallback, !1))
}
if (callback) {
if ("Firefox" !== DetectRTC.browser.name) return isChromeExtensionAvailable(callback);
var isFirefoxAddonResponded = !1;
window.addEventListener("message", messageCallback, !1), window.postMessage({
checkIfScreenCapturingEnabled: !0,
domains: [document.domain]
}, "*"), setTimeout(function () {
isFirefoxAddonResponded || callback(!0)
}, 2e3)
}
}
function getSourceId(callback, audioPlusTab) {
if (!callback) throw'"callback" parameter is mandatory.';
return sourceId = null, screenCallback = callback, audioPlusTab ? void window.postMessage("audio-plus-tab", "*") : void window.postMessage("get-sourceId", "*")
}
function getChromeExtensionStatus(extensionid, callback) {
if (window.IsAndroidChrome) return chromeMediaSource = "screen", void callback("installed-enabled");
if (2 != arguments.length && (callback = extensionid, extensionid = window.RMCExtensionID || "ajhifddimkapgcifgcodmmfdlknahffk"), "Firefox" === DetectRTC.browser.name) return callback("not-chrome");
sourceId = null, chromeMediaSource = "screen";
var image = document.createElement("img");
image.src = "chrome-extension://" + extensionid + "/icon.png", image.onload = function () {
window.postMessage("are-you-there", "*"), setTimeout(function () {
callback("screen" == chromeMediaSource ? "installed-disabled" : "installed-enabled")
}, 2e3)
}, image.onerror = function () {
callback("not-installed")
}
}
function getAspectRatio(w, h) {
function gcd(a, b) {
return 0 == b ? a : gcd(b, a % b)
}
var r = gcd(w, h);
return w / r / (h / r)
}
function getScreenConstraints(callback, audioPlusTab) {
var firefoxScreenConstraints = {mozMediaSource: "window", mediaSource: "window"};
return "Firefox" === DetectRTC.browser.name ? callback(null, firefoxScreenConstraints) : (sourceId = null, void isChromeExtensionAvailable(function (isAvailable) {
var screen_constraints = {
mandatory: {
chromeMediaSource: chromeMediaSource,
maxWidth: screen.width,
maxHeight: screen.height,
minWidth: screen.width,
minHeight: screen.height,
minAspectRatio: getAspectRatio(screen.width, screen.height),
maxAspectRatio: getAspectRatio(screen.width, screen.height),
minFrameRate: 64,
maxFrameRate: 128
}, optional: []
};
return window.IsAndroidChrome ? void callback(null, screen_constraints) : "desktop" != chromeMediaSource || sourceId ? ("desktop" == chromeMediaSource && (screen_constraints.mandatory.chromeMediaSourceId = sourceId), sourceId = null, screenCallback = null, void callback(null, screen_constraints)) : void getSourceId(function () {
screen_constraints.mandatory.chromeMediaSourceId = sourceId, callback("PermissionDeniedError" == sourceId ? sourceId : null, screen_constraints), sourceId = null
}, audioPlusTab)
}))
}
function TextReceiver(connection) {
function receive(data, userid, extra) {
var uuid = data.uuid;
if (content[uuid] || (content[uuid] = []), content[uuid].push(data.message), data.last) {
var message = content[uuid].join("");
data.isobject && (message = JSON.parse(message));
var receivingTime = (new Date).getTime(), latency = receivingTime - data.sendingTime,
e = {data: message, userid: userid, extra: extra, latency: latency};
connection.autoTranslateText ? (e.original = e.data, connection.Translator.TranslateText(e.data, function (translatedText) {
e.data = translatedText, connection.onmessage(e)
})) : connection.onmessage(e), delete content[uuid]
}
}
var content = {};
return {receive: receive}
}
!function () {
function getBrowserInfo() {
var nameOffset, verOffset, ix, nAgt = (navigator.appVersion, navigator.userAgent),
browserName = navigator.appName, fullVersion = "" + parseFloat(navigator.appVersion),
majorVersion = parseInt(navigator.appVersion, 10);
if (isOpera) {
browserName = "Opera";
try {
fullVersion = navigator.userAgent.split("OPR/")[1].split(" ")[0], majorVersion = fullVersion.split(".")[0]
} catch (e) {
fullVersion = "0.0.0.0", majorVersion = 0
}
} else isIE ? (verOffset = nAgt.indexOf("rv:"), verOffset > 0 ? fullVersion = nAgt.substring(verOffset + 3) : (verOffset = nAgt.indexOf("MSIE"), fullVersion = nAgt.substring(verOffset + 5)), browserName = "IE") : isChrome ? (verOffset = nAgt.indexOf("Chrome"), browserName = "Chrome", fullVersion = nAgt.substring(verOffset + 7)) : isSafari ? (verOffset = nAgt.indexOf("Safari"), browserName = "Safari", fullVersion = nAgt.substring(verOffset + 7), (verOffset = nAgt.indexOf("Version")) !== -1 && (fullVersion = nAgt.substring(verOffset + 8)), navigator.userAgent.indexOf("Version/") !== -1 && (fullVersion = navigator.userAgent.split("Version/")[1].split(" ")[0])) : isFirefox ? (verOffset = nAgt.indexOf("Firefox"), browserName = "Firefox", fullVersion = nAgt.substring(verOffset + 8)) : (nameOffset = nAgt.lastIndexOf(" ") + 1) < (verOffset = nAgt.lastIndexOf("/")) && (browserName = nAgt.substring(nameOffset, verOffset), fullVersion = nAgt.substring(verOffset + 1), browserName.toLowerCase() === browserName.toUpperCase() && (browserName = navigator.appName));
return isEdge && (browserName = "Edge", fullVersion = navigator.userAgent.split("Edge/")[1]), (ix = fullVersion.search(/[; \)]/)) !== -1 && (fullVersion = fullVersion.substring(0, ix)), majorVersion = parseInt("" + fullVersion, 10), isNaN(majorVersion) && (fullVersion = "" + parseFloat(navigator.appVersion), majorVersion = parseInt(navigator.appVersion, 10)), {
fullVersion: fullVersion,
version: majorVersion,
name: browserName,
isPrivateBrowsing: !1
}
}
function retry(isDone, next) {
var currentTrial = 0, maxRetry = 50, isTimeout = !1, id = window.setInterval(function () {
isDone() && (window.clearInterval(id), next(isTimeout)), currentTrial++ > maxRetry && (window.clearInterval(id), isTimeout = !0, next(isTimeout))
}, 10)
}
function isIE10OrLater(userAgent) {
var ua = userAgent.toLowerCase();
if (0 === ua.indexOf("msie") && 0 === ua.indexOf("trident")) return !1;
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
return !!(match && parseInt(match[1], 10) >= 10)
}
function detectPrivateMode(callback) {
var isPrivate;
try {
if (window.webkitRequestFileSystem) window.webkitRequestFileSystem(window.TEMPORARY, 1, function () {
isPrivate = !1
}, function (e) {
isPrivate = !0
}); else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open("test"), db.onerror = function () {
return !0
}
} catch (e) {
isPrivate = !0
}
"undefined" == typeof isPrivate && retry(function () {
return "done" === db.readyState
}, function (isTimeout) {
isTimeout || (isPrivate = !db.result)
})
} else if (isIE10OrLater(window.navigator.userAgent)) {
isPrivate = !1;
try {
window.indexedDB || (isPrivate = !0)
} catch (e) {
isPrivate = !0
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
try {
window.localStorage.setItem("test", 1)
} catch (e) {
isPrivate = !0
}
"undefined" == typeof isPrivate && (isPrivate = !1, window.localStorage.removeItem("test"))
}
} catch (e) {
isPrivate = !1
}
retry(function () {
return "undefined" != typeof isPrivate
}, function (isTimeout) {
callback(isPrivate)
})
}
function detectDesktopOS() {
for (var cs, unknown = "-", nVer = navigator.appVersion, nAgt = navigator.userAgent, os = unknown, clientStrings = [{
s: "Windows 10",
r: /(Windows 10.0|Windows NT 10.0)/
}, {s: "Windows 8.1", r: /(Windows 8.1|Windows NT 6.3)/}, {
s: "Windows 8",
r: /(Windows 8|Windows NT 6.2)/
}, {s: "Windows 7", r: /(Windows 7|Windows NT 6.1)/}, {
s: "Windows Vista",
r: /Windows NT 6.0/
}, {s: "Windows Server 2003", r: /Windows NT 5.2/}, {
s: "Windows XP",
r: /(Windows NT 5.1|Windows XP)/
}, {s: "Windows 2000", r: /(Windows NT 5.0|Windows 2000)/}, {
s: "Windows ME",
r: /(Win 9x 4.90|Windows ME)/
}, {s: "Windows 98", r: /(Windows 98|Win98)/}, {
s: "Windows 95",
r: /(Windows 95|Win95|Windows_95)/
}, {s: "Windows NT 4.0", r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/}, {
s: "Windows CE",
r: /Windows CE/
}, {s: "Windows 3.11", r: /Win16/}, {s: "Android", r: /Android/}, {
s: "Open BSD",
r: /OpenBSD/
}, {s: "Sun OS", r: /SunOS/}, {s: "Linux", r: /(Linux|X11)/}, {
s: "iOS",
r: /(iPhone|iPad|iPod)/
}, {s: "Mac OS X", r: /Mac OS X/}, {s: "Mac OS", r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/}, {
s: "QNX",
r: /QNX/
}, {s: "UNIX", r: /UNIX/}, {s: "BeOS", r: /BeOS/}, {s: "OS/2", r: /OS\/2/}, {
s: "Search Bot",
r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/
}], i = 0; cs = clientStrings[i]; i++) if (cs.r.test(nAgt)) {
os = cs.s;
break
}
var osVersion = unknown;
switch (/Windows/.test(os) && (/Windows (.*)/.test(os) && (osVersion = /Windows (.*)/.exec(os)[1]), os = "Windows"), os) {
case"Mac OS X":
/Mac OS X (10[\.\_\d]+)/.test(nAgt) && (osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]);
break;
case"Android":
/Android ([\.\_\d]+)/.test(nAgt) && (osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]);
break;
case"iOS":
/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt) && (osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer), osVersion = osVersion[1] + "." + osVersion[2] + "." + (0 | osVersion[3]))
}
return {osName: os, osVersion: osVersion}
}
function getAndroidVersion(ua) {
ua = (ua || navigator.userAgent).toLowerCase();
var match = ua.match(/android\s([0-9\.]*)/);
return !!match && match[1]
}
function DetectLocalIPAddress(callback, stream) {
DetectRTC.isWebRTCSupported && getIPs(function (ip) {
callback(ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/) ? "Local: " + ip : "Public: " + ip)
}, stream)
}
function getIPs(callback, stream) {
function handleCandidate(candidate) {
var ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/, match = ipRegex.exec(candidate);
if (match) {
var ipAddress = match[1];
void 0 === ipDuplicates[ipAddress] && callback(ipAddress), ipDuplicates[ipAddress] = !0
}
}
function afterCreateOffer() {
var lines = pc.localDescription.sdp.split("\n");
lines.forEach(function (line) {
0 === line.indexOf("a=candidate:") && handleCandidate(line)
})
}
if ("undefined" != typeof document && "function" == typeof document.getElementById) {
var ipDuplicates = {},
RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if (!RTCPeerConnection) {
var iframe = document.getElementById("iframe");
if (!iframe) return;
var win = iframe.contentWindow;
RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection
}
if (RTCPeerConnection) {
var peerConfig = null;
"Chrome" === DetectRTC.browser && DetectRTC.browser.version < 58 && (peerConfig = {optional: [{RtpDataChannels: !0}]});
var servers = {iceServers: [{urls: "stun:stun.l.google.com:19302"}]},
pc = new RTCPeerConnection(servers, peerConfig);
if (stream && (pc.addStream ? pc.addStream(stream) : pc.addTrack && stream.getTracks()[0] && pc.addTrack(stream.getTracks()[0], stream)), pc.onicecandidate = function (ice) {
ice.candidate && handleCandidate(ice.candidate.candidate)
}, !stream) try {
pc.createDataChannel("sctp", {})
} catch (e) {
}
DetectRTC.isPromisesSupported ? pc.createOffer().then(function (result) {
pc.setLocalDescription(result).then(afterCreateOffer)
}) : pc.createOffer(function (result) {
pc.setLocalDescription(result, afterCreateOffer, function () {
})
}, function () {
})
}
}
}
function checkDeviceSupport(callback) {
if (!canEnumerate) return void(callback && callback());
if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources && (navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)), !navigator.enumerateDevices && navigator.enumerateDevices && (navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator)), !navigator.enumerateDevices) return void(callback && callback());
MediaDevices = [], audioInputDevices = [], audioOutputDevices = [], videoInputDevices = [], hasMicrophone = !1, hasSpeakers = !1, hasWebcam = !1, isWebsiteHasMicrophonePermissions = !1, isWebsiteHasWebcamPermissions = !1;
var alreadyUsedDevices = {};
navigator.enumerateDevices(function (devices) {
devices.forEach(function (_device) {
var device = {};
for (var d in _device) try {
"function" != typeof _device[d] && (device[d] = _device[d])
} catch (e) {
}
alreadyUsedDevices[device.deviceId + device.label + device.kind] || ("audio" === device.kind && (device.kind = "audioinput"), "video" === device.kind && (device.kind = "videoinput"), device.deviceId || (device.deviceId = device.id), device.id || (device.id = device.deviceId), device.label ? ("videoinput" !== device.kind || isWebsiteHasWebcamPermissions || (isWebsiteHasWebcamPermissions = !0), "audioinput" !== device.kind || isWebsiteHasMicrophonePermissions || (isWebsiteHasMicrophonePermissions = !0)) : (device.isCustomLabel = !0, "videoinput" === device.kind ? device.label = "Camera " + (videoInputDevices.length + 1) : "audioinput" === device.kind ? device.label = "Microphone " + (audioInputDevices.length + 1) : "audiooutput" === device.kind ? device.label = "Speaker " + (audioOutputDevices.length + 1) : device.label = "Please invoke getUserMedia once.", "undefined" != typeof DetectRTC && DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || "") && "undefined" != typeof document && "string" == typeof document.domain && document.domain.search && document.domain.search(/localhost|127.0./g) === -1 && (device.label = "HTTPs is required to get label of this " + device.kind + " device.")), "audioinput" === device.kind && (hasMicrophone = !0, audioInputDevices.indexOf(device) === -1 && audioInputDevices.push(device)), "audiooutput" === device.kind && (hasSpeakers = !0, audioOutputDevices.indexOf(device) === -1 && audioOutputDevices.push(device)), "videoinput" === device.kind && (hasWebcam = !0, videoInputDevices.indexOf(device) === -1 && videoInputDevices.push(device)), MediaDevices.push(device), alreadyUsedDevices[device.deviceId + device.label + device.kind] = device)
}), "undefined" != typeof DetectRTC && (DetectRTC.MediaDevices = MediaDevices, DetectRTC.hasMicrophone = hasMicrophone, DetectRTC.hasSpeakers = hasSpeakers, DetectRTC.hasWebcam = hasWebcam, DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions, DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions, DetectRTC.audioInputDevices = audioInputDevices, DetectRTC.audioOutputDevices = audioOutputDevices, DetectRTC.videoInputDevices = videoInputDevices), callback && callback()
})
}
function getAspectRatio(w, h) {
function gcd(a, b) {
return 0 == b ? a : gcd(b, a % b)
}
var r = gcd(w, h);
return w / r / (h / r)
}
var browserFakeUserAgent = "Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45",
isNodejs = "object" == typeof process && "object" == typeof process.versions && process.versions.node && !process.browser;
if (isNodejs) {
var version = process.versions.node.toString().replace("v", "");
browserFakeUserAgent = "Nodejs/" + version + " (NodeOS) AppleWebKit/" + version + " (KHTML, like Gecko) Nodejs/" + version + " Nodejs/" + version
}
!function (that) {
"undefined" == typeof window && ("undefined" == typeof window && "undefined" != typeof global ? (global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function () {
}
}, that.window = global) : "undefined" == typeof window, "undefined" == typeof location && (that.location = {
protocol: "file:",
href: "",
hash: ""
}), "undefined" == typeof screen && (that.screen = {width: 0, height: 0}))
}("undefined" != typeof global ? global : window);
var navigator = window.navigator;
"undefined" != typeof navigator ? ("undefined" != typeof navigator.webkitGetUserMedia && (navigator.getUserMedia = navigator.webkitGetUserMedia), "undefined" != typeof navigator.mozGetUserMedia && (navigator.getUserMedia = navigator.mozGetUserMedia)) : navigator = {
getUserMedia: function () {
}, userAgent: browserFakeUserAgent
};
var isMobileDevice = !!/Android|webOS|iPhone|iPad|iPod|BB10|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(navigator.userAgent || ""),
isEdge = !(navigator.userAgent.indexOf("Edge") === -1 || !navigator.msSaveOrOpenBlob && !navigator.msSaveBlob),
isOpera = !!window.opera || navigator.userAgent.indexOf(" OPR/") >= 0,
isFirefox = "undefined" != typeof window.InstallTrigger,
isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent),
isChrome = !!window.chrome && !isOpera,
isIE = "undefined" != typeof document && !!document.documentMode && !isEdge, isMobile = {
Android: function () {
return navigator.userAgent.match(/Android/i)
}, BlackBerry: function () {
return navigator.userAgent.match(/BlackBerry|BB10/i)
}, iOS: function () {
return navigator.userAgent.match(/iPhone|iPad|iPod/i)
}, Opera: function () {
return navigator.userAgent.match(/Opera Mini/i)
}, Windows: function () {
return navigator.userAgent.match(/IEMobile/i)
}, any: function () {
return isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()
}, getOsName: function () {
var osName = "Unknown OS";
return isMobile.Android() && (osName = "Android"), isMobile.BlackBerry() && (osName = "BlackBerry"), isMobile.iOS() && (osName = "iOS"), isMobile.Opera() && (osName = "Opera Mini"), isMobile.Windows() && (osName = "Windows"), osName
}
}, osName = "Unknown OS", osVersion = "Unknown OS Version", osInfo = detectDesktopOS();
osInfo && osInfo.osName && "-" != osInfo.osName ? (osName = osInfo.osName, osVersion = osInfo.osVersion) : isMobile.any() && (osName = isMobile.getOsName(), "Android" == osName && (osVersion = getAndroidVersion()));
var isNodejs = "object" == typeof process && "object" == typeof process.versions && process.versions.node;
"Unknown OS" === osName && isNodejs && (osName = "Nodejs", osVersion = process.versions.node.toString().replace("v", ""));
var isCanvasSupportsStreamCapturing = !1, isVideoSupportsStreamCapturing = !1;
["captureStream", "mozCaptureStream", "webkitCaptureStream"].forEach(function (item) {
"undefined" != typeof document && "function" == typeof document.createElement && (!isCanvasSupportsStreamCapturing && item in document.createElement("canvas") && (isCanvasSupportsStreamCapturing = !0), !isVideoSupportsStreamCapturing && item in document.createElement("video") && (isVideoSupportsStreamCapturing = !0))
});
var MediaDevices = [], audioInputDevices = [], audioOutputDevices = [], videoInputDevices = [];
navigator.mediaDevices && navigator.mediaDevices.enumerateDevices && (navigator.enumerateDevices = function (callback) {
var enumerateDevices = navigator.mediaDevices.enumerateDevices();
enumerateDevices && enumerateDevices.then ? navigator.mediaDevices.enumerateDevices().then(callback)["catch"](function () {
callback([])
}) : callback([])
});
var canEnumerate = !1;
"undefined" != typeof MediaStreamTrack && "getSources" in MediaStreamTrack ? canEnumerate = !0 : navigator.mediaDevices && navigator.mediaDevices.enumerateDevices && (canEnumerate = !0);
var hasMicrophone = !1, hasSpeakers = !1, hasWebcam = !1, isWebsiteHasMicrophonePermissions = !1,
isWebsiteHasWebcamPermissions = !1, DetectRTC = window.DetectRTC || {};
DetectRTC.browser = getBrowserInfo(), detectPrivateMode(function (isPrivateBrowsing) {
DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing
}), DetectRTC.browser["is" + DetectRTC.browser.name] = !0, DetectRTC.osName = osName, DetectRTC.osVersion = osVersion;
var isWebRTCSupported = ("object" == typeof process && "object" == typeof process.versions && process.versions["node-webkit"], !1);
["RTCPeerConnection", "webkitRTCPeerConnection", "mozRTCPeerConnection", "RTCIceGatherer"].forEach(function (item) {
isWebRTCSupported || item in window && (isWebRTCSupported = !0)
}), DetectRTC.isWebRTCSupported = isWebRTCSupported, DetectRTC.isORTCSupported = "undefined" != typeof RTCIceGatherer;
var isScreenCapturingSupported = !1;
if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35 ? isScreenCapturingSupported = !0 : DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34 ? isScreenCapturingSupported = !0 : DetectRTC.browser.isEdge && DetectRTC.browser.version >= 17 ? isScreenCapturingSupported = !0 : "Android" === DetectRTC.osName && DetectRTC.browser.isChrome && (isScreenCapturingSupported = !0), !/^(https:|chrome-extension:)$/g.test(location.protocol || "")) {
var isNonLocalHost = "undefined" != typeof document && "string" == typeof document.domain && document.domain.search && document.domain.search(/localhost|127.0./g) === -1;
isNonLocalHost && (DetectRTC.browser.isChrome || DetectRTC.browser.isEdge || DetectRTC.browser.isOpera) ? isScreenCapturingSupported = !1 : DetectRTC.browser.isFirefox && (isScreenCapturingSupported = !1)
}
DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported;
var webAudio = {isSupported: !1, isCreateMediaStreamSourceSupported: !1};
["AudioContext", "webkitAudioContext", "mozAudioContext", "msAudioContext"].forEach(function (item) {
webAudio.isSupported || item in window && (webAudio.isSupported = !0, window[item] && "createMediaStreamSource" in window[item].prototype && (webAudio.isCreateMediaStreamSourceSupported = !0))
}), DetectRTC.isAudioContextSupported = webAudio.isSupported, DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported;
var isRtpDataChannelsSupported = !1;
DetectRTC.browser.isChrome && DetectRTC.browser.version > 31 && (isRtpDataChannelsSupported = !0), DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported;
var isSCTPSupportd = !1;
DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28 ? isSCTPSupportd = !0 : DetectRTC.browser.isChrome && DetectRTC.browser.version > 25 ? isSCTPSupportd = !0 : DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11 && (isSCTPSupportd = !0), DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd, DetectRTC.isMobileDevice = isMobileDevice;
var isGetUserMediaSupported = !1;
navigator.getUserMedia ? isGetUserMediaSupported = !0 : navigator.mediaDevices && navigator.mediaDevices.getUserMedia && (isGetUserMediaSupported = !0), DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || "") && "undefined" != typeof document && "string" == typeof document.domain && document.domain.search && document.domain.search(/localhost|127.0./g) === -1 && (isGetUserMediaSupported = "Requires HTTPs"), "Nodejs" === DetectRTC.osName && (isGetUserMediaSupported = !1), DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported;
var displayResolution = "";
if (screen.width) {
var width = screen.width ? screen.width : "", height = screen.height ? screen.height : "";
displayResolution += "" + width + " x " + height
}
DetectRTC.displayResolution = displayResolution, DetectRTC.displayAspectRatio = getAspectRatio(screen.width, screen.height).toFixed(2),
DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing, DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing, "Chrome" == DetectRTC.browser.name && DetectRTC.browser.version >= 53 && (DetectRTC.isCanvasSupportsStreamCapturing || (DetectRTC.isCanvasSupportsStreamCapturing = "Requires chrome flag: enable-experimental-web-platform-features"), DetectRTC.isVideoSupportsStreamCapturing || (DetectRTC.isVideoSupportsStreamCapturing = "Requires chrome flag: enable-experimental-web-platform-features")), DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress, DetectRTC.isWebSocketsSupported = "WebSocket" in window && 2 === window.WebSocket.CLOSING, DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported, "Nodejs" === DetectRTC.osName && (DetectRTC.isWebSocketsSupported = !0, DetectRTC.isWebSocketsBlocked = !1), DetectRTC.checkWebSocketsSupport = function (callback) {
callback = callback || function () {
};
try {
var starttime, websocket = new WebSocket("wss://echo.websocket.org:443/");
websocket.onopen = function () {
DetectRTC.isWebSocketsBlocked = !1, starttime = (new Date).getTime(), websocket.send("ping")
}, websocket.onmessage = function () {
DetectRTC.WebsocketLatency = (new Date).getTime() - starttime + "ms", callback(), websocket.close(), websocket = null
}, websocket.onerror = function () {
DetectRTC.isWebSocketsBlocked = !0, callback()
}
} catch (e) {
DetectRTC.isWebSocketsBlocked = !0, callback()
}
}, DetectRTC.load = function (callback) {
callback = callback || function () {
}, checkDeviceSupport(callback)
}, "undefined" != typeof MediaDevices ? DetectRTC.MediaDevices = MediaDevices : DetectRTC.MediaDevices = [], DetectRTC.hasMicrophone = hasMicrophone, DetectRTC.hasSpeakers = hasSpeakers, DetectRTC.hasWebcam = hasWebcam, DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions, DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions, DetectRTC.audioInputDevices = audioInputDevices, DetectRTC.audioOutputDevices = audioOutputDevices, DetectRTC.videoInputDevices = videoInputDevices;
var isSetSinkIdSupported = !1;
"undefined" != typeof document && "function" == typeof document.createElement && "setSinkId" in document.createElement("video") && (isSetSinkIdSupported = !0), DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported;
var isRTPSenderReplaceTracksSupported = !1;
DetectRTC.browser.isFirefox && "undefined" != typeof mozRTCPeerConnection ? "getSenders" in mozRTCPeerConnection.prototype && (isRTPSenderReplaceTracksSupported = !0) : DetectRTC.browser.isChrome && "undefined" != typeof webkitRTCPeerConnection && "getSenders" in webkitRTCPeerConnection.prototype && (isRTPSenderReplaceTracksSupported = !0), DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported;
var isRemoteStreamProcessingSupported = !1;
DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38 && (isRemoteStreamProcessingSupported = !0), DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported;
var isApplyConstraintsSupported = !1;
"undefined" != typeof MediaStreamTrack && "applyConstraints" in MediaStreamTrack.prototype && (isApplyConstraintsSupported = !0), DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported;
var isMultiMonitorScreenCapturingSupported = !1;
DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43 && (isMultiMonitorScreenCapturingSupported = !0), DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported, DetectRTC.isPromisesSupported = !!("Promise" in window), DetectRTC.version = "1.3.6", "undefined" == typeof DetectRTC && (window.DetectRTC = {});
var MediaStream = window.MediaStream;
"undefined" == typeof MediaStream && "undefined" != typeof webkitMediaStream && (MediaStream = webkitMediaStream), "undefined" != typeof MediaStream && "function" == typeof MediaStream ? DetectRTC.MediaStream = Object.keys(MediaStream.prototype) : DetectRTC.MediaStream = !1, "undefined" != typeof MediaStreamTrack ? DetectRTC.MediaStreamTrack = Object.keys(MediaStreamTrack.prototype) : DetectRTC.MediaStreamTrack = !1;
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
"undefined" != typeof RTCPeerConnection ? DetectRTC.RTCPeerConnection = Object.keys(RTCPeerConnection.prototype) : DetectRTC.RTCPeerConnection = !1, window.DetectRTC = DetectRTC, "undefined" != typeof module && (module.exports = DetectRTC), "function" == typeof define && define.amd && define("DetectRTC", [], function () {
return DetectRTC
})
}(), "undefined" != typeof cordova && (DetectRTC.isMobileDevice = !0, DetectRTC.browser.name = "Chrome"), navigator && navigator.userAgent && navigator.userAgent.indexOf("Crosswalk") !== -1 && (DetectRTC.isMobileDevice = !0, DetectRTC.browser.name = "Chrome"), window.addEventListener || (window.addEventListener = function (el, eventName, eventHandler) {
el.attachEvent && el.attachEvent("on" + eventName, eventHandler)
}), window.attachEventListener = function (video, type, listener, useCapture) {
video.addEventListener(type, listener, useCapture)
};
var MediaStream = window.MediaStream;
"undefined" == typeof MediaStream && "undefined" != typeof webkitMediaStream && (MediaStream = webkitMediaStream), "undefined" != typeof MediaStream && ("stop" in MediaStream.prototype || (MediaStream.prototype.stop = function () {
this.getTracks().forEach(function (track) {
track.stop()
})
})), window.iOSDefaultAudioOutputDevice = window.iOSDefaultAudioOutputDevice || "speaker", "undefined" == typeof window.enableAdapter && ("Firefox" === DetectRTC.browser.name && DetectRTC.browser.version >= 54 && (window.enableAdapter = !0), "Chrome" === DetectRTC.browser.name && DetectRTC.browser.version >= 60, "undefined" != typeof adapter && adapter.browserDetails && "string" == typeof adapter.browserDetails.browser && (window.enableAdapter = !0)), window.enableAdapter || ("undefined" == typeof URL.createObjectURL && (URL.createObjectURL = function (stream) {
return "blob:https://" + document.domain + "/" + getRandomString()
}), "srcObject" in HTMLMediaElement.prototype || (HTMLMediaElement.prototype.srcObject = function (stream) {
return "mozSrcObject" in this ? void(this.mozSrcObject = stream) : void(this.src = URL.createObjectURL(stream))
})), document.addEventListener("deviceready", setCordovaAPIs, !1), setCordovaAPIs();
var RTCPeerConnection, defaults = {};
"undefined" != typeof window.RTCPeerConnection ? RTCPeerConnection = window.RTCPeerConnection : "undefined" != typeof mozRTCPeerConnection ? RTCPeerConnection = mozRTCPeerConnection : "undefined" != typeof webkitRTCPeerConnection && (RTCPeerConnection = webkitRTCPeerConnection);
var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription,
RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate,
MediaStreamTrack = window.MediaStreamTrack, CodecsHandler = function () {
function preferCodec(sdp, codecName) {
var info = splitLines(sdp);
return info.videoCodecNumbers ? "vp8" === codecName && info.vp8LineNumber === info.videoCodecNumbers[0] ? sdp : "vp9" === codecName && info.vp9LineNumber === info.videoCodecNumbers[0] ? sdp : "h264" === codecName && info.h264LineNumber === info.videoCodecNumbers[0] ? sdp : sdp = preferCodecHelper(sdp, codecName, info) : sdp
}
function preferCodecHelper(sdp, codec, info, ignore) {
var preferCodecNumber = "";
if ("vp8" === codec) {
if (!info.vp8LineNumber) return sdp;
preferCodecNumber = info.vp8LineNumber
}
if ("vp9" === codec) {
if (!info.vp9LineNumber) return sdp;
preferCodecNumber = info.vp9LineNumber
}
if ("h264" === codec) {
if (!info.h264LineNumber) return sdp;
preferCodecNumber = info.h264LineNumber
}
var newLine = info.videoCodecNumbersOriginal.split("SAVPF")[0] + "SAVPF ", newOrder = [preferCodecNumber];
return ignore && (newOrder = []), info.videoCodecNumbers.forEach(function (codecNumber) {
codecNumber !== preferCodecNumber && newOrder.push(codecNumber)
}), newLine += newOrder.join(" "), sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine)
}
function splitLines(sdp) {
var info = {};
return sdp.split("\n").forEach(function (line) {
0 === line.indexOf("m=video") && (info.videoCodecNumbers = [], line.split("SAVPF")[1].split(" ").forEach(function (codecNumber) {
codecNumber = codecNumber.trim(), codecNumber && codecNumber.length && (info.videoCodecNumbers.push(codecNumber), info.videoCodecNumbersOriginal = line)
})), line.indexOf("VP8/90000") === -1 || info.vp8LineNumber || (info.vp8LineNumber = line.replace("a=rtpmap:", "").split(" ")[0]), line.indexOf("VP9/90000") === -1 || info.vp9LineNumber || (info.vp9LineNumber = line.replace("a=rtpmap:", "").split(" ")[0]), line.indexOf("H264/90000") === -1 || info.h264LineNumber || (info.h264LineNumber = line.replace("a=rtpmap:", "").split(" ")[0])
}), info
}
function removeVPX(sdp) {
var info = splitLines(sdp);
return sdp = preferCodecHelper(sdp, "vp9", info, !0), sdp = preferCodecHelper(sdp, "vp8", info, !0)
}
function disableNACK(sdp) {
if (!sdp || "string" != typeof sdp) throw"Invalid arguments.";
return sdp = sdp.replace("a=rtcp-fb:126 nack\r\n", ""), sdp = sdp.replace("a=rtcp-fb:126 nack pli\r\n", "a=rtcp-fb:126 pli\r\n"), sdp = sdp.replace("a=rtcp-fb:97 nack\r\n", ""), sdp = sdp.replace("a=rtcp-fb:97 nack pli\r\n", "a=rtcp-fb:97 pli\r\n")
}
function prioritize(codecMimeType, peer) {
if (peer && peer.getSenders && peer.getSenders().length) {
if (!codecMimeType || "string" != typeof codecMimeType) throw"Invalid arguments.";
peer.getSenders().forEach(function (sender) {
for (var params = sender.getParameters(), i = 0; i < params.codecs.length; i++) if (params.codecs[i].mimeType == codecMimeType) {
params.codecs.unshift(params.codecs.splice(i, 1));
break
}
sender.setParameters(params)
})
}
}
function removeNonG722(sdp) {
return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, "m=audio $1 RTP/SAVPF 9")
}
function setBAS(sdp, bandwidth, isScreen) {
return bandwidth ? "undefined" != typeof isFirefox && isFirefox ? sdp : (isScreen && (bandwidth.screen ? bandwidth.screen < 300 && console.warn("It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.") : console.warn("It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.")), bandwidth.screen && isScreen && (sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ""), sdp = sdp.replace(/a=mid:video\r\n/g, "a=mid:video\r\nb=AS:" + bandwidth.screen + "\r\n")), (bandwidth.audio || bandwidth.video) && (sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, "")), bandwidth.audio && (sdp = sdp.replace(/a=mid:audio\r\n/g, "a=mid:audio\r\nb=AS:" + bandwidth.audio + "\r\n")), bandwidth.screen ? sdp = sdp.replace(/a=mid:video\r\n/g, "a=mid:video\r\nb=AS:" + bandwidth.screen + "\r\n") : bandwidth.video && (sdp = sdp.replace(/a=mid:video\r\n/g, "a=mid:video\r\nb=AS:" + bandwidth.video + "\r\n")), sdp) : sdp
}
function findLine(sdpLines, prefix, substr) {
return findLineInRange(sdpLines, 0, -1, prefix, substr)
}
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
for (var realEndLine = endLine !== -1 ? endLine : sdpLines.length, i = startLine; i < realEndLine; ++i) if (0 === sdpLines[i].indexOf(prefix) && (!substr || sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1)) return i;
return null
}
function getCodecPayloadType(sdpLine) {
var pattern = new RegExp("a=rtpmap:(\\d+) \\w+\\/\\d+"), result = sdpLine.match(pattern);
return result && 2 === result.length ? result[1] : null
}
function setVideoBitrates(sdp, params) {
params = params || {};
var vp8Payload, xgoogle_min_bitrate = params.min, xgoogle_max_bitrate = params.max,
sdpLines = sdp.split("\r\n"), vp8Index = findLine(sdpLines, "a=rtpmap", "VP8/90000");
if (vp8Index && (vp8Payload = getCodecPayloadType(sdpLines[vp8Index])), !vp8Payload) return sdp;
var rtxPayload, rtxIndex = findLine(sdpLines, "a=rtpmap", "rtx/90000");
if (rtxIndex && (rtxPayload = getCodecPayloadType(sdpLines[rtxIndex])), !rtxIndex) return sdp;
var rtxFmtpLineIndex = findLine(sdpLines, "a=fmtp:" + rtxPayload.toString());
if (null !== rtxFmtpLineIndex) {
var appendrtxNext = "\r\n";
appendrtxNext += "a=fmtp:" + vp8Payload + " x-google-min-bitrate=" + (xgoogle_min_bitrate || "228") + "; x-google-max-bitrate=" + (xgoogle_max_bitrate || "228"), sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext), sdp = sdpLines.join("\r\n")
}
return sdp
}
function setOpusAttributes(sdp, params) {
params = params || {};
var opusPayload, sdpLines = sdp.split("\r\n"), opusIndex = findLine(sdpLines, "a=rtpmap", "opus/48000");
if (opusIndex && (opusPayload = getCodecPayloadType(sdpLines[opusIndex])), !opusPayload) return sdp;
var opusFmtpLineIndex = findLine(sdpLines, "a=fmtp:" + opusPayload.toString());
if (null === opusFmtpLineIndex) return sdp;
var appendOpusNext = "";
return appendOpusNext += "; stereo=" + ("undefined" != typeof params.stereo ? params.stereo : "1"), appendOpusNext += "; sprop-stereo=" + ("undefined" != typeof params["sprop-stereo"] ? params["sprop-stereo"] : "1"), "undefined" != typeof params.maxaveragebitrate && (appendOpusNext += "; maxaveragebitrate=" + (params.maxaveragebitrate || 1048576)), "undefined" != typeof params.maxplaybackrate && (appendOpusNext += "; maxplaybackrate=" + (params.maxplaybackrate || 1048576)), "undefined" != typeof params.cbr && (appendOpusNext += "; cbr=" + ("undefined" != typeof params.cbr ? params.cbr : "1")), "undefined" != typeof params.useinbandfec && (appendOpusNext += "; useinbandfec=" + params.useinbandfec), "undefined" != typeof params.usedtx && (appendOpusNext += "; usedtx=" + params.usedtx), "undefined" != typeof params.maxptime && (appendOpusNext += "\r\na=maxptime:" + params.maxptime), sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext), sdp = sdpLines.join("\r\n")
}
function forceStereoAudio(sdp) {
for (var sdpLines = sdp.split("\r\n"), fmtpLineIndex = null, i = 0; i < sdpLines.length; i++) if (sdpLines[i].search("opus/48000") !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
break
}
for (var i = 0; i < sdpLines.length; i++) if (sdpLines[i].search("a=fmtp") !== -1) {
var payload = extractSdp(sdpLines[i], /a=fmtp:(\d+)/);
if (payload === opusPayload) {
fmtpLineIndex = i;
break
}
}
return null === fmtpLineIndex ? sdp : (sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat("; stereo=1; sprop-stereo=1"), sdp = sdpLines.join("\r\n"))
}
return {
removeVPX: removeVPX,
disableNACK: disableNACK,
prioritize: prioritize,
removeNonG722: removeNonG722,
setApplicationSpecificBandwidth: function (sdp, bandwidth, isScreen) {
return setBAS(sdp, bandwidth, isScreen)
},
setVideoBitrates: function (sdp, params) {
return setVideoBitrates(sdp, params)
},
setOpusAttributes: function (sdp, params) {
return setOpusAttributes(sdp, params)
},
preferVP9: function (sdp) {
return preferCodec(sdp, "vp9")
},
preferCodec: preferCodec,
forceStereoAudio: forceStereoAudio
}
}();
window.BandwidthHandler = CodecsHandler;
var OnIceCandidateHandler = function () {
function processCandidates(connection, icePair) {
var candidate = icePair.candidate, iceRestrictions = connection.candidates, stun = iceRestrictions.stun,
turn = iceRestrictions.turn;
if (isNull(iceRestrictions.reflexive) || (stun = iceRestrictions.reflexive), isNull(iceRestrictions.relay) || (turn = iceRestrictions.relay), (iceRestrictions.host || !candidate.match(/typ host/g)) && (turn || !candidate.match(/typ relay/g)) && (stun || !candidate.match(/typ srflx/g))) {
var protocol = connection.iceProtocols;
if ((protocol.udp || !candidate.match(/ udp /g)) && (protocol.tcp || !candidate.match(/ tcp /g))) return connection.enableLogs && console.debug("Your candidate pairs:", candidate), {
candidate: candidate,
sdpMid: icePair.sdpMid,
sdpMLineIndex: icePair.sdpMLineIndex
}
}
}
return {processCandidates: processCandidates}
}(), IceServersHandler = function () {
function getIceServers(connection) {
var iceServers = [{
urls: ["stun:webrtcweb.com:7788", "stun:webrtcweb.com:7788?transport=udp"],
username: "muazkh",
credential: "muazkh"
}, {
urls: ["turn:webrtcweb.com:7788", "turn:webrtcweb.com:4455?transport=udp", "turn:webrtcweb.com:8877?transport=udp", "turn:webrtcweb.com:8877?transport=tcp"],
username: "muazkh",
credential: "muazkh"
}, {urls: ["stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302?transport=udp"]}];
return "undefined" != typeof window.InstallTrigger && (iceServers[0].urls = iceServers[0].urls.pop(), iceServers[1].urls = iceServers[1].urls.pop()), iceServers
}
return {getIceServers: getIceServers}
}();
window.currentUserMediaRequest = {
streams: [], mutex: !1, queueRequests: [], remove: function (idInstance) {
this.mutex = !1;
var stream = this.streams[idInstance];
if (stream) {
stream = stream.stream;
var options = stream.currentUserMediaRequestOptions;
this.queueRequests.indexOf(options) && (delete this.queueRequests[this.queueRequests.indexOf(options)], this.queueRequests = removeNullEntries(this.queueRequests)), this.streams[idInstance].stream = null, delete this.streams[idInstance]
}
}
};
var StreamsHandler = function () {
function handleType(type) {
if (type) return "string" == typeof type || "undefined" == typeof type ? type : type.audio && type.video ? null : type.audio ? "audio" : type.video ? "video" : void 0
}
function setHandlers(stream, syncAction, connection) {
function graduallyIncreaseVolume() {
if (connection.streamEvents[stream.streamid].mediaElement) {
var mediaElement = connection.streamEvents[stream.streamid].mediaElement;
mediaElement.volume = 0, afterEach(200, 5, function () {
try {
mediaElement.volume += .2
} catch (e) {
mediaElement.volume = 1
}
})
}
}
if (stream && stream.addEventListener) {
if ("undefined" == typeof syncAction || 1 == syncAction) {
var streamEndedEvent = "ended";
"oninactive" in stream && (streamEndedEvent = "inactive"), stream.addEventListener(streamEndedEvent, function () {
StreamsHandler.onSyncNeeded(this.streamid, streamEndedEvent)
}, !1)
}
stream.mute = function (type, isSyncAction) {
type = handleType(type), "undefined" != typeof isSyncAction && (syncAction = isSyncAction), "undefined" != typeof type && "audio" != type || stream.getAudioTracks().forEach(function (track) {
track.enabled = !1, connection.streamEvents[stream.streamid].isAudioMuted = !0
}), "undefined" != typeof type && "video" != type || stream.getVideoTracks().forEach(function (track) {
track.enabled = !1
}), "undefined" != typeof syncAction && 1 != syncAction || StreamsHandler.onSyncNeeded(stream.streamid, "mute", type), connection.streamEvents[stream.streamid].muteType = type || "both", fireEvent(stream, "mute", type)
}, stream.unmute = function (type, isSyncAction) {
type = handleType(type), "undefined" != typeof isSyncAction && (syncAction = isSyncAction), graduallyIncreaseVolume(), "undefined" != typeof type && "audio" != type || stream.getAudioTracks().forEach(function (track) {
track.enabled = !0, connection.streamEvents[stream.streamid].isAudioMuted = !1
}), "undefined" != typeof type && "video" != type || (stream.getVideoTracks().forEach(function (track) {
track.enabled = !0
}), "undefined" != typeof type && "video" == type && connection.streamEvents[stream.streamid].isAudioMuted && !function looper(times) {
times || (times = 0), times++, times < 100 && connection.streamEvents[stream.streamid].isAudioMuted && (stream.mute("audio"), setTimeout(function () {
looper(times)
}, 50))
}()), "undefined" != typeof syncAction && 1 != syncAction || StreamsHandler.onSyncNeeded(stream.streamid, "unmute", type), connection.streamEvents[stream.streamid].unmuteType = type || "both", fireEvent(stream, "unmute", type)
}
}
}
function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) {
startedTimes = (startedTimes || 0) + 1, startedTimes >= numberOfTimes || setTimeout(function () {
callback(), afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes)
}, setTimeoutInteval)
}
return {
setHandlers: setHandlers, onSyncNeeded: function (streamid, action, type) {
}
}
}();
window.addEventListener("message", function (event) {
event.origin == window.location.origin && onMessageCallback(event.data)
}), window.IsAndroidChrome = !1;
try {
navigator.userAgent.toLowerCase().indexOf("android") > -1 && /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor) && (window.IsAndroidChrome = !0)
} catch (e) {
}
var sourceId, screenCallback, chromeMediaSource = "screen", TextSender = {
send: function (config) {
function sendText(textMessage, text) {
var data = {type: "text", uuid: uuid, sendingTime: sendingTime};
textMessage && (text = textMessage, data.packets = parseInt(text.length / packetSize)), text.length > packetSize ? data.message = text.slice(0, packetSize) : (data.message = text, data.last = !0, data.isobject = isobject), channel.send(data, remoteUserId), textToTransfer = text.slice(data.message.length), textToTransfer.length && setTimeout(function () {
sendText(null, textToTransfer)
}, connection.chunkInterval || 100)
}
var connection = config.connection, channel = config.channel, remoteUserId = config.remoteUserId,
initialText = config.text, packetSize = connection.chunkSize || 1e3, textToTransfer = "", isobject = !1;
isString(initialText) || (isobject = !0, initialText = JSON.stringify(initialText));
var uuid = getRandomString(), sendingTime = (new Date).getTime();
sendText(initialText)
}
}, FileProgressBarHandler = function () {
function handle(connection) {
function updateLabel(progress, label) {
if (progress.position !== -1) {
var position = +progress.position.toFixed(2).split(".")[1] || 100;
label.innerHTML = position + "%"
}
}
var progressHelper = {};
connection.onFileStart = function (file) {
var div = document.createElement("div");
return div.title = file.name, div.innerHTML = " ", file.remoteUserId && (div.innerHTML += " (Sharing with:" + file.remoteUserId + ")"), connection.filesContainer || (connection.filesContainer = document.body || document.documentElement), connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild), file.remoteUserId ? (progressHelper[file.uuid] || (progressHelper[file.uuid] = {}), progressHelper[file.uuid][file.remoteUserId] = {
div: div,
progress: div.querySelector("progress"),
label: div.querySelector("label")
}, void(progressHelper[file.uuid][file.remoteUserId].progress.max = file.maxChunks)) : (progressHelper[file.uuid] = {
div: div,
progress: div.querySelector("progress"),
label: div.querySelector("label")
}, void(progressHelper[file.uuid].progress.max = file.maxChunks))
}, connection.onFileProgress = function (chunk) {
var helper = progressHelper[chunk.uuid];
helper && (chunk.remoteUserId && !(helper = progressHelper[chunk.uuid][chunk.remoteUserId]) || (helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max, updateLabel(helper.progress, helper.label)))
}, connection.onFileEnd = function (file) {
var helper = progressHelper[file.uuid];
if (!helper) return void console.error("No such progress-helper element exist.", file);
if (!file.remoteUserId || (helper = progressHelper[file.uuid][file.remoteUserId])) {
var div = helper.div;
file.type.indexOf("image") != -1 ? div.innerHTML = 'Download ' + file.name + '
' : div.innerHTML = 'Download ' + file.name + '
'
}
}
}
return {handle: handle}
}(), TranslationHandler = function () {
function handle(connection) {
connection.autoTranslateText = !1, connection.language = "en", connection.googKey = "AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE", connection.Translator = {
TranslateText: function (text, callback) {
var newScript = document.createElement("script");
newScript.type = "text/javascript";
var sourceText = encodeURIComponent(text), randomNumber = "method" + connection.token();
window[randomNumber] = function (response) {
return response.data && response.data.translations[0] && callback ? void callback(response.data.translations[0].translatedText) : response.error && "Daily Limit Exceeded" === response.error.message ? void console.error('Text translation failed. Error message: "Daily Limit Exceeded."') : response.error ? void console.error(response.error.message) : void console.error(response)
};
var source = "https://www.googleapis.com/language/translate/v2?key=" + connection.googKey + "&target=" + (connection.language || "en-US") + "&callback=window." + randomNumber + "&q=" + sourceText;
newScript.src = source, document.getElementsByTagName("head")[0].appendChild(newScript)
}, getListOfLanguages: function (callback) {
var xhr = new XMLHttpRequest;
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
var response = JSON.parse(xhr.responseText);
if (response && response.data && response.data.languages) return void callback(response.data.languages);
if (response.error && "Daily Limit Exceeded" === response.error.message) return void console.error('Text translation failed. Error message: "Daily Limit Exceeded."');
if (response.error) return void console.error(response.error.message);
console.error(response)
}
};
var url = "https://www.googleapis.com/language/translate/v2/languages?key=" + connection.googKey + "&target=en";
xhr.open("GET", url, !0), xhr.send(null)
}
}
}
return {handle: handle}
}();
!function (connection) {
function onUserLeft(remoteUserId) {
connection.deletePeer(remoteUserId)
}
function connectSocket(connectCallback) {
if (connection.socketAutoReConnect = !0, connection.socket) return void(connectCallback && connectCallback(connection.socket));
if ("undefined" == typeof SocketConnection) if ("undefined" != typeof FirebaseConnection) window.SocketConnection = FirebaseConnection; else {
if ("undefined" == typeof PubNubConnection) throw"SocketConnection.js seems missed.";
window.SocketConnection = PubNubConnection
}
new SocketConnection(connection, function (s) {
connectCallback && connectCallback(connection.socket)
})
}
function beforeJoin(userPreferences, callback) {
if (connection.dontCaptureUserMedia || userPreferences.isDataOnly) return void callback();
var localMediaConstraints = {};
userPreferences.localPeerSdpConstraints.OfferToReceiveAudio && (localMediaConstraints.audio = connection.mediaConstraints.audio), userPreferences.localPeerSdpConstraints.OfferToReceiveVideo && (localMediaConstraints.video = connection.mediaConstraints.video);
var session = userPreferences.session || connection.session;
return session.oneway && "two-way" !== session.audio && "two-way" !== session.video && "two-way" !== session.screen ? void callback() : (session.oneway && session.audio && "two-way" === session.audio && (session = {audio: !0}), void((session.audio || session.video || session.screen) && (session.screen ? "Edge" === DetectRTC.browser.name ? navigator.getDisplayMedia({
video: !0,
audio: isAudioPlusTab(connection)
}).then(function (screen) {
screen.isScreen = !0, mPeer.onGettingLocalMedia(screen), !session.audio && !session.video || isAudioPlusTab(connection) ? callback(screen) : connection.invokeGetUserMedia(null, callback)
}, function (error) {
console.error("Unable to capture screen on Edge. HTTPs and version 17+ is required.")
}) : connection.getScreenConstraints(function (error, screen_constraints) {
connection.invokeGetUserMedia({
audio: !!isAudioPlusTab(connection) && getAudioScreenConstraints(screen_constraints),
video: screen_constraints,
isScreen: !0
}, !session.audio && !session.video || isAudioPlusTab(connection) ? callback : connection.invokeGetUserMedia(null, callback))
}) : (session.audio || session.video) && connection.invokeGetUserMedia(null, callback, session))))
}
function beforeUnload(shiftModerationControlOnLeave, dontCloseSocket) {
connection.closeBeforeUnload && (connection.isInitiator === !0 && connection.dontMakeMeModerator(), connection.peers.getAllParticipants().forEach(function (participant) {
mPeer.onNegotiationNeeded({userLeft: !0}, participant), connection.peers[participant] && connection.peers[participant].peer && connection.peers[participant].peer.close(), delete connection.peers[participant]
}), dontCloseSocket || connection.closeSocket(), connection.broadcasters = [], connection.isInitiator = !1)
}
function applyConstraints(stream, mediaConstraints) {
return stream ? (mediaConstraints.audio && stream.getAudioTracks().forEach(function (track) {
track.applyConstraints(mediaConstraints.audio)
}), void(mediaConstraints.video && stream.getVideoTracks().forEach(function (track) {
track.applyConstraints(mediaConstraints.video)
}))) : void(connection.enableLogs && console.error("No stream to applyConstraints."))
}
function replaceTrack(track, remoteUserId, isVideoTrack) {
return remoteUserId ? void mPeer.replaceTrack(track, remoteUserId, isVideoTrack) : void connection.peers.getAllParticipants().forEach(function (participant) {
mPeer.replaceTrack(track, participant, isVideoTrack)
})
}
function keepNextBroadcasterOnServer() {
if (connection.isInitiator && !connection.session.oneway && !connection.session.broadcast && "many-to-many" === connection.direction) {
var firstBroadcaster = connection.broadcasters[0], otherBroadcasters = [];
connection.broadcasters.forEach(function (broadcaster) {
broadcaster !== firstBroadcaster && otherBroadcasters.push(broadcaster)
}), connection.autoCloseEntireSession || connection.shiftModerationControl(firstBroadcaster, otherBroadcasters, !0)
}
}
forceOptions = forceOptions || {useDefaultDevices: !0}, connection.channel = connection.sessionid = (roomid || location.href.replace(/\/|:|#|\?|\$|\^|%|\.|`|~|!|\+|@|\[|\||]|\|*. /g, "").split("\n").join("").split("\r").join("")) + "";
var mPeer = new MultiPeers(connection), preventDuplicateOnStreamEvents = {};
mPeer.onGettingLocalMedia = function (stream, callback) {
if (callback = callback || function () {
}, !preventDuplicateOnStreamEvents[stream.streamid]) {
preventDuplicateOnStreamEvents[stream.streamid] = !0;
try {
stream.type = "local"
} catch (e) {
}
connection.setStreamEndHandler(stream), getRMCMediaElement(stream, function (mediaElement) {
mediaElement.id = stream.streamid, mediaElement.muted = !0, mediaElement.volume = 0, connection.attachStreams.indexOf(stream) === -1 && connection.attachStreams.push(stream), "undefined" != typeof StreamsHandler && StreamsHandler.setHandlers(stream, !0, connection), connection.streamEvents[stream.streamid] = {
stream: stream,
type: "local",
mediaElement: mediaElement,
userid: connection.userid,
extra: connection.extra,
streamid: stream.streamid,
isAudioMuted: !0
}, setHarkEvents(connection, connection.streamEvents[stream.streamid]), setMuteHandlers(connection, connection.streamEvents[stream.streamid]), connection.onstream(connection.streamEvents[stream.streamid]), callback()
}, connection)
}
}, mPeer.onGettingRemoteMedia = function (stream, remoteUserId) {
try {
stream.type = "remote"
} catch (e) {
}
connection.setStreamEndHandler(stream, "remote-stream"), getRMCMediaElement(stream, function (mediaElement) {
mediaElement.id = stream.streamid, "undefined" != typeof StreamsHandler && StreamsHandler.setHandlers(stream, !1, connection), connection.streamEvents[stream.streamid] = {
stream: stream,
type: "remote",
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
mediaElement: mediaElement,
streamid: stream.streamid
}, setMuteHandlers(connection, connection.streamEvents[stream.streamid]), connection.onstream(connection.streamEvents[stream.streamid])
}, connection)
}, mPeer.onRemovingRemoteMedia = function (stream, remoteUserId) {
var streamEvent = connection.streamEvents[stream.streamid];
streamEvent || (streamEvent = {
stream: stream,
type: "remote",
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
streamid: stream.streamid,
mediaElement: connection.streamEvents[stream.streamid] ? connection.streamEvents[stream.streamid].mediaElement : null
}), connection.peersBackup[streamEvent.userid] && (streamEvent.extra = connection.peersBackup[streamEvent.userid].extra), connection.onstreamended(streamEvent), delete connection.streamEvents[stream.streamid]
}, mPeer.onNegotiationNeeded = function (message, remoteUserId, callback) {
remoteUserId = remoteUserId || message.remoteUserId, connectSocket(function () {
connection.socket.emit(connection.socketMessageEvent, "password" in message ? message : {
remoteUserId: remoteUserId,
message: message,
sender: connection.userid
}, callback || function () {
})
})
}, mPeer.onUserLeft = onUserLeft, mPeer.disconnectWith = function (remoteUserId, callback) {
connection.socket && connection.socket.emit("disconnect-with", remoteUserId, callback || function () {
}), connection.deletePeer(remoteUserId)
}, connection.broadcasters = [], connection.socketOptions = {transport: "polling"}, connection.openOrJoin = function (localUserid, password, callback) {
callback = callback || function () {
}, connection.checkPresence(localUserid, function (isRoomExist, roomid) {
if ("function" == typeof password && "undefined" != typeof password && (callback = password, password = null), !password && connection.password && (password = connection.password), isRoomExist) {
connection.sessionid = roomid;
var localPeerSdpConstraints = !1, remotePeerSdpConstraints = !1,
isOneWay = !!connection.session.oneway, isDataOnly = isData(connection.session);
remotePeerSdpConstraints = {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
}, localPeerSdpConstraints = {
OfferToReceiveAudio: isOneWay ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: isOneWay ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
};
var connectionDescription = {
remoteUserId: connection.sessionid,
message: {
newParticipationRequest: !0,
isOneWay: isOneWay,
isDataOnly: isDataOnly,
localPeerSdpConstraints: localPeerSdpConstraints,
remotePeerSdpConstraints: remotePeerSdpConstraints
},
sender: connection.userid,
password: password || !1
};
return void beforeJoin(connectionDescription.message, function () {
mPeer.onNegotiationNeeded(connectionDescription), callback(isRoomExist, roomid)
})
}
connection.waitingForLocalMedia = !0, connection.isInitiator = !0;
connection.userid;
return connection.userid = connection.sessionid = localUserid || connection.sessionid, connection.userid += "",
connection.socket.emit("changed-uuid", connection.userid), password && connection.socket.emit("set-password", password), isData(connection.session) ? void(connection.waitingForLocalMedia = !1) : void connection.captureUserMedia(function () {
connection.waitingForLocalMedia = !1, callback(isRoomExist, roomid)
})
})
}, connection.waitingForLocalMedia = !1, connection.open = function (localUserid, isPublicModerator, callback) {
connection.waitingForLocalMedia = !0, connection.isInitiator = !0, callback = callback || function () {
}, "function" == typeof isPublicModerator && (callback = isPublicModerator, isPublicModerator = !1);
connection.userid;
connection.userid = connection.sessionid = localUserid || connection.sessionid, connection.userid += "", connectSocket(function () {
return connection.socket.emit("changed-uuid", connection.userid), connection.password && connection.socket.emit("set-password", connection.password), 1 == isPublicModerator && connection.becomePublicModerator(), isData(connection.session) ? (connection.waitingForLocalMedia = !1, void callback()) : void connection.captureUserMedia(function () {
connection.waitingForLocalMedia = !1, callback()
})
})
}, connection.becomePublicModerator = function () {
connection.isInitiator && connection.socket.emit("become-a-public-moderator")
}, connection.dontMakeMeModerator = function () {
connection.socket.emit("dont-make-me-moderator")
}, connection.deletePeer = function (remoteUserId) {
if (remoteUserId) {
var eventObject = {
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {}
};
if (connection.peersBackup[eventObject.userid] && (eventObject.extra = connection.peersBackup[eventObject.userid].extra), connection.onleave(eventObject), connection.peers[remoteUserId]) {
connection.peers[remoteUserId].streams.forEach(function (stream) {
stream.stop()
});
var peer = connection.peers[remoteUserId].peer;
if (peer && "closed" !== peer.iceConnectionState) try {
peer.close()
} catch (e) {
}
connection.peers[remoteUserId] && (connection.peers[remoteUserId].peer = null, delete connection.peers[remoteUserId])
}
if (connection.broadcasters.indexOf(remoteUserId) !== -1) {
var newArray = [];
connection.broadcasters.forEach(function (broadcaster) {
broadcaster !== remoteUserId && newArray.push(broadcaster)
}), connection.broadcasters = newArray, keepNextBroadcasterOnServer()
}
}
}, connection.rejoin = function (connectionDescription) {
if (!connection.isInitiator && connectionDescription && Object.keys(connectionDescription).length) {
var extra = {};
connection.peers[connectionDescription.remoteUserId] && (extra = connection.peers[connectionDescription.remoteUserId].extra, connection.deletePeer(connectionDescription.remoteUserId)), connectionDescription && connectionDescription.remoteUserId && (connection.join(connectionDescription.remoteUserId), connection.onReConnecting({
userid: connectionDescription.remoteUserId,
extra: extra
}))
}
}, connection.join = connection.connect = function (remoteUserId, options) {
connection.sessionid = !!remoteUserId && (remoteUserId.sessionid || remoteUserId.remoteUserId || remoteUserId) || connection.sessionid, connection.sessionid += "";
var localPeerSdpConstraints = !1, remotePeerSdpConstraints = !1, isOneWay = !1, isDataOnly = !1;
if (remoteUserId && remoteUserId.session || !remoteUserId || "string" == typeof remoteUserId) {
var session = remoteUserId ? remoteUserId.session || connection.session : connection.session;
isOneWay = !!session.oneway, isDataOnly = isData(session), remotePeerSdpConstraints = {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
}, localPeerSdpConstraints = {
OfferToReceiveAudio: isOneWay ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: isOneWay ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
}
}
options = options || {};
var cb = function () {
};
"function" == typeof options && (cb = options, options = {}), "undefined" != typeof options.localPeerSdpConstraints && (localPeerSdpConstraints = options.localPeerSdpConstraints), "undefined" != typeof options.remotePeerSdpConstraints && (remotePeerSdpConstraints = options.remotePeerSdpConstraints), "undefined" != typeof options.isOneWay && (isOneWay = options.isOneWay), "undefined" != typeof options.isDataOnly && (isDataOnly = options.isDataOnly);
var connectionDescription = {
remoteUserId: connection.sessionid,
message: {
newParticipationRequest: !0,
isOneWay: isOneWay,
isDataOnly: isDataOnly,
localPeerSdpConstraints: localPeerSdpConstraints,
remotePeerSdpConstraints: remotePeerSdpConstraints
},
sender: connection.userid,
password: connection.password || !1
};
return beforeJoin(connectionDescription.message, function () {
connectSocket(function () {
connection.peers[connection.sessionid] || (mPeer.onNegotiationNeeded(connectionDescription), cb())
})
}), connectionDescription
}, connection.connectWithAllParticipants = function (remoteUserId) {
mPeer.onNegotiationNeeded("connectWithAllParticipants", remoteUserId || connection.sessionid)
}, connection.removeFromBroadcastersList = function (remoteUserId) {
mPeer.onNegotiationNeeded("removeFromBroadcastersList", remoteUserId || connection.sessionid), connection.peers.getAllParticipants(remoteUserId || connection.sessionid).forEach(function (participant) {
mPeer.onNegotiationNeeded("dropPeerConnection", participant), connection.deletePeer(participant)
}), connection.attachStreams.forEach(function (stream) {
stream.stop()
})
}, connection.getUserMedia = connection.captureUserMedia = function (callback, sessionForced) {
callback = callback || function () {
};
var session = sessionForced || connection.session;
return connection.dontCaptureUserMedia || isData(session) ? void callback() : void((session.audio || session.video || session.screen) && (session.screen ? "Edge" === DetectRTC.browser.name ? navigator.getDisplayMedia({
video: !0,
audio: isAudioPlusTab(connection)
}).then(function (screen) {
if (screen.isScreen = !0, mPeer.onGettingLocalMedia(screen), (session.audio || session.video) && !isAudioPlusTab(connection)) {
var nonScreenSession = {};
for (var s in session) "screen" !== s && (nonScreenSession[s] = session[s]);
return void connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession)
}
callback(screen)
}, function (error) {
console.error("Unable to capture screen on Edge. HTTPs and version 17+ is required.")
}) : connection.getScreenConstraints(function (error, screen_constraints) {
if (error) throw error;
connection.invokeGetUserMedia({
audio: !!isAudioPlusTab(connection) && getAudioScreenConstraints(screen_constraints),
video: screen_constraints,
isScreen: !0
}, function (stream) {
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
var nonScreenSession = {};
for (var s in session) "screen" !== s && (nonScreenSession[s] = session[s]);
return void connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession)
}
callback(stream)
})
}) : (session.audio || session.video) && connection.invokeGetUserMedia(sessionForced, callback, session)))
}, connection.closeBeforeUnload = !0, window.addEventListener("beforeunload", beforeUnload, !1), connection.userid = getRandomString(), connection.changeUserId = function (newUserId, callback) {
callback = callback || function () {
}, connection.userid = newUserId || getRandomString(), connection.socket.emit("changed-uuid", connection.userid, callback)
}, connection.extra = {}, connection.attachStreams = [], connection.session = {
audio: !0,
video: !0
}, connection.enableFileSharing = !1, connection.bandwidth = {
screen: !1,
audio: !1,
video: !1
}, connection.codecs = {audio: "opus", video: "VP9"}, connection.processSdp = function (sdp) {
return "Safari" === DetectRTC.browser.name ? sdp : ("VP8" === connection.codecs.video.toUpperCase() && (sdp = CodecsHandler.preferCodec(sdp, "vp8")), "VP9" === connection.codecs.video.toUpperCase() && (sdp = CodecsHandler.preferCodec(sdp, "vp9")), "H264" === connection.codecs.video.toUpperCase() && (sdp = CodecsHandler.preferCodec(sdp, "h264")), "G722" === connection.codecs.audio && (sdp = CodecsHandler.removeNonG722(sdp)), "Firefox" === DetectRTC.browser.name ? sdp : ((connection.bandwidth.video || connection.bandwidth.screen) && (sdp = CodecsHandler.setApplicationSpecificBandwidth(sdp, connection.bandwidth, !!connection.session.screen)), connection.bandwidth.video && (sdp = CodecsHandler.setVideoBitrates(sdp, {
min: 8 * connection.bandwidth.video * 1024,
max: 8 * connection.bandwidth.video * 1024
})), connection.bandwidth.audio && (sdp = CodecsHandler.setOpusAttributes(sdp, {
maxaveragebitrate: 8 * connection.bandwidth.audio * 1024,
maxplaybackrate: 8 * connection.bandwidth.audio * 1024,
stereo: 1,
maxptime: 3
})), sdp))
}, "undefined" != typeof CodecsHandler && (connection.BandwidthHandler = connection.CodecsHandler = CodecsHandler), connection.mediaConstraints = {
audio: {
mandatory: {},
optional: connection.bandwidth.audio ? [{bandwidth: 8 * connection.bandwidth.audio * 1024 || 1048576}] : []
},
video: {
mandatory: {},
optional: connection.bandwidth.video ? [{bandwidth: 8 * connection.bandwidth.video * 1024 || 1048576}, {facingMode: "user"}] : [{facingMode: "user"}]
}
}, "Firefox" === DetectRTC.browser.name && (connection.mediaConstraints = {
audio: !0,
video: !0
}), forceOptions.useDefaultDevices || DetectRTC.isMobileDevice || DetectRTC.load(function () {
var lastAudioDevice, lastVideoDevice;
if (DetectRTC.MediaDevices.forEach(function (device) {
"audioinput" === device.kind && connection.mediaConstraints.audio !== !1 && (lastAudioDevice = device), "videoinput" === device.kind && connection.mediaConstraints.video !== !1 && (lastVideoDevice = device)
}), lastAudioDevice) {
if ("Firefox" === DetectRTC.browser.name) return void(connection.mediaConstraints.audio !== !0 ? connection.mediaConstraints.audio.deviceId = lastAudioDevice.id : connection.mediaConstraints.audio = {deviceId: lastAudioDevice.id});
1 == connection.mediaConstraints.audio && (connection.mediaConstraints.audio = {
mandatory: {},
optional: []
}), connection.mediaConstraints.audio.optional || (connection.mediaConstraints.audio.optional = []);
var optional = [{sourceId: lastAudioDevice.id}];
connection.mediaConstraints.audio.optional = optional.concat(connection.mediaConstraints.audio.optional)
}
if (lastVideoDevice) {
if ("Firefox" === DetectRTC.browser.name) return void(connection.mediaConstraints.video !== !0 ? connection.mediaConstraints.video.deviceId = lastVideoDevice.id : connection.mediaConstraints.video = {deviceId: lastVideoDevice.id});
1 == connection.mediaConstraints.video && (connection.mediaConstraints.video = {
mandatory: {},
optional: []
}), connection.mediaConstraints.video.optional || (connection.mediaConstraints.video.optional = []);
var optional = [{sourceId: lastVideoDevice.id}];
connection.mediaConstraints.video.optional = optional.concat(connection.mediaConstraints.video.optional)
}
}), connection.sdpConstraints = {
mandatory: {OfferToReceiveAudio: !0, OfferToReceiveVideo: !0},
optional: [{VoiceActivityDetection: !1}]
}, connection.rtcpMuxPolicy = "require", connection.iceTransportPolicy = null, connection.optionalArgument = {
optional: [{DtlsSrtpKeyAgreement: !0}, {googImprovedWifiBwe: !0}, {googScreencastMinBitrate: 300}, {googIPv6: !0}, {googDscp: !0}, {googCpuUnderuseThreshold: 55}, {googCpuOveruseThreshold: 85}, {googSuspendBelowMinBitrate: !0}, {googCpuOveruseDetection: !0}],
mandatory: {}
}, connection.iceServers = IceServersHandler.getIceServers(connection), connection.candidates = {
host: !0,
stun: !0,
turn: !0
}, connection.iceProtocols = {tcp: !0, udp: !0}, connection.onopen = function (event) {
connection.enableLogs && console.info("Data connection has been opened between you & ", event.userid)
}, connection.onclose = function (event) {
connection.enableLogs && console.warn("Data connection has been closed between you & ", event.userid)
}, connection.onerror = function (error) {
connection.enableLogs && console.error(error.userid, "data-error", error)
}, connection.onmessage = function (event) {
connection.enableLogs && console.debug("data-message", event.userid, event.data)
}, connection.send = function (data, remoteUserId) {
connection.peers.send(data, remoteUserId)
}, connection.close = connection.disconnect = connection.leave = function () {
beforeUnload(!1, !0)
}, connection.closeEntireSession = function (callback) {
callback = callback || function () {
}, connection.socket.emit("close-entire-session", function looper() {
return connection.getAllParticipants().length ? void setTimeout(looper, 100) : (connection.onEntireSessionClosed({
sessionid: connection.sessionid,
userid: connection.userid,
extra: connection.extra
}), void connection.changeUserId(null, function () {
connection.close(), callback()
}))
})
}, connection.onEntireSessionClosed = function (event) {
connection.enableLogs && console.info("Entire session is closed: ", event.sessionid, event.extra)
}, connection.onstream = function (e) {
var parentNode = connection.videosContainer;
parentNode.insertBefore(e.mediaElement, parentNode.firstChild);
var played = e.mediaElement.play();
return "undefined" != typeof played ? void played["catch"](function () {
}).then(function () {
setTimeout(function () {
e.mediaElement.play()
}, 2e3)
}) : void setTimeout(function () {
e.mediaElement.play()
}, 2e3)
}, connection.onstreamended = function (e) {
e.mediaElement || (e.mediaElement = document.getElementById(e.streamid)), e.mediaElement && e.mediaElement.parentNode && e.mediaElement.parentNode.removeChild(e.mediaElement)
}, connection.direction = "many-to-many", connection.removeStream = function (streamid, remoteUserId) {
var stream;
return connection.attachStreams.forEach(function (localStream) {
localStream.id === streamid && (stream = localStream)
}), stream ? (connection.peers.getAllParticipants().forEach(function (participant) {
if (!remoteUserId || participant === remoteUserId) {
var user = connection.peers[participant];
try {
user.peer.removeStream(stream)
} catch (e) {
}
}
}), void connection.renegotiate()) : void console.warn("No such stream exist.", streamid)
}, connection.addStream = function (session, remoteUserId) {
function gumCallback(stream) {
session.streamCallback && session.streamCallback(stream), connection.renegotiate(remoteUserId)
}
return session.getAudioTracks ? (connection.attachStreams.indexOf(session) === -1 && (session.streamid || (session.streamid = session.id), connection.attachStreams.push(session)), void connection.renegotiate(remoteUserId)) : isData(session) ? void connection.renegotiate(remoteUserId) : void((session.audio || session.video || session.screen) && (session.screen ? "Edge" === DetectRTC.browser.name ? navigator.getDisplayMedia({
video: !0,
audio: isAudioPlusTab(connection)
}).then(function (screen) {
screen.isScreen = !0, mPeer.onGettingLocalMedia(screen), !session.audio && !session.video || isAudioPlusTab(connection) ? gumCallback(screen) : connection.invokeGetUserMedia(null, function (stream) {
gumCallback(stream)
})
}, function (error) {
console.error("Unable to capture screen on Edge. HTTPs and version 17+ is required.")
}) : connection.getScreenConstraints(function (error, screen_constraints) {
return error ? "PermissionDeniedError" === error ? (session.streamCallback && session.streamCallback(null), void(connection.enableLogs && console.error("User rejected to share his screen."))) : alert(error) : void connection.invokeGetUserMedia({
audio: !!isAudioPlusTab(connection) && getAudioScreenConstraints(screen_constraints),
video: screen_constraints,
isScreen: !0
}, function (stream) {
!session.audio && !session.video || isAudioPlusTab(connection) ? gumCallback(stream) : connection.invokeGetUserMedia(null, function (stream) {
gumCallback(stream)
})
})
}) : (session.audio || session.video) && connection.invokeGetUserMedia(null, gumCallback)))
}, connection.invokeGetUserMedia = function (localMediaConstraints, callback, session) {
session || (session = connection.session), localMediaConstraints || (localMediaConstraints = connection.mediaConstraints), getUserMediaHandler({
onGettingLocalMedia: function (stream) {
var videoConstraints = localMediaConstraints.video;
videoConstraints && (videoConstraints.mediaSource || videoConstraints.mozMediaSource ? stream.isScreen = !0 : videoConstraints.mandatory && videoConstraints.mandatory.chromeMediaSource && (stream.isScreen = !0)), stream.isScreen || (stream.isVideo = stream.getVideoTracks().length, stream.isAudio = !stream.isVideo && stream.getAudioTracks().length), mPeer.onGettingLocalMedia(stream, function () {
"function" == typeof callback && callback(stream)
})
},
onLocalMediaError: function (error, constraints) {
mPeer.onLocalMediaError(error, constraints)
},
localMediaConstraints: localMediaConstraints || {
audio: !!session.audio && localMediaConstraints.audio,
video: !!session.video && localMediaConstraints.video
}
})
}, connection.applyConstraints = function (mediaConstraints, streamid) {
if (!MediaStreamTrack || !MediaStreamTrack.prototype.applyConstraints) return void alert("track.applyConstraints is NOT supported in your browser.");
if (streamid) {
var stream;
return connection.streamEvents[streamid] && (stream = connection.streamEvents[streamid].stream), void applyConstraints(stream, mediaConstraints)
}
connection.attachStreams.forEach(function (stream) {
applyConstraints(stream, mediaConstraints)
})
}, connection.replaceTrack = function (session, remoteUserId, isVideoTrack) {
function gumCallback(stream) {
connection.replaceTrack(stream, remoteUserId, isVideoTrack || session.video || session.screen)
}
if (session = session || {}, !RTCPeerConnection.prototype.getSenders) return void connection.addStream(session);
if (session instanceof MediaStreamTrack) return void replaceTrack(session, remoteUserId, isVideoTrack);
if (session instanceof MediaStream) return session.getVideoTracks().length && replaceTrack(session.getVideoTracks()[0], remoteUserId, !0), void(session.getAudioTracks().length && replaceTrack(session.getAudioTracks()[0], remoteUserId, !1));
if (isData(session)) throw"connection.replaceTrack requires audio and/or video and/or screen.";
(session.audio || session.video || session.screen) && (session.screen ? "Edge" === DetectRTC.browser.name ? navigator.getDisplayMedia({
video: !0,
audio: isAudioPlusTab(connection)
}).then(function (screen) {
screen.isScreen = !0, mPeer.onGettingLocalMedia(screen), !session.audio && !session.video || isAudioPlusTab(connection) ? gumCallback(screen) : connection.invokeGetUserMedia(null, gumCallback)
}, function (error) {
console.error("Unable to capture screen on Edge. HTTPs and version 17+ is required.")
}) : connection.getScreenConstraints(function (error, screen_constraints) {
return error ? alert(error) : void connection.invokeGetUserMedia({
audio: !!isAudioPlusTab(connection) && getAudioScreenConstraints(screen_constraints),
video: screen_constraints,
isScreen: !0
}, !session.audio && !session.video || isAudioPlusTab(connection) ? gumCallback : connection.invokeGetUserMedia(null, gumCallback))
}) : (session.audio || session.video) && connection.invokeGetUserMedia(null, gumCallback))
}, connection.resetTrack = function (remoteUsersIds, isVideoTrack) {
remoteUsersIds || (remoteUsersIds = connection.getAllParticipants()), "string" == typeof remoteUsersIds && (remoteUsersIds = [remoteUsersIds]), remoteUsersIds.forEach(function (participant) {
var peer = connection.peers[participant].peer;
"undefined" != typeof isVideoTrack && isVideoTrack !== !0 || !peer.lastVideoTrack || connection.replaceTrack(peer.lastVideoTrack, participant, !0), "undefined" != typeof isVideoTrack && isVideoTrack !== !1 || !peer.lastAudioTrack || connection.replaceTrack(peer.lastAudioTrack, participant, !1)
})
}, connection.renegotiate = function (remoteUserId) {
return remoteUserId ? void mPeer.renegotiatePeer(remoteUserId) : void connection.peers.getAllParticipants().forEach(function (participant) {
mPeer.renegotiatePeer(participant)
})
}, connection.setStreamEndHandler = function (stream, isRemote) {
if (stream && stream.addEventListener && (isRemote = !!isRemote, !stream.alreadySetEndHandler)) {
stream.alreadySetEndHandler = !0;
var streamEndedEvent = "ended";
"oninactive" in stream && (streamEndedEvent = "inactive"), stream.addEventListener(streamEndedEvent, function () {
if (stream.idInstance && currentUserMediaRequest.remove(stream.idInstance), !isRemote) {
var streams = [];
connection.attachStreams.forEach(function (s) {
s.id != stream.id && streams.push(s)
}), connection.attachStreams = streams
}
var streamEvent = connection.streamEvents[stream.streamid];
if (streamEvent || (streamEvent = {
stream: stream,
streamid: stream.streamid,
type: isRemote ? "remote" : "local",
userid: connection.userid,
extra: connection.extra,
mediaElement: connection.streamEvents[stream.streamid] ? connection.streamEvents[stream.streamid].mediaElement : null
}), isRemote && connection.peers[streamEvent.userid]) {
var peer = connection.peers[streamEvent.userid].peer, streams = [];
peer.getRemoteStreams().forEach(function (s) {
s.id != stream.id && streams.push(s)
}), connection.peers[streamEvent.userid].streams = streams
}
streamEvent.userid === connection.userid && "remote" === streamEvent.type || (connection.peersBackup[streamEvent.userid] && (streamEvent.extra = connection.peersBackup[streamEvent.userid].extra), connection.onstreamended(streamEvent), delete connection.streamEvents[stream.streamid])
}, !1)
}
}, connection.onMediaError = function (error, constraints) {
connection.enableLogs && console.error(error, constraints)
}, connection.addNewBroadcaster = function (broadcasterId, userPreferences) {
connection.socket.isIO || (connection.broadcasters.length && setTimeout(function () {
mPeer.connectNewParticipantWithAllBroadcasters(broadcasterId, userPreferences, connection.broadcasters.join("|-,-|"))
}, 1e4), connection.session.oneway || connection.session.broadcast || "many-to-many" !== connection.direction || connection.broadcasters.indexOf(broadcasterId) !== -1 || (connection.broadcasters.push(broadcasterId), keepNextBroadcasterOnServer()))
}, connection.autoCloseEntireSession = !1, connection.filesContainer = connection.videosContainer = document.body || document.documentElement, connection.isInitiator = !1, connection.shareFile = mPeer.shareFile, "undefined" != typeof FileProgressBarHandler && FileProgressBarHandler.handle(connection), "undefined" != typeof TranslationHandler && TranslationHandler.handle(connection), connection.token = getRandomString, connection.onNewParticipant = function (participantId, userPreferences) {
connection.acceptParticipationRequest(participantId, userPreferences)
}, connection.acceptParticipationRequest = function (participantId, userPreferences) {
userPreferences.successCallback && (userPreferences.successCallback(), delete userPreferences.successCallback), mPeer.createNewPeer(participantId, userPreferences)
}, connection.onShiftedModerationControl = function (sender, existingBroadcasters) {
connection.acceptModerationControl(sender, existingBroadcasters)
}, connection.acceptModerationControl = function (sender, existingBroadcasters) {
connection.isInitiator = !0, connection.broadcasters = existingBroadcasters, connection.peers.getAllParticipants().forEach(function (participant) {
mPeer.onNegotiationNeeded({
changedUUID: sender,
oldUUID: connection.userid,
newUUID: sender
}, participant)
}), connection.userid = sender, connection.changeUserId(connection.userid)
}, connection.shiftModerationControl = function (remoteUserId, existingBroadcasters, firedOnLeave) {
mPeer.onNegotiationNeeded({
shiftedModerationControl: !0,
broadcasters: existingBroadcasters,
firedOnLeave: !!firedOnLeave
}, remoteUserId)
}, "undefined" != typeof StreamsHandler && (connection.StreamsHandler = StreamsHandler), connection.onleave = function (userid) {
}, connection.invokeSelectFileDialog = function (callback) {
var selector = new FileSelector;
selector.accept = "*.*", selector.selectSingleFile(callback)
}, connection.getPublicModerators = connection.getPublicUsers = function (userIdStartsWith, callback) {
"function" == typeof userIdStartsWith && (callback = userIdStartsWith), connectSocket(function () {
connection.socket.emit("get-public-moderators", "string" == typeof userIdStartsWith ? userIdStartsWith : "", callback)
})
}, connection.onmute = function (e) {
if (e && e.mediaElement) if ("both" === e.muteType || "video" === e.muteType) {
e.mediaElement.src = null;
var paused = e.mediaElement.pause();
"undefined" != typeof paused ? paused.then(function () {
e.mediaElement.poster = e.snapshot || "https://cdn.webrtc-experiment.com/images/muted.png"
}) : e.mediaElement.poster = e.snapshot || "https://cdn.webrtc-experiment.com/images/muted.png"
} else "audio" === e.muteType && (e.mediaElement.muted = !0)
}, connection.onunmute = function (e) {
e && e.mediaElement && e.stream && ("both" === e.unmuteType || "video" === e.unmuteType ? (e.mediaElement.poster = null, e.mediaElement.srcObject = e.stream, e.mediaElement.play()) : "audio" === e.unmuteType && (e.mediaElement.muted = !1))
}, connection.onExtraDataUpdated = function (event) {
event.status = "online", connection.onUserStatusChanged(event, !0)
}, connection.onJoinWithPassword = function (remoteUserId) {
console.warn(remoteUserId, "is password protected. Please join with password.")
}, connection.onInvalidPassword = function (remoteUserId, oldPassword) {
console.warn(remoteUserId, "is password protected. Please join with valid password. Your old password", oldPassword, "is wrong.")
}, connection.onPasswordMaxTriesOver = function (remoteUserId) {
console.warn(remoteUserId, "is password protected. Your max password tries exceeded the limit.")
}, connection.getAllParticipants = function (sender) {
return connection.peers.getAllParticipants(sender)
}, "undefined" != typeof StreamsHandler && (StreamsHandler.onSyncNeeded = function (streamid, action, type) {
connection.peers.getAllParticipants().forEach(function (participant) {
mPeer.onNegotiationNeeded({
streamid: streamid,
action: action,
streamSyncNeeded: !0,
type: type || "both"
}, participant)
})
}), connection.connectSocket = function (callback) {
connectSocket(callback)
}, connection.closeSocket = function () {
try {
io.sockets = {}
} catch (e) {
}
connection.socket && ("function" == typeof connection.socket.disconnect && connection.socket.disconnect(), "function" == typeof connection.socket.resetProps && connection.socket.resetProps(), connection.socket = null)
}, connection.getSocket = function (callback) {
return connection.socket ? callback && callback(connection.socket) : connectSocket(callback), connection.socket
}, connection.getRemoteStreams = mPeer.getRemoteStreams;
var skipStreams = ["selectFirst", "selectAll", "forEach"];
if (connection.streamEvents = {
selectFirst: function (options) {
if (!options) {
var firstStream;
for (var str in connection.streamEvents) skipStreams.indexOf(str) !== -1 || firstStream || (firstStream = connection.streamEvents[str]);
return firstStream
}
}, selectAll: function () {
}
}, connection.socketURL = "/", connection.socketMessageEvent = "RTCMultiConnection-Message", connection.socketCustomEvent = "RTCMultiConnection-Custom-Message", connection.DetectRTC = DetectRTC, connection.setCustomSocketEvent = function (customEvent) {
customEvent && (connection.socketCustomEvent = customEvent), connection.socket && connection.socket.emit("set-custom-socket-event-listener", connection.socketCustomEvent)
}, connection.getNumberOfBroadcastViewers = function (broadcastId, callback) {
connection.socket && broadcastId && callback && connection.socket.emit("get-number-of-users-in-specific-broadcast", broadcastId, callback)
}, connection.onNumberOfBroadcastViewersUpdated = function (event) {
connection.enableLogs && connection.isInitiator && console.info("Number of broadcast (", event.broadcastId, ") viewers", event.numberOfBroadcastViewers)
}, connection.onUserStatusChanged = function (event, dontWriteLogs) {
connection.enableLogs && !dontWriteLogs && console.info(event.userid, event.status)
}, connection.getUserMediaHandler = getUserMediaHandler, connection.multiPeersHandler = mPeer, connection.enableLogs = !0, connection.setCustomSocketHandler = function (customSocketHandler) {
"undefined" != typeof SocketConnection && (SocketConnection = customSocketHandler)
}, connection.chunkSize = 65e3, connection.maxParticipantsAllowed = 1e3, connection.disconnectWith = mPeer.disconnectWith, connection.checkPresence = function (remoteUserId, callback) {
return "SSEConnection" === SocketConnection.name ? void SSEConnection.checkPresence(remoteUserId, function (isRoomExist, roomid) {
return connection.socket ? void callback(isRoomExist, roomid) : (isRoomExist || (connection.userid = roomid), void connection.connectSocket(function () {
callback(isRoomExist, roomid)
}))
}) : connection.socket ? void connection.socket.emit("check-presence", (remoteUserId || connection.sessionid) + "", callback) : void connection.connectSocket(function () {
connection.checkPresence(remoteUserId, callback)
})
}, connection.onReadyForOffer = function (remoteUserId, userPreferences) {
connection.multiPeersHandler.createNewPeer(remoteUserId, userPreferences)
}, connection.setUserPreferences = function (userPreferences) {
return connection.dontAttachStream && (userPreferences.dontAttachLocalStream = !0), connection.dontGetRemoteStream && (userPreferences.dontGetRemoteStream = !0), userPreferences
}, connection.updateExtraData = function () {
connection.socket.emit("extra-data-updated", connection.extra)
}, connection.enableScalableBroadcast = !1, connection.maxRelayLimitPerUser = 3, connection.dontCaptureUserMedia = !1, connection.dontAttachStream = !1, connection.dontGetRemoteStream = !1, connection.onReConnecting = function (event) {
connection.enableLogs && console.info("ReConnecting with", event.userid, "...")
}, connection.beforeAddingStream = function (stream) {
return stream
}, connection.beforeRemovingStream = function (stream) {
return stream
}, "undefined" != typeof isChromeExtensionAvailable && (connection.checkIfChromeExtensionAvailable = isChromeExtensionAvailable), "undefined" != typeof isFirefoxExtensionAvailable && (connection.checkIfChromeExtensionAvailable = isFirefoxExtensionAvailable), "undefined" != typeof getChromeExtensionStatus && (connection.getChromeExtensionStatus = getChromeExtensionStatus), connection.getScreenConstraints = function (callback, audioPlusTab) {
isAudioPlusTab(connection, audioPlusTab) && (audioPlusTab = !0), getScreenConstraints(function (error, screen_constraints) {
error || (screen_constraints = connection.modifyScreenConstraints(screen_constraints), callback(error, screen_constraints))
}, audioPlusTab)
}, connection.modifyScreenConstraints = function (screen_constraints) {
return screen_constraints
}, connection.onPeerStateChanged = function (state) {
connection.enableLogs && state.iceConnectionState.search(/closed|failed/gi) !== -1 && console.error("Peer connection is closed between you & ", state.userid, state.extra, "state:", state.iceConnectionState)
}, connection.isOnline = !0, listenEventHandler("online", function () {
connection.isOnline = !0
}), listenEventHandler("offline", function () {
connection.isOnline = !1
}), connection.isLowBandwidth = !1, navigator && navigator.connection && navigator.connection.type && (connection.isLowBandwidth = navigator.connection.type.toString().toLowerCase().search(/wifi|cell/g) !== -1, connection.isLowBandwidth)) {
if (connection.bandwidth = {
audio: !1,
video: !1,
screen: !1
}, connection.mediaConstraints.audio && connection.mediaConstraints.audio.optional && connection.mediaConstraints.audio.optional.length) {
var newArray = [];
connection.mediaConstraints.audio.optional.forEach(function (opt) {
"undefined" == typeof opt.bandwidth && newArray.push(opt)
}), connection.mediaConstraints.audio.optional = newArray
}
if (connection.mediaConstraints.video && connection.mediaConstraints.video.optional && connection.mediaConstraints.video.optional.length) {
var newArray = [];
connection.mediaConstraints.video.optional.forEach(function (opt) {
"undefined" == typeof opt.bandwidth && newArray.push(opt)
}), connection.mediaConstraints.video.optional = newArray
}
}
connection.getExtraData = function (remoteUserId) {
if (!remoteUserId) throw"remoteUserId is required.";
return connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {}
}, forceOptions.autoOpenOrJoin && connection.openOrJoin(connection.sessionid), connection.onUserIdAlreadyTaken = function (useridAlreadyTaken, yourNewUserId) {
connection.enableLogs && console.warn("Userid already taken.", useridAlreadyTaken, "Your new userid:", yourNewUserId), connection.join(useridAlreadyTaken)
}, connection.onRoomFull = function (roomid) {
connection.enableLogs && console.warn(roomid, "is full.")
}, connection.trickleIce = !0, connection.version = "3.4.5", connection.onSettingLocalDescription = function (event) {
connection.enableLogs && console.info("Set local description for remote user", event.userid)
}, connection.oneRoomAlreadyExist = function (roomid) {
connection.enableLogs && console.info('Server says "Room ', roomid, "already exist. Joining instead."), connection.join(roomid)
}, connection.resetScreen = function () {
sourceId = null, DetectRTC && DetectRTC.screen && delete DetectRTC.screen.sourceId, currentUserMediaRequest = {
streams: [],
mutex: !1,
queueRequests: []
}
}, connection.autoCreateMediaElement = !0, connection.password = null
}(this)
};