-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathscript.js
More file actions
178 lines (162 loc) · 6.5 KB
/
script.js
File metadata and controls
178 lines (162 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
const peerConfig = {
'iceServers': [
{'urls': 'stun:stun.l.google.com:19302'},
{'urls': 'stun:stun.stunprotocol.org:3478'},
{'urls': 'stun:stun.sipnet.net:3478'},
{'urls': 'stun:stun.ideasip.com:3478'},
{'urls': 'stun:stun.iptel.org:3478'}
]
};
let peerConnection, signalChannel, msgChannel, localStream, signal, isPeer;
const remoteMessage = {sdp: null, ice: []};
const isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
const onGetUserMediaSuccess = (stream) => document.getElementById('selfVideo').srcObject = stream;
const errorHandler = (error) => console.error(error);
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
.then((stream) => {
localStream = stream;
onGetUserMediaSuccess(stream);
})
.catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API');
}
const signalMsgHandler = async event => {
signal = JSON.parse(event.data);
isPeer && peerConnection.addStream(localStream);
await setPeerDescription(signal);
await setPeerIceCandidates(signal);
}
const msgHandler = event => addChat(event.data, false);
const setUpPeerConnection = () => {
peerConnection = new RTCPeerConnection(peerConfig);
peerConnection.onicecandidate = (event) => {
if(event.candidate != null) remoteMessage.ice.push(event.candidate);
else {
// Completed the gathering of ICE candidate.
if(signalChannel){
if(signalChannel.readyState === 'open'){
signalChannel.send(JSON.stringify(remoteMessage));
return;
}
}
const signalHash = LZString.compressToBase64(JSON.stringify(remoteMessage));
if(isPeer){
document.getElementById('copyMessage').value = signalHash;
document.querySelector('#step1 .desc').innerHTML = "<i>Copy the below message and share it with your peer.</i>";
document.getElementById('step1').style.display = 'block';
}else{
document.getElementById('copyMessage').value = window.location.href+'#'+signalHash;
document.getElementById('step1').style.display = 'block';
document.getElementById('step2').style.display = 'block';
}
}
}
peerConnection.ontrack = (event) => {
document.getElementById('callerVideo').srcObject = event.streams[0];
document.getElementById('blanket').style.display = 'none';
};
peerConnection.ondatachannel = (event) => {
const channel = event.channel;
if(channel.label == 'signal'){
signalChannel = event.channel;
signalChannel.onmessage = signalMsgHandler;
}else if(channel.label == 'messages'){
msgChannel = event.channel;
msgChannel.onmessage = msgHandler;
}
};
}
const addStream = (stream) => peerConnection.addStream(stream);
const createOffer = async () => {
const description = await peerConnection.createOffer({offerToReceiveAudio: 1});
await setLocalDescription(description);
}
const createDataChannel = (name, msgHandler) => {
if(name === "signal"){
signalChannel = peerConnection.createDataChannel(name);
signalChannel.onmessage = msgHandler;
}else if (name === "messages"){
msgChannel = peerConnection.createDataChannel(name);
msgChannel.onmessage = msgHandler;
}
}
const sendMsgDataChannel = (channel, msg) => channel.send(msg);
const setLocalDescription = async (description) => {
await peerConnection.setLocalDescription(description);
remoteMessage.sdp = peerConnection.localDescription;
};
const setPeerDescription = async (signal) => {
await peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp));
// Only create answers in response to offers
if(signal.sdp.type == 'offer') {
const description = await peerConnection.createAnswer();
isSafari && setTimeout(() => peerConnection.onicecandidate({candidate: null}), 1000);
await setLocalDescription(description);
}
};
const setPeerIceCandidates = async (signal) => {
const addIceCandidatePromises = signal.ice.map(ice => peerConnection.addIceCandidate(new RTCIceCandidate(ice)));
await Promise.all(addIceCandidatePromises);
}
const onPeerSignal = async (signalMsg) => {
signal = JSON.parse(LZString.decompressFromBase64(signalMsg));
await setPeerDescription(signal);
await setPeerIceCandidates(signal);
peerConnection.addStream(localStream);
createDataChannel('messages', msgHandler);
await createOffer();
isSafari && setTimeout(() => peerConnection.onicecandidate({candidate: null}), 1000);
}
const startApp = async () => {
peerConnection || setUpPeerConnection();
if(!isPeer){
createDataChannel('signal', signalMsgHandler);
await createOffer();
isSafari && setTimeout(() => peerConnection.onicecandidate({candidate: null}), 1000);
}else{
await setPeerDescription(signal);
await setPeerIceCandidates(signal);
}
}
// if the location.hash is present, treat as peer
if(window.location.hash.length > 1){
signal = JSON.parse(LZString.decompressFromBase64(window.location.hash.substring(1)));
isPeer = true;
}
startApp();
const copyBtn = () => {
document.getElementById('copyMessage').select();
document.execCommand('copy');
const copyBtn = document.querySelector('#step1 button');
copyBtn.innerHTML = "Copied";
setTimeout(() => {
copyBtn.innerHTML = "Copy";
}, 3000);
}
const connectBtn = () => {
document.getElementById('pasteMessage').value && onPeerSignal(document.getElementById('pasteMessage').value);
}
const toggleChat = () => {
document.getElementById('chatWrap').style.display = document.getElementById('chatWrap').style.display == 'none' ? 'block' : 'none';
}
document.getElementById('newmsg').addEventListener('keydown', (event) => {
if (event.keyCode === 13) {
event.preventDefault();
const newChat = event.target.textContent.trim();
msgChannel.send(newChat);
addChat(newChat, true);
event.target.textContent = '';
}
});
const addChat = (msg, isMine) => {
const chat = document.createElement('div');
chat.className = 'chat ' +(isMine ? 'mine': '');
chat.innerHTML = msg;
document.getElementById('chats').appendChild(chat);
document.getElementById('chatWrap').style.display = 'block';
}