This repository has been archived by the owner on Apr 4, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
/
DataChannel.min.js
16 lines (10 loc) · 21.6 KB
/
DataChannel.min.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'use strict';
// Last time updated: 2017-07-29 4:31:53 PM UTC
// __________________
// DataChannel v1.0.0
// Open-Sourced: https://github.com/muaz-khan/DataChannel
// --------------------------------------------------
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// --------------------------------------------------
"use strict";!function(){function DataConnector(root,config){function newPrivateSocket(_config){function initPeer(offerSDP){"one-to-one"===root.direction&&window.isFirstConnectionOpened||(offerSDP?(peerConfig.offerSDP=offerSDP,peerConfig.onAnswerSDP=sendsdp):peerConfig.onOfferSDP=sendsdp,peer=new RTCPeerConnection(peerConfig))}function sendsdp(sdp){sdp=JSON.stringify(sdp);var part=parseInt(sdp.length/3),firstPart=sdp.slice(0,part),secondPart=sdp.slice(part,sdp.length-1),thirdPart="";sdp.length>part+part&&(secondPart=sdp.slice(part,part+part),thirdPart=sdp.slice(part+part,sdp.length)),socket.send({userToken:self.userToken,firstPart:firstPart}),socket.send({userToken:self.userToken,secondPart:secondPart}),socket.send({userToken:self.userToken,thirdPart:thirdPart})}function socketResponse(response){if(response.userToken!==self.userToken){if((response.firstPart||response.secondPart||response.thirdPart)&&(response.firstPart&&(_config.userid=response.userToken,inner.firstPart=response.firstPart,inner.secondPart&&inner.thirdPart&&selfInvoker()),response.secondPart&&(inner.secondPart=response.secondPart,inner.firstPart&&inner.thirdPart&&selfInvoker()),response.thirdPart&&(inner.thirdPart=response.thirdPart,inner.firstPart&&inner.secondPart&&selfInvoker())),response.candidate&&!gotstream&&peer){if(!inner.firstPart||!inner.secondPart||!inner.thirdPart)return setTimeout(function(){socketResponse(response)},400);peer.addICE({sdpMLineIndex:response.candidate.sdpMLineIndex,candidate:JSON.parse(response.candidate.candidate)}),console.debug("ice candidate",response.candidate.candidate)}response.left&&(peer&&peer.peer&&(peer.peer.close(),peer.peer=null),response.closeEntireSession?leaveChannels():socket&&(socket.send({left:!0,userToken:self.userToken}),socket=null),root.onleave(response.userToken)),response.playRoleOfBroadcaster&&setTimeout(function(){self.roomToken=response.roomToken,root.open(self.roomToken),self.sockets=swap(self.sockets)},600)}}function selfInvoker(){invokedOnce||(invokedOnce=!0,inner.sdp=JSON.parse(inner.firstPart+inner.secondPart+inner.thirdPart),isofferer?peer.addAnswerSDP(inner.sdp):initPeer(inner.sdp),console.debug("sdp",inner.sdp.sdp))}var socketConfig={channel:_config.channel,onmessage:socketResponse,onopen:function(){isofferer&&!peer&&initPeer(),_config.socketIndex=socket.index=self.sockets.length,self.socketObjects[socketConfig.channel]=socket,self.sockets[_config.socketIndex]=socket}};socketConfig.callback=function(_socket){socket=_socket,socketConfig.onopen()};var gotstream,peer,socket=root.openSignalingChannel(socketConfig),isofferer=_config.isofferer,inner={},peerConfig={onICE:function(candidate){if(!socket)return setTimeout(function(){peerConfig.onICE(candidate)},2e3);socket.send({userToken:self.userToken,candidate:{sdpMLineIndex:candidate.sdpMLineIndex,candidate:JSON.stringify(candidate.candidate)}})},onopen:function(channel){channel.peer=peer.peer,rtcDataChannels.push(channel),config.onopen(_config.userid,channel),"many-to-many"===root.direction&&isbroadcaster&&channels.split("--").length>3&&defaultSocket&&defaultSocket.send({newParticipant:socket.channel,userToken:self.userToken}),window.isFirstConnectionOpened=gotstream=!0},onmessage:function(event){config.onmessage(event.data,_config.userid)},onclose:config.onclose,onerror:root.onerror,preferSCTP:root.preferSCTP},invokedOnce=!1}function onNewParticipant(channel){if(channel&&-1===channels.indexOf(channel)&&channel!==self.userToken){channels+=channel+"--";var newChannel=uniqueToken();newPrivateSocket({channel:newChannel,closeSocket:!0}),defaultSocket&&defaultSocket.send({participant:!0,userToken:self.userToken,joinUser:channel,channel:newChannel})}}function uniqueToken(){return(Math.round(60535*Math.random())+5e6).toString()}function leaveChannels(channel){var socket,alert={left:!0,userToken:self.userToken};if(isbroadcaster&&(root.autoCloseEntireSession?alert.closeEntireSession=!0:self.sockets[0].send({playRoleOfBroadcaster:!0,userToken:self.userToken,roomToken:self.roomToken})),!channel){for(var sockets=self.sockets,length=sockets.length,i=0;i<length;i++)(socket=sockets[i])&&(socket.send(alert),self.socketObjects[socket.channel]&&delete self.socketObjects[socket.channel],delete sockets[i]);that.left=!0}channel&&(socket=self.socketObjects[channel])&&(socket.send(alert),self.sockets[socket.index]&&delete self.sockets[socket.index],delete self.socketObjects[channel]),self.sockets=swap(self.sockets)}var self={},that=this;self.userToken=(root.userid=root.userid||uniqueToken()).toString(),self.sockets=[],self.socketObjects={};var channels="--",isbroadcaster=!1,isGetNewRoom=!0,rtcDataChannels=[];window.addEventListener("beforeunload",function(){leaveChannels()},!1),window.addEventListener("keydown",function(e){116===e.keyCode&&leaveChannels()},!1);var defaultSocket=root.openSignalingChannel({onmessage:function(response){response.userToken!==self.userToken&&(isGetNewRoom&&response.roomToken&&response.broadcaster&&config.ondatachannel(response),response.newParticipant&&onNewParticipant(response.newParticipant),response.userToken&&response.joinUser===self.userToken&&response.participant&&-1===channels.indexOf(response.userToken)&&(channels+=response.userToken+"--",console.debug("Data connection is being opened between you and",response.userToken||response.channel),newPrivateSocket({isofferer:!0,channel:response.channel||response.userToken,closeSocket:!0})))},callback:function(socket){defaultSocket=socket}});return{createRoom:function(roomToken){self.roomToken=(roomToken||uniqueToken()).toString(),isbroadcaster=!0,isGetNewRoom=!1,function transmit(){defaultSocket&&defaultSocket.send({roomToken:self.roomToken,broadcaster:self.userToken}),root.transmitRoomOnce||that.leaving||("one-to-one"===root.direction?window.isFirstConnectionOpened||setTimeout(transmit,3e3):setTimeout(transmit,3e3))}()},joinRoom:function(_config){self.roomToken=_config.roomToken,isGetNewRoom=!1,newPrivateSocket({channel:self.userToken}),defaultSocket.send({participant:!0,userToken:self.userToken,joinUser:_config.joinUser})},send:function(message,_channel){var data,_channels=rtcDataChannels,length=_channels.length;if(length)if(data=JSON.stringify(message),_channel)"open"===_channel.readyState&&_channel.send(data);else for(var i=0;i<length;i++)"open"===_channels[i].readyState&&_channels[i].send(data)},leave:function(userid,autoCloseEntireSession){autoCloseEntireSession&&(root.autoCloseEntireSession=!0),leaveChannels(userid),userid||(self.joinedARoom=isbroadcaster=!1,isGetNewRoom=!0)}}}function getRandomString(){return(Math.random()*(new Date).getTime()).toString(36).replace(/\./g,"-")}function swap(arr){for(var swapped=[],length=arr.length,i=0;i<length;i++)arr[i]&&swapped.push(arr[i]);return swapped}function RTCPeerConnection(options){function onSdpError(e){var message=JSON.stringify(e,null,"\t");-1!==message.indexOf("RTP/SAVPF Expects at least 4 fields")&&(message="It seems that you are trying to interop RTP-datachannels with SCTP. It is not supported!"),console.error("onSdpError:",message)}function onSdpSuccess(){}function createOffer(){options.onOfferSDP&&peerConnection.createOffer(function(sessionDescription){peerConnection.setLocalDescription(sessionDescription),options.onOfferSDP(sessionDescription)},onSdpError,constraints)}function createAnswer(){options.onAnswerSDP&&(options.offerSDP=new SessionDescription(options.offerSDP),peerConnection.setRemoteDescription(options.offerSDP,onSdpSuccess,onSdpError),peerConnection.createAnswer(function(sessionDescription){peerConnection.setLocalDescription(sessionDescription),options.onAnswerSDP(sessionDescription)},onSdpError,constraints))}function _openOffererChannel(){var dataChannelDict={};console.debug("dataChannelDict",dataChannelDict),channel=peerConnection.createDataChannel("channel",dataChannelDict),setChannelEvents()}function setChannelEvents(){channel.onmessage=options.onmessage,channel.onopen=function(){options.onopen(channel)},channel.onclose=options.onclose,channel.onerror=options.onerror}function openAnswererChannel(){peerConnection.ondatachannel=function(event){channel=event.channel,setChannelEvents()},moz&&createAnswer()}var w=window,PeerConnection=w.mozRTCPeerConnection||w.webkitRTCPeerConnection,SessionDescription=w.mozRTCSessionDescription||w.RTCSessionDescription,IceCandidate=w.mozRTCIceCandidate||w.RTCIceCandidate,iceServers={iceServers:IceServersHandler.getIceServers()},optional={optional:[]};navigator.onLine||(iceServers=null,console.warn("No internet connection detected. No STUN/TURN server is used to make sure local/host candidates are used for peers connection."));var peerConnection=new PeerConnection(iceServers,optional);moz&&!options.onOfferSDP||(moz||options.onOfferSDP)&&(_openOffererChannel(),moz&&createOffer()),peerConnection.onicecandidate=function(event){event.candidate&&peerConnection&&options.onICE&&options.onICE(event.candidate)};var constraints=options.constraints||{optional:[],mandatory:{OfferToReceiveAudio:!1,OfferToReceiveVideo:!1}};moz||(createOffer(),createAnswer());var channel;return options.onAnswerSDP&&moz&&options.onmessage&&openAnswererChannel(),moz||options.onOfferSDP||openAnswererChannel(),{addAnswerSDP:function(sdp){sdp=new SessionDescription(sdp),peerConnection.setRemoteDescription(sdp,onSdpSuccess,onSdpError)},addICE:function(candidate){peerConnection.addIceCandidate(new IceCandidate({sdpMLineIndex:candidate.sdpMLineIndex,candidate:candidate.candidate}))},peer:peerConnection,channel:channel,sendData:function(message){channel&&channel.send(message)}}}function FileReceiver(root){var content={},packets={},numberOfPackets={};return{receive:function(data){var uuid=data.uuid;if(void 0!==data.packets&&(numberOfPackets[uuid]=packets[uuid]=parseInt(data.packets)),root.onFileProgress&&root.onFileProgress({remaining:packets[uuid]--,length:numberOfPackets[uuid],received:numberOfPackets[uuid]-packets[uuid],maxChunks:numberOfPackets[uuid],uuid:uuid,currentPosition:numberOfPackets[uuid]-packets[uuid]},uuid),content[uuid]||(content[uuid]=[]),content[uuid].push(data.message),data.last){var dataURL=content[uuid].join("");FileConverter.DataURLToBlob(dataURL,data.fileType,function(blob){blob.uuid=uuid,blob.name=data.name,blob.extra=data.extra||{},blob.url=(window.URL||window.webkitURL).createObjectURL(blob),root.autoSaveToDisk&&FileSaver.SaveToDisk(blob.url,data.name),root.onFileReceived&&root.onFileReceived(blob),delete content[uuid]})}}}}function SocketConnector(_channel,config){var socket=config.openSignalingChannel({channel:_channel,onopen:config.onopen,onmessage:config.onmessage,callback:function(_socket){socket=_socket}});return{send:function(message){socket&&socket.send({userid:userid,message:message})}}}function TextReceiver(){var content={};return{receive:function(data,onmessage,userid){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)),onmessage(message,userid,(new Date).getTime()-data.sendingTime),delete content[uuid]}}}}window.DataChannel=function(channel,extras){function prepareInit(callback){for(var extra in extras)self[extra]=extras[extra];if(self.direction=self.direction||"many-to-many",self.userid&&(window.userid=self.userid),self.openSignalingChannel)callback();else if(void 0===self.transmitRoomOnce&&(self.transmitRoomOnce=!0),self.openSignalingChannel=function(config){channel=(config=config||{}).channel||self.channel||"default-channel";var socket=new window.Firebase("https://"+(self.firebase||"webrtc-experiment")+".firebaseIO.com/"+channel);return socket.channel=channel,socket.on("child_added",function(data){config.onmessage(data.val())}),socket.send=function(data){this.push(data)},self.socket||(self.socket=socket),(channel!==self.channel||self.isInitiator&&channel===self.channel)&&socket.onDisconnect().remove(),config.onopen&&setTimeout(config.onopen,1),socket},window.Firebase)callback();else{var script=document.createElement("script");script.src="https://cdn.webrtc-experiment.com/firebase.js",script.onload=callback,document.documentElement.appendChild(script)}}function init(){self.config||(self.config={ondatachannel:function(room){if(dataConnector){var tempRoom={id:room.roomToken,owner:room.broadcaster};if(self.ondatachannel)return self.ondatachannel(tempRoom);self.joinedARoom||(self.joinedARoom=!0,self.join(tempRoom))}else self.room=room},onopen:function(userid,_channel){self.onopen(userid,_channel),self.channels[userid]={channel:_channel,send:function(data){self.send(data,this.channel)}}},onmessage:function(data,userid){if(IsDataChannelSupported&&!data.size&&(data=JSON.parse(data)),!IsDataChannelSupported){if(data.userid===window.userid)return;data=data.message}"text"===data.type?textReceiver.receive(data,self.onmessage,userid):void 0!==data.maxChunks?fileReceiver.receive(data,self):self.onmessage(data,userid)},onclose:function(event){var myChannels=self.channels,closedChannel=event.currentTarget;for(var userid in myChannels)closedChannel===myChannels[userid].channel&&delete myChannels[userid];self.onclose(event)},openSignalingChannel:self.openSignalingChannel},dataConnector=IsDataChannelSupported?new DataConnector(self,self.config):new SocketConnector(self.channel,self.config),fileReceiver=new FileReceiver(self),textReceiver=new TextReceiver(self),self.room&&self.config.ondatachannel(self.room))}channel&&(this.automatic=!0),this.channel=channel||location.href.replace(/\/|:|#|%|\.|\[|\]/g,""),extras=extras||{};var dataConnector,fileReceiver,textReceiver,self=this;this.onmessage=function(message,userid){console.debug(userid,"sent message:",message)},this.channels={},this.onopen=function(userid){console.debug(userid,"is connected with you.")},this.onclose=function(event){console.error("data channel closed:",event)},this.onerror=function(event){console.error("data channel error:",event)},this.autoSaveToDisk=!0,this.onFileReceived=function(fileName){console.debug("File <",fileName,"> received successfully.")},this.onFileSent=function(file){console.debug("File <",file.name,"> sent successfully.")},this.onFileProgress=function(packets){console.debug("<",packets.remaining,"> items remaining.")},this.open=function(_channel){self.joinedARoom=!0,self.socket?self.socket.onDisconnect().remove():self.isInitiator=!0,_channel&&(self.channel=_channel),prepareInit(function(){init(),IsDataChannelSupported&&dataConnector.createRoom(_channel)})},this.connect=function(_channel){_channel&&(self.channel=_channel),prepareInit(init)},this.join=function(room){if(!room.id||!room.owner)throw"Invalid room info passed.";dataConnector||init(),dataConnector.joinRoom&&dataConnector.joinRoom({roomToken:room.id,joinUser:room.owner})},this.send=function(data,_channel){if(!data)throw"No file, data or text message to share.";void 0===data.size||void 0===data.type?TextSender.send({text:data,channel:dataConnector,_channel:_channel,root:self}):FileSender.send({file:data,channel:dataConnector,onFileSent:function(file){self.onFileSent(file)},onFileProgress:function(packets,uuid){self.onFileProgress(packets,uuid)},_channel:_channel,root:self})},this.onleave=function(userid){console.debug(userid,"left!")},this.leave=this.eject=function(userid){dataConnector.leave(userid,self.autoCloseEntireSession)},this.openNewSession=function(isOpenNewSession,isNonFirebaseClient){if(isOpenNewSession){if(self.isNewSessionOpened)return;self.isNewSessionOpened=!0,self.joinedARoom||self.open()}isOpenNewSession&&!isNonFirebaseClient||self.connect(),isNonFirebaseClient&&setTimeout(function(){self.openNewSession(!0)},5e3)},void 0===this.preferSCTP&&(this.preferSCTP=!!(isFirefox||chromeVersion>=32)),void 0===this.chunkSize&&(this.chunkSize=isFirefox||chromeVersion>=32?13e3:1e3),void 0===this.chunkInterval&&(this.chunkInterval=isFirefox||chromeVersion>=32?100:500),self.automatic&&(window.Firebase?(console.debug("checking presence of the room.."),new window.Firebase("https://"+(extras.firebase||self.firebase||"muazkh")+".firebaseIO.com/"+self.channel).once("value",function(data){console.debug("room is present?",null!==data.val()),self.openNewSession(null===data.val())})):self.openNewSession(!1,!0))};var moz=!!navigator.mozGetUserMedia,IsDataChannelSupported=!(moz&&!navigator.mozGetUserMedia||!moz&&!navigator.webkitGetUserMedia),userid=getRandomString(),isMobileDevice=navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i),isChrome=!!navigator.webkitGetUserMedia,isFirefox=!!navigator.mozGetUserMedia,chromeVersion=50;isChrome&&(chromeVersion=navigator.mozGetUserMedia?0:parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]));var IceServersHandler=function(){function getSTUNObj(stunStr){var urlsParam="urls";"undefined"!=typeof isPluginRTC&&(urlsParam="url");var obj={};return obj[urlsParam]=stunStr,obj}function getTURNObj(turnStr,username,credential){var urlsParam="urls";"undefined"!=typeof isPluginRTC&&(urlsParam="url");var obj={username:username,credential:credential};return obj[urlsParam]=turnStr,obj}return{getIceServers:function(connection){var iceServers=[];return iceServers.push(getSTUNObj("stun:stun.l.google.com:19302")),iceServers.push(getTURNObj("stun:webrtcweb.com:7788","muazkh","muazkh")),iceServers.push(getTURNObj("turn:webrtcweb.com:7788","muazkh","muazkh")),iceServers.push(getTURNObj("turn:webrtcweb.com:8877","muazkh","muazkh")),iceServers.push(getTURNObj("turns:webrtcweb.com:7788","muazkh","muazkh")),iceServers.push(getTURNObj("turns:webrtcweb.com:8877","muazkh","muazkh")),iceServers.push(getTURNObj("stun:webrtcweb.com:4455","muazkh","muazkh")),iceServers.push(getTURNObj("turn:webrtcweb.com:4455","muazkh","muazkh")),iceServers.push(getTURNObj("turn:webrtcweb.com:5544?transport=tcp","muazkh","muazkh")),iceServers}}}(),FileConverter={DataURLToBlob:function(dataURL,fileType,callback){if(window.Worker&&!isMobileDevice){var webWorker=function(){var blob=URL.createObjectURL(new Blob(['function getBlob(_dataURL, _fileType) {var binary = atob(_dataURL.substr(_dataURL.indexOf(",") + 1)),i = binary.length,view = new Uint8Array(i);while (i--) {view[i] = binary.charCodeAt(i);};postMessage(new Blob([view], {type: _fileType}));};this.onmessage = function (e) {var data = JSON.parse(e.data); getBlob(data.dataURL, data.fileType);}'],{type:"application/javascript"})),worker=new Worker(blob);return URL.revokeObjectURL(blob),worker}();webWorker.onmessage=function(event){callback(event.data)},webWorker.postMessage(JSON.stringify({dataURL:dataURL,fileType:fileType}))}else{for(var binary=atob(dataURL.substr(dataURL.indexOf(",")+1)),i=binary.length,view=new Uint8Array(i);i--;)view[i]=binary.charCodeAt(i);callback(new Blob([view]))}}},FileSaver={SaveToDisk:function(fileUrl,fileName){var hyperlink=document.createElement("a");hyperlink.href=fileUrl,hyperlink.target="_blank",hyperlink.download=fileName||fileUrl;var mouseEvent=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0});hyperlink.dispatchEvent(mouseEvent),(window.URL||window.webkitURL).revokeObjectURL(hyperlink.href)}},FileSender={send:function(config){function onReadAsDataURL(dataURL,text){var data={type:"file",uuid:file.uuid,maxChunks:numberOfPackets,currentPosition:numberOfPackets-packets,name:file.name,fileType:file.type,size:file.size};dataURL&&(text=dataURL,numberOfPackets=packets=data.packets=parseInt(text.length/packetSize),file.maxChunks=data.maxChunks=numberOfPackets,data.currentPosition=numberOfPackets-packets,root.onFileSent&&root.onFileSent(file)),root.onFileProgress&&root.onFileProgress({remaining:packets--,length:numberOfPackets,sent:numberOfPackets-packets,maxChunks:numberOfPackets,uuid:file.uuid,currentPosition:numberOfPackets-packets},file.uuid),text.length>packetSize?data.message=text.slice(0,packetSize):(data.message=text,data.last=!0,data.name=file.name,file.url=URL.createObjectURL(file),root.onFileSent(file,file.uuid)),channel.send(data,privateChannel),(textToTransfer=text.slice(data.message.length)).length&&setTimeout(function(){onReadAsDataURL(null,textToTransfer)},root.chunkInterval||100)}var root=config.root,channel=config.channel,privateChannel=config._channel,file=config.file;if(config.file){var packetSize=15e3;root.chunkSize&&(packetSize=root.chunkSize);var textToTransfer="",numberOfPackets=0,packets=0;if(file.uuid=getRandomString(),window.Worker&&!isMobileDevice){var webWorker=function(){var blob=URL.createObjectURL(new Blob(["function readFile(_file) {postMessage(new FileReaderSync().readAsDataURL(_file));};this.onmessage = function (e) {readFile(e.data);}"],{type:"application/javascript"})),worker=new Worker(blob);return URL.revokeObjectURL(blob),worker}();webWorker.onmessage=function(event){onReadAsDataURL(event.data)},webWorker.postMessage(file)}else{var reader=new FileReader;reader.onload=function(e){onReadAsDataURL(e.target.result)},reader.readAsDataURL(file)}}else console.error("You must attach/select a file.")}},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,_channel),(textToTransfer=text.slice(data.message.length)).length&&setTimeout(function(){sendText(null,textToTransfer)},root.chunkInterval||100)}var root=config.root,channel=config.channel,_channel=config._channel,initialText=config.text,packetSize=root.chunkSize||1e3,textToTransfer="",isobject=!1;"string"!=typeof initialText&&(isobject=!0,initialText=JSON.stringify(initialText));var uuid=getRandomString(),sendingTime=(new Date).getTime();sendText(initialText)}}}();