matrix-engine
Version:
basic_timeline improved, VT func setup vide html element id with name arg.- DISABLE RAYCAST DEBUG TEST [2.3.3] Fix for GUI win desktop [2.3.0] DestrucMesh solution & loading convex objs for physics BASIC, SpriteAnimation CPU/texture solution added, Improv
1,257 lines (1,053 loc) • 226 kB
JavaScript
'use strict';
// Last time updated: 2019-06-15 4:26:11 PM UTC
// _________________________
// RTCMultiConnection v3.6.9
// Open-Sourced: https://github.com/muaz-khan/RTCMultiConnection
// --------------------------------------------------
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// --------------------------------------------------
var RTCMultiConnection3 = function(roomid, forceOptions) {
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
(function(that) {
if (!that) {
return;
}
if (typeof window !== 'undefined') {
return;
}
if (typeof global === 'undefined') {
return;
}
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
if (!global.console) {
global.console = {};
}
if (typeof global.console.debug === 'undefined') {
global.console.debug = global.console.info = global.console.error = global.console.log = global.console.log || function() {
console.log(arguments);
};
}
if (typeof document === 'undefined') {
/*global document:true */
that.document = {};
document.createElement = document.captureStream = document.mozCaptureStream = function() {
var obj = {
getContext: function() {
return obj;
},
play: function() {},
pause: function() {},
drawImage: function() {},
toDataURL: function() {
return '';
}
};
return obj;
};
document.addEventListener = document.removeEventListener = that.addEventListener = that.removeEventListener = function() {};
that.HTMLVideoElement = that.HTMLMediaElement = function() {};
}
if (typeof io === 'undefined') {
that.io = function() {
return {
on: function(eventName, callback) {
callback = callback || function() {};
if (eventName === 'connect') {
callback();
}
},
emit: function(eventName, data, callback) {
callback = callback || function() {};
if (eventName === 'open-room' || eventName === 'join-room') {
callback(true, data.sessionid, null);
}
}
};
};
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: '',
origin: 'self'
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 0,
height: 0
};
}
if (typeof URL === 'undefined') {
/*global screen:true */
that.URL = {
createObjectURL: function() {
return '';
},
revokeObjectURL: function() {
return '';
}
};
}
/*global window:true */
that.window = global;
})(typeof global !== 'undefined' ? global : null);
function SocketConnection(connection, connectCallback) {
function isData(session) {
return !session.audio && !session.video && !session.screen && session.data;
}
var parameters = '';
parameters += '?userid=' + connection.userid;
parameters += '&sessionid=' + connection.sessionid;
parameters += '&msgEvent=' + connection.socketMessageEvent;
parameters += '&socketCustomEvent=' + connection.socketCustomEvent;
parameters += '&autoCloseEntireSession=' + !!connection.autoCloseEntireSession;
if (connection.session.broadcast === true) {
parameters += '&oneToMany=true';
}
parameters += '&maxParticipantsAllowed=' + connection.maxParticipantsAllowed;
if (connection.enableScalableBroadcast) {
parameters += '&enableScalableBroadcast=true';
parameters += '&maxRelayLimitPerUser=' + (connection.maxRelayLimitPerUser || 2);
}
parameters += '&extra=' + JSON.stringify(connection.extra || {});
if (connection.socketCustomParameters) {
parameters += connection.socketCustomParameters;
}
try {
io.sockets = {};
} catch (e) {};
if (!connection.socketURL) {
connection.socketURL = '/';
}
if (connection.socketURL.substr(connection.socketURL.length - 1, 1) != '/') {
// connection.socketURL = 'https://domain.com:999/';
throw '"socketURL" MUST end with a slash.';
}
if (connection.enableLogs) {
if (connection.socketURL == '/') {
connection.socketURL = "https://localhost:999/";
}
}
console.info('socket.io url is: ', connection.socketURL);
try {
connection.socket = io(connection.socketURL + parameters, {
withCredentials: true });
} catch (e) {
connection.socket = io.connect(connection.socketURL + parameters, connection.socketOptions, {
withCredentials: true });
}
var mPeer = connection.multiPeersHandler;
connection.socket.on('extra-data-updated', function(remoteUserId, extra) {
if (!connection.peers[remoteUserId]) return;
connection.peers[remoteUserId].extra = extra;
connection.onExtraDataUpdated({
userid: remoteUserId,
extra: extra
});
updateExtraBackup(remoteUserId, extra);
});
function updateExtraBackup(remoteUserId, extra) {
if (!connection.peersBackup[remoteUserId]) {
connection.peersBackup[remoteUserId] = {
userid: remoteUserId,
extra: {}
};
}
connection.peersBackup[remoteUserId].extra = extra;
}
function onMessageEvent(message) {
if (message.remoteUserId != connection.userid) return;
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
});
updateExtraBackup(message.sender, message.extra);
}
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 (action === 'ended' || action === 'inactive' || action === 'stream-removed') {
if (connection.peersBackup[stream.userid]) {
stream.extra = connection.peersBackup[stream.userid].extra;
}
connection.onstreamended(stream);
return;
}
var type = message.message.type != 'both' ? message.message.type : null;
if (typeof stream.stream[action] == 'function') {
try {
stream.stream[action](type);
} catch(err) {}
}
return;
}
if (message.message === 'dropPeerConnection') {
connection.deletePeer(message.sender);
return;
}
if (message.message.allParticipants) {
if (message.message.allParticipants.indexOf(message.sender) === -1) {
message.message.allParticipants.push(message.sender);
}
message.message.allParticipants.forEach(function(participant) {
mPeer[!connection.peers[participant] ? 'createNewPeer' : 'renegotiatePeer'](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 || connection.direction === 'one-way',
isDataOnly: isData(connection.session)
});
});
return;
}
if (message.message.newParticipant) {
if (message.message.newParticipant == connection.userid) return;
if (!!connection.peers[message.message.newParticipant]) return;
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 || connection.direction === 'one-way',
isDataOnly: isData(connection.session)
});
return;
}
if (message.message.readyForOffer) {
if (connection.attachStreams.length) {
connection.waitingForLocalMedia = false;
}
if (connection.waitingForLocalMedia) {
// if someone is waiting to join you
// make sure that we've local media before making a handshake
setTimeout(function() {
onMessageEvent(message);
}, 1);
return;
}
}
if (message.message.newParticipationRequest && message.sender !== connection.userid) {
if (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: typeof message.message.isOneWay !== 'undefined' ? message.message.isOneWay : !!connection.session.oneway || connection.direction === 'one-way',
isDataOnly: typeof message.message.isDataOnly !== 'undefined' ? message.message.isDataOnly : isData(connection.session),
dontGetRemoteStream: typeof message.message.isOneWay !== 'undefined' ? message.message.isOneWay : !!connection.session.oneway || connection.direction === 'one-way',
dontAttachLocalStream: !!message.message.dontGetRemoteStream,
connectionDescription: message,
successCallback: function() {}
};
connection.onNewParticipant(message.sender, userPreferences);
return;
}
if (message.message.changedUUID) {
if (connection.peers[message.message.oldUUID]) {
connection.peers[message.message.newUUID] = connection.peers[message.message.oldUUID];
delete connection.peers[message.message.oldUUID];
}
}
if (message.message.userLeft) {
mPeer.onUserLeft(message.sender);
if (!!message.message.autoCloseEntireSession) {
connection.leave();
}
return;
}
mPeer.addNegotiatedMessage(message.message, message.sender);
}
connection.socket.on(connection.socketMessageEvent, onMessageEvent);
var alreadyConnected = false;
connection.socket.resetProps = function() {
alreadyConnected = false;
};
connection.socket.on('connect', function() {
if (alreadyConnected) {
return;
}
alreadyConnected = true;
if (connection.enableLogs) {
console.info('socket.io connection is opened.');
}
setTimeout(function() {
connection.socket.emit('extra-data-updated', connection.extra);
}, 1000);
if (connectCallback) {
connectCallback(connection.socket);
}
});
connection.socket.on('disconnect', function(event) {
connection.onSocketDisconnect(event);
});
connection.socket.on('error', function(event) {
connection.onSocketError(event);
});
connection.socket.on('user-disconnected', function(remoteUserId) {
if (remoteUserId === connection.userid) {
return;
}
connection.onUserStatusChanged({
userid: remoteUserId,
status: 'offline',
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra || {} : {}
});
connection.deletePeer(remoteUserId);
});
connection.socket.on('user-connected', function(userid) {
if (userid === connection.userid) {
return;
}
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.onUserIdAlreadyTaken(useridAlreadyTaken, yourNewUserId);
});
connection.socket.on('logs', function(log) {
if (!connection.enableLogs) return;
console.debug('server-logs', log);
});
connection.socket.on('number-of-broadcast-viewers-updated', function(data) {
connection.onNumberOfBroadcastViewersUpdated(data);
});
connection.socket.on('set-isInitiator-true', function(sessionid) {
if (sessionid != connection.sessionid) return;
connection.isInitiator = true;
});
}
function MultiPeers(connection) {
var self = this;
var skipPeers = ['getAllParticipants', 'getLength', 'selectFirst', 'streams', 'send', 'forEach'];
connection.peers = {
getLength: function() {
var numberOfPeers = 0;
for (var peer in this) {
if (skipPeers.indexOf(peer) == -1) {
numberOfPeers++;
}
}
return numberOfPeers;
},
selectFirst: function() {
var firstPeer;
for (var peer in this) {
if (skipPeers.indexOf(peer) == -1) {
firstPeer = this[peer];
}
}
return firstPeer;
},
getAllParticipants: function(sender) {
var allPeers = [];
for (var peer in this) {
if (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)) {
if (connection.enableFileSharing) {
self.shareFile(data, remoteUserId);
return;
}
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
}
if (data.type !== 'text' && !(data instanceof ArrayBuffer) && !(data instanceof DataView)) {
TextSender.send({
text: data,
channel: this,
connection: connection,
remoteUserId: remoteUserId
});
return;
}
if (data.type === 'text') {
data = JSON.stringify(data);
}
if (remoteUserId) {
var remoteUser = connection.peers[remoteUserId];
if (remoteUser) {
if (!remoteUser.channels.length) {
connection.peers[remoteUserId].createDataChannel();
connection.renegotiate(remoteUserId);
setTimeout(function() {
that.send(data, remoteUserId);
}, 3000);
return;
}
remoteUser.channels.forEach(function(channel) {
channel.send(data);
});
return;
}
}
this.getAllParticipants().forEach(function(participant) {
if (!that[participant].channels.length) {
connection.peers[participant].createDataChannel();
connection.renegotiate(participant);
setTimeout(function() {
that[participant].channels.forEach(function(channel) {
channel.send(data);
});
}, 3000);
return;
}
that[participant].channels.forEach(function(channel) {
channel.send(data);
});
});
}
};
this.uuid = connection.userid;
this.getLocalConfig = function(remoteSdp, remoteUserId, userPreferences) {
if (!userPreferences) {
userPreferences = {};
}
return {
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)
if (localCandidate) {
self.onNegotiationNeeded(localCandidate, remoteUserId);
}
},
remoteSdp: remoteSdp,
onDataChannelMessage: function(message) {
if (!connection.fbr && connection.enableFileSharing) initFileBufferReader();
if (typeof message == 'string' || !connection.enableFileSharing) {
self.onDataChannelMessage(message, remoteUserId);
return;
}
var that = this;
if (message instanceof ArrayBuffer || message instanceof DataView) {
connection.fbr.convertToObject(message, function(object) {
that.onDataChannelMessage(object);
});
return;
}
if (message.readyForNextChunk) {
connection.fbr.getNextChunk(message, function(nextChunk, isLastChunk) {
connection.peers[remoteUserId].channels.forEach(function(channel) {
channel.send(nextChunk);
});
}, remoteUserId);
return;
}
if (message.chunkMissing) {
connection.fbr.chunkMissing(message);
return;
}
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) {
if (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);
if (states.iceConnectionState === 'new') {
self.onNegotiationStarted(remoteUserId, states);
}
if (states.iceConnectionState === 'connected') {
self.onNegotiationCompleted(remoteUserId, states);
}
if (states.iceConnectionState.search(/closed|failed/gi) !== -1) {
self.onUserLeft(remoteUserId);
self.disconnectWith(remoteUserId);
}
}
};
};
this.createNewPeer = function(remoteUserId, userPreferences) {
if (connection.maxParticipantsAllowed <= connection.getAllParticipants().length) {
return;
}
userPreferences = userPreferences || {};
if (connection.isInitiator && !!connection.session.audio && connection.session.audio === 'two-way' && !userPreferences.streamsToShare) {
userPreferences.isOneWay = false;
userPreferences.isDataOnly = false;
userPreferences.session = connection.session;
}
if (!userPreferences.isOneWay && !userPreferences.isDataOnly) {
userPreferences.isOneWay = true;
this.onNegotiationNeeded({
enableMedia: true,
userPreferences: userPreferences
}, remoteUserId);
return;
}
userPreferences = connection.setUserPreferences(userPreferences, remoteUserId);
var localConfig = this.getLocalConfig(null, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig);
};
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.renegotiatePeer = function(remoteUserId, userPreferences, remoteSdp) {
if (!connection.peers[remoteUserId]) {
if (connection.enableLogs) {
console.error('Peer (' + remoteUserId + ') does not exist. Renegotiation skipped.');
}
return;
}
if (!userPreferences) {
userPreferences = {};
}
userPreferences.renegotiatingPeer = true;
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.replaceTrack = function(track, remoteUserId, isVideoTrack) {
if (!connection.peers[remoteUserId]) {
throw 'This peer (' + remoteUserId + ') does not exist.';
}
var peer = connection.peers[remoteUserId].peer;
if (!!peer.getSenders && typeof peer.getSenders === 'function' && peer.getSenders().length) {
peer.getSenders().forEach(function(rtpSender) {
if (isVideoTrack && rtpSender.track.kind === 'video') {
connection.peers[remoteUserId].peer.lastVideoTrack = rtpSender.track;
rtpSender.replaceTrack(track);
}
if (!isVideoTrack && rtpSender.track.kind === 'audio') {
connection.peers[remoteUserId].peer.lastAudioTrack = rtpSender.track;
rtpSender.replaceTrack(track);
}
});
return;
}
console.warn('RTPSender.replaceTrack is NOT supported.');
this.renegotiatePeer(remoteUserId);
};
this.onNegotiationNeeded = function(message, remoteUserId) {};
this.addNegotiatedMessage = function(message, remoteUserId) {
if (message.type && message.sdp) {
if (message.type == 'answer') {
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].addRemoteSdp(message);
}
}
if (message.type == 'offer') {
if (message.renegotiatingPeer) {
this.renegotiatePeer(remoteUserId, null, message);
} else {
this.createAnsweringPeer(message, remoteUserId);
}
}
if (connection.enableLogs) {
console.log('Remote peer\'s sdp:', message.sdp);
}
return;
}
if (message.candidate) {
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].addRemoteCandidate(message);
}
if (connection.enableLogs) {
console.log('Remote peer\'s candidate pairs:', message.candidate);
}
return;
}
if (message.enableMedia) {
connection.session = message.userPreferences.session || connection.session;
if (connection.session.oneway && connection.attachStreams.length) {
connection.attachStreams = [];
}
if (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: true,
userPreferences: message.userPreferences
}, remoteUserId);
}
if (message.readyForOffer) {
connection.onReadyForOffer(remoteUserId, message.userPreferences);
}
function cb(stream) {
gumCallback(stream, message, remoteUserId);
}
};
function gumCallback(stream, message, remoteUserId) {
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: true,
userPreferences: message.userPreferences
}, remoteUserId);
}
this.onGettingRemoteMedia = function(stream, remoteUserId) {};
this.onRemovingRemoteMedia = function(stream, remoteUserId) {};
this.onGettingLocalMedia = function(localStream) {};
this.onLocalMediaError = function(error, constraints) {
connection.onMediaError(error, constraints);
};
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);
};
}
this.shareFile = function(file, remoteUserId) {
initFileBufferReader();
connection.fbr.readAsArrayBuffer(file, function(uuid) {
var arrayOfUsers = connection.getAllParticipants();
if (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,
// extra: connection.extra,
chunkSize: DetectRTC.browser.name === 'Firefox' ? 15 * 1000 : connection.chunkSize || 0
});
};
if (typeof 'TextReceiver' !== 'undefined') {
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) {
// keep last channel only; we are not expecting parallel/channels channels
if (connection.peers[remoteUserId].channels.length) {
connection.peers[remoteUserId].channels = [channel];
return;
}
connection.peers[remoteUserId].channels.push(channel);
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) {
remoteUserId = remoteUserId || connection.peers.getAllParticipants()[0];
return connection.peers[remoteUserId] ? connection.peers[remoteUserId].streams : [];
};
}
'use strict';
// Last Updated On: 2019-01-10 5:32:55 AM UTC
// ________________
// DetectRTC v1.3.9
// Open-Sourced: https://github.com/muaz-khan/DetectRTC
// --------------------------------------------------
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// --------------------------------------------------
(function() {
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node && /*node-process*/ !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) {
if (typeof window !== 'undefined') {
return;
}
if (typeof window === 'undefined' && typeof global !== 'undefined') {
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
/*global window:true */
that.window = global;
} else if (typeof window === 'undefined') {
// window = this;
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: ''
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 0,
height: 0
};
}
})(typeof global !== 'undefined' ? global : window);
/*global navigator:true */
var navigator = window.navigator;
if (typeof navigator !== 'undefined') {
if (typeof navigator.webkitGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
if (typeof navigator.mozGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.mozGetUserMedia;
}
} else {
navigator = {
getUserMedia: function() {},
userAgent: browserFakeUserAgent
};
}
var isMobileDevice = !!(/Android|webOS|iPhone|iPad|iPod|BB10|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(navigator.userAgent || ''));
var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob);
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isFirefox = typeof window.InstallTrigger !== 'undefined';
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var isChrome = !!window.chrome && !isOpera;
var isIE = typeof document !== 'undefined' && !!document.documentMode && !isEdge;
// this one can also be used:
// https://www.websocket.org/js/stuff.js (DetectBrowser.js)
function getBrowserInfo() {
var nVer = navigator.appVersion;
var nAgt = navigator.userAgent;
var browserName = navigator.appName;
var fullVersion = '' + parseFloat(navigator.appVersion);
var majorVersion = parseInt(navigator.appVersion, 10);
var nameOffset, verOffset, ix;
// both and safri and chrome has same userAgent
if (isSafari && !isChrome && nAgt.indexOf('CriOS') !== -1) {
isSafari = false;
isChrome = true;
}
// In Opera, the true version is after 'Opera' or after 'Version'
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;
}
}
// In MSIE version <=10, the true version is after 'MSIE' in userAgent
// In IE 11, look for the string after 'rv:'
else if (isIE) {
verOffset = nAgt.indexOf('rv:');
if (verOffset > 0) { //IE 11
fullVersion = nAgt.substring(verOffset + 3);
} else { //IE 10 or earlier
verOffset = nAgt.indexOf('MSIE');
fullVersion = nAgt.substring(verOffset + 5);
}
browserName = 'IE';
}
// In Chrome, the true version is after 'Chrome'
else if (isChrome) {
verOffset = nAgt.indexOf('Chrome');
browserName = 'Chrome';
fullVersion = nAgt.substring(verOffset + 7);
}
// In Safari, the true version is after 'Safari' or after 'Version'
else if (isSafari) {
verOffset = nAgt.indexOf('Safari');
browserName = 'Safari';
fullVersion = nAgt.substring(verOffset + 7);
if ((verOffset = nAgt.indexOf('Version')) !== -1) {
fullVersion = nAgt.substring(verOffset + 8);
}
if (navigator.userAgent.indexOf('Version/') !== -1) {
fullVersion = navigator.userAgent.split('Version/')[1].split(' ')[0];
}
}
// In Firefox, the true version is after 'Firefox'
else if (isFirefox) {
verOffset = nAgt.indexOf('Firefox');
browserName = 'Firefox';
fullVersion = nAgt.substring(verOffset + 8);
}
// In most other browsers, 'name/version' is at the end of userAgent
else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
browserName = nAgt.substring(nameOffset, verOffset);
fullVersion = nAgt.substring(verOffset + 1);
if (browserName.toLowerCase() === browserName.toUpperCase()) {
browserName = navigator.appName;
}
}
if (isEdge) {
browserName = 'Edge';
fullVersion = navigator.userAgent.split('Edge/')[1];
// fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString();
}
// trim the fullVersion string at semicolon/space/bracket if present
if ((ix = fullVersion.search(/[; \)]/)) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
majorVersion = parseInt('' + fullVersion, 10);
if (isNaN(majorVersion)) {
fullVersion = '' + parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion, 10);
}
return {
fullVersion: fullVersion,
version: majorVersion,
name: browserName,
isPrivateBrowsing: false
};
}
// via: https://gist.github.com/cou929/7973956
function retry(isDone, next) {
var currentTrial = 0,
maxRetry = 50,
interval = 10,
isTimeout = false;
var id = window.setInterval(
function() {
if (isDone()) {
window.clearInterval(id);
next(isTimeout);
}
if (currentTrial++ > maxRetry) {
window.clearInterval(id);
isTimeout = true;
next(isTimeout);
}
},
10
);
}
function isIE10OrLater(userAgent) {
var ua = userAgent.toLowerCase();
if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
return false;
}
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
if (match && parseInt(match[1], 10) >= 10) {
return true;
}
return false;
}
function detectPrivateMode(callback) {
var isPrivate;
try {
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(
window.TEMPORARY, 1,
function() {
isPrivate = false;
},
function(e) {
isPrivate = true;
}
);
} else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open('test');
db.onerror = function() {
return true;
};
} catch (e) {
isPrivate = true;
}
if (typeof isPrivate === 'undefined') {
retry(
function isDone() {
return db.readyState === 'done' ? true : false;
},
function next(isTimeout) {
if (!isTimeout) {
isPrivate = db.result ? false : true;
}
}
);
}
} else if (isIE10OrLater(window.navigator.userAgent)) {
isPrivate = false;
try {
if (!window.indexedDB) {
isPrivate = true;
}
} catch (e) {
isPrivate = true;
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
try {
window.localStorage.setItem('test', 1);
} catch (e) {
isPrivate = true;
}
if (typeof isPrivate === 'undefined') {
isPrivate = false;
window.localStorage.removeItem('test');
}
}
} catch (e) {
isPrivate = false;
}
retry(
function isDone() {
return typeof isPrivate !== 'undefined' ? true : false;
},
function next(isTimeout) {
callback(isPrivate);
}
);
}
var 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';
if (isMobile.Android()) {
osName = 'Android';
}
if (isMobile.BlackBerry()) {
osName = 'BlackBerry';
}
if (isMobile.iOS()) {
osName = 'iOS';
}
if (isMobile.Opera()) {
osName = 'Opera Mini';
}
if (isMobile.Windows()) {
osName = 'Windows';
}
return osName;
}
};
// via: http://jsfiddle.net/ChristianL/AVyND/
function detectDesktopOS() {