From 30372b8916e53cb3a16b6287952e838d3d6db506 Mon Sep 17 00:00:00 2001 From: Laurent Di Biase Date: Wed, 12 Aug 2020 17:51:30 +0200 Subject: [PATCH] Add files via upload --- ReadMe.txt | 48 + about.html | 90 + assets/listen-broadcast.js | 208 ++ assets/listen.js | 359 ++ assets/play.js | 498 +++ bundle.js | 485 +++ config.json | 17 + css/main.css | 334 ++ dev/FileBufferReader.js | 1236 +++++++ dev/MultiStreamsMixer.js | 571 +++ dev/MultiStreamsMixer.min.js | 13 + dist/README.md | 25 + dist/RTCMultiConnection.js | 5910 ++++++++++++++++++++++++++++++++ dist/RTCMultiConnection.min.js | 18 + home.html | 89 + imgs/favicon.ico | Bin 0 -> 128245 bytes imgs/logo.png | Bin 0 -> 43824 bytes imgs/menu-icon.png | Bin 0 -> 3518 bytes imgs/visual.png | Bin 0 -> 117459 bytes index.html | 69 + js/bootstrap.min.js | 7 + js/jquery-3.3.1.slim.min.js | 2 + js/jquery.min.js | 2 + listen.html | 108 + package-lock.json | 1492 ++++++++ package.json | 18 + play.html | 158 + server-original.js | 85 + server.js | 207 ++ 29 files changed, 12049 insertions(+) create mode 100644 ReadMe.txt create mode 100644 about.html create mode 100644 assets/listen-broadcast.js create mode 100644 assets/listen.js create mode 100644 assets/play.js create mode 100644 bundle.js create mode 100644 config.json create mode 100644 css/main.css create mode 100644 dev/FileBufferReader.js create mode 100644 dev/MultiStreamsMixer.js create mode 100644 dev/MultiStreamsMixer.min.js create mode 100644 dist/README.md create mode 100644 dist/RTCMultiConnection.js create mode 100644 dist/RTCMultiConnection.min.js create mode 100644 home.html create mode 100644 imgs/favicon.ico create mode 100644 imgs/logo.png create mode 100644 imgs/menu-icon.png create mode 100644 imgs/visual.png create mode 100644 index.html create mode 100644 js/bootstrap.min.js create mode 100644 js/jquery-3.3.1.slim.min.js create mode 100644 js/jquery.min.js create mode 100644 listen.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 play.html create mode 100644 server-original.js create mode 100644 server.js diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..5ca1a0a --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,48 @@ + + +EARTHLOOP PROJECT + +From Laurent Di Biase + +2020 + + +Collaboratif web platform for networked Music + + + +This project use WebRTC API from the RTCMulticonnection Library of Muaz Khan +https://github.com/muaz-khan. +The audio plugins come form Faust code and are embeded in a Web Audio API + + +//To launch the program file from terminal : + +node server.js + + +////////////////////////// + RTCMultiConnection +////////////////////////// + +from last Github repository + + +https://github.com/muaz-khan/RTCMultiConnection/blob/master/docs/how-to-use.md + + +/* + +The idea is to receive into the "listen" page, the opening connection data form the "Play" page at the moment of the opening session by a player. + + +*/ + + + + + + + + + diff --git a/about.html b/about.html new file mode 100644 index 0000000..16a6145 --- /dev/null +++ b/about.html @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + EarthLoop About + + + + + + + + + + + + + + + + + + + + + +
+

+ EarthLoop +

+

Connected People for Networked Music

+
+ + + +
+

+

Links:

+

+
+

+ +

+
+ +
+
© Laurent Di Biase - 2020
+
+ + \ No newline at end of file diff --git a/assets/listen-broadcast.js b/assets/listen-broadcast.js new file mode 100644 index 0000000..91b6021 --- /dev/null +++ b/assets/listen-broadcast.js @@ -0,0 +1,208 @@ +// ...................................................... +// .......................UI Code........................ +// ...................................................... + + +document.getElementById('join-room').onclick = function() { + //disableInputButtons(); + + connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: true, + OfferToReceiveVideo: false + }; + connection.join(roomid); +}; + +document.getElementById('leave-room').onclick = function() { + document.getElementById('join-room').disabled = false; + connection.getAllParticipants().forEach(function(participantId) { + connection.disconnectWith(participantId); + }); + // close the URL display + document.getElementById('room-urls').style.display = "none"; + // close socket.io connection + connection.closeSocket(); +}; + +// ...................................................... +// ..................RTCMultiConnection Code............. +// ...................................................... + + +var connection = new RTCMultiConnection(); + +connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/'; + + + +// keep room opened even if owner leaves +connection.autoCloseEntireSession = true; + +connection.session = { + audio: true, + video: false, + oneway: true +}; + +connection.mediaConstraints = { + audio: { + sampleRate: 48000, + channelCount: 2, + volume: 1.0, + echoCancellation:false, + autoGainControl:false, + noiseSuppression:false, + highPassFilter:false + }, + video: false +}; + +connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: false, + OfferToReceiveVideo: false +}; + +// https://www.rtcmulticonnection.org/docs/iceServers/ +// use your own TURN-server here! +connection.iceServers = [{ + 'urls': [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + 'stun:stun.l.google.com:19302?transport=udp', + ] +}]; + +connection.audiosContainer = document.getElementById('audios-container'); + +connection.onstream = function(event) { + var existing = document.getElementById(event.streamid); + if(existing && existing.parentNode) { + existing.parentNode.removeChild(existing); + } + + event.mediaElement.removeAttribute('src'); + event.mediaElement.removeAttribute('srcObject'); + event.mediaElement.muted = true; + event.mediaElement.volume = 0; + + var audio = document.createElement('audio'); + + if(event.type === 'local') { + audio.volume = 0; + try { + audio.setAttributeNode(document.createAttribute('muted')); + } catch (e) { + audio.setAttribute('muted', true); + } + } + audio.srcObject = event.stream; + + var mediaElement = getHTMLMediaElement(audio, { + title: event.userid, + showOnMouseEnter: false + }); + + connection.audiosContainer.appendChild(mediaElement); + + setTimeout(function() { + mediaElement.media.play(); + }, 5000); + + mediaElement.id = event.streamid; +}; + +connection.onstreamended = function(event) { + var mediaElement = document.getElementById(event.streamid); + if (mediaElement) { + mediaElement.parentNode.removeChild(mediaElement); + + if(event.userid === connection.sessionid && !connection.isInitiator) { + alert('Broadcast is ended. We will reload this page to clear the cache.'); + location.reload(); + } + } +}; + + +///*************Socket.io***************** +/*var socket = connection.getSocket(); + +socket.on('buttonEvent', roomid ); + +Storage(); + +*/ + +// ...................................................... +// ......................Handling Room-ID................ +// ...................................................... + +function showRoomURL(roomid) { + var roomHashURL = '#' + roomid; + var roomQueryStringURL = '?roomid=' + roomid; + + var html = '

Unique URL for your room:


'; + + html += 'Hash URL: ' + roomHashURL + ''; + html += '
'; + html += 'QueryString URL: ' + roomQueryStringURL + ''; + + var roomURLsDiv = document.getElementById('room-urls'); + roomURLsDiv.innerHTML = html; + + roomURLsDiv.style.display = 'block'; +} + +(function() { + var params = {}, + r = /([^&=]+)=?([^&]*)/g; + + function d(s) { + return decodeURIComponent(s.replace(/\+/g, ' ')); + } + var match, search = window.location.search; + while (match = r.exec(search.substring(1))) + params[d(match[1])] = d(match[2]); + window.params = params; +})(); + +var roomid = ''; +if (localStorage.getItem(connection.socketMessageEvent)) { + roomid = localStorage.getItem(connection.socketMessageEvent); +} else { + roomid = connection.token(); +} + +function Storage() { + localStorage.setItem(connection.socketMessageEvent, roomid); +}; + +var hashString = location.hash.replace('#', ''); +if (hashString.length && hashString.indexOf('comment-') == 0) { + hashString = ''; +} + +var roomid = params.roomid; +if (!roomid && hashString.length) { + roomid = hashString; +} + +if (roomid && roomid.length) { + localStorage.setItem(connection.socketMessageEvent, roomid); + + // auto-join-room + (function reCheckRoomPresence() { + connection.checkPresence(roomid, function(isRoomExist) { + if (isRoomExist) { + connection.join(roomid); + return; + } + + setTimeout(reCheckRoomPresence, 5000); + }); + })(); + +} + + diff --git a/assets/listen.js b/assets/listen.js new file mode 100644 index 0000000..90e291a --- /dev/null +++ b/assets/listen.js @@ -0,0 +1,359 @@ +let joinRoom = document.getElementById('join-room'); +let leaveRoom = document.getElementById('leave-room'); + +let roomID; +// ...................................................... +// .......................UI Code........................ +// ...................................................... + + + + + +leaveRoom.addEventListener('click',function() { + document.getElementById('join-room').disabled = false; + connection.getAllParticipants().forEach(function(participantId) { + connection.disconnectWith(participantId); + }); + // close the URL display + document.getElementById('room-urls').style.display = "none"; + // close socket.io connection + connection.closeSocket(); +}); + +// ...................................................... +// ..................RTCMultiConnection Code............. +// ...................................................... + +let connection = new RTCMultiConnection(); + +// recording is disabled because it is resulting for browser-crash +// if you enable below line, please also uncomment "RecordRTC.js" in html section. +var enableRecordings = false; + +//var connection = new RTCMultiConnection(); + +// https://www.rtcmulticonnection.org/docs/iceServers/ +// use your own TURN-server here! +connection.iceServers = [{ + 'urls': [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + 'stun:stun.l.google.com:19302?transport=udp', + ] +}]; + +// its mandatory in v3 +connection.enableScalableBroadcast = true; + +// each relaying-user should serve only 1 users +connection.maxRelayLimitPerUser = 1; + +// we don't need to keep room-opened +// scalable-broadcast.js will handle stuff itself. +connection.autoCloseEntireSession = true; + +// by default, socket.io server is assumed to be deployed on your own URL +//connection.socketURL = '/'; + +// comment-out below line if you do not have your own socket.io server +connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/'; + +connection.socketMessageEvent = 'scalable-media-broadcast-demo'; + +//document.getElementById('broadcast-id').value = connection.userid; + +// user need to connect server, so that others can reach him. +connection.connectSocket(function(socket) { + socket.on('logs', function(log) { + document.querySelector('h3').innerHTML = log.replace(//g, '___').replace(/----/g, '(').replace(/___/g, ')'); + }); + + // this event is emitted when a broadcast is already created. + socket.on('join-broadcaster', function(hintsToJoinBroadcast) { + console.log('join-broadcaster', hintsToJoinBroadcast); + + connection.session = hintsToJoinBroadcast.typeOfStreams; + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: !!connection.session.video, + OfferToReceiveAudio: !!connection.session.audio + }; + connection.broadcastId = hintsToJoinBroadcast.broadcastId; + connection.join(hintsToJoinBroadcast.userid); + }); + + socket.on('rejoin-broadcast', function(broadcastId) { + console.log('rejoin-broadcast', broadcastId); + + connection.attachStreams = []; + socket.emit('check-broadcast-presence', broadcastId, function(isBroadcastExists) { + if (!isBroadcastExists) { + // the first person (i.e. real-broadcaster) MUST set his user-id + connection.userid = broadcastId; + } + + socket.emit('join-broadcast', { + broadcastId: broadcastId, + userid: connection.userid, + typeOfStreams: connection.session + }); + }); + }); + + socket.on('broadcast-stopped', function(broadcastId) { + // alert('Broadcast has been stopped.'); + // location.reload(); + console.error('broadcast-stopped', broadcastId); + alert('This broadcast has been stopped.'); + }); +/* + // this event is emitted when a broadcast is absent. + socket.on('start-broadcasting', function(typeOfStreams) { + console.log('start-broadcasting', typeOfStreams); + + // host i.e. sender should always use this! + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: false, + OfferToReceiveAudio: false + }; + connection.session = typeOfStreams; + + // "open" method here will capture media-stream + // we can skip this function always; it is totally optional here. + // we can use "connection.getUserMediaHandler" instead + connection.open(connection.userid); + });*/ +}); + +window.onbeforeunload = function() { + // Firefox is ugly. + document.getElementById('join-room').disabled = false; +}; + +//var videoPreview = document.getElementById('video-preview'); +var audioMonitoring = document.getElementById('audio-monitoring'); + +connection.onstream = function(event) { + /*if (connection.isInitiator && event.type !== 'local') { + return; + } + + connection.isUpperUserLeft = false; + audioMonitoring.srcObject = event.stream; + audioMonitoring.play(); + + audioMonitoring.userid = event.userid; + + if (event.type === 'local') { + audioMonitoring.muted = true; + }*/ + + if (connection.isInitiator == false && event.type === 'remote') { + // he is merely relaying the media + connection.dontCaptureUserMedia = true; + connection.attachStreams = [event.stream]; + connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: false, + OfferToReceiveVideo: false + }; + + connection.getSocket(function(socket) { + socket.emit('can-relay-broadcast'); + + if (connection.DetectRTC.browser.name === 'Chrome') { + connection.getAllParticipants().forEach(function(p) { + if (p + '' != event.userid + '') { + var peer = connection.peers[p].peer; + peer.getLocalStreams().forEach(function(localStream) { + peer.removeStream(localStream); + }); + event.stream.getTracks().forEach(function(track) { + peer.addTrack(track, event.stream); + }); + connection.dontAttachStream = true; + connection.renegotiate(p); + connection.dontAttachStream = false; + } + }); + } + + if (connection.DetectRTC.browser.name === 'Firefox') { + // Firefox is NOT supporting removeStream method + // that's why using alternative hack. + // NOTE: Firefox seems unable to replace-tracks of the remote-media-stream + // need to ask all deeper nodes to rejoin + connection.getAllParticipants().forEach(function(p) { + if (p + '' != event.userid + '') { + connection.replaceTrack(event.stream, p); + } + }); + } + + // Firefox seems UN_ABLE to record remote MediaStream + // WebAudio solution merely records audio + // so recording is skipped for Firefox. + if (connection.DetectRTC.browser.name === 'Chrome') { + repeatedlyRecordStream(event.stream); + } + }); + } + + // to keep room-id in cache + localStorage.setItem(connection.socketMessageEvent, connection.sessionid); +}; + +// ask node.js server to look for a broadcast +// if broadcast is available, simply join it. i.e. "join-broadcaster" event should be emitted. +// if broadcast is absent, simply create it. i.e. "start-broadcasting" event should be fired. +connection.socket.on('clickedReceived', function() { + //disableInputButtons(); + connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: true, + OfferToReceiveVideo: false + }; + connection.join(roomID.value); + socket.emit('confirmation', function(){ + console.log('Click bien reçu !') + }) + + + var broadcastId = roomID.value; + if (broadcastId.replace(/^\s+|\s+$/g, '').length <= 0) { + alert('Please enter broadcast-id'); + document.getElementById('broadcast-id').focus(); + } + + document.getElementById('join-room').disabled = true; + + connection.extra.broadcastId = broadcastId; + + connection.session = { + audio: true, + video: false, + oneway: true + }; + + connection.mediaConstraints = { + audio: { + sampleRate: 48000, + channelCount: 2, + volume: 1.0, + echoCancellation:false, + autoGainControl:false, + noiseSuppression:false, + highPassFilter:false + }, + video: false + }; + + connection.getSocket(function(socket) { + socket.emit('check-broadcast-presence', broadcastId, function(isBroadcastExists) { + if (!isBroadcastExists) { + // the first person (i.e. real-broadcaster) MUST set his user-id + connection.userid = broadcastId; + } + + console.log('check-broadcast-presence', broadcastId, isBroadcastExists); + + socket.emit('join-broadcast', { + broadcastId: broadcastId, + userid: connection.userid, + typeOfStreams: connection.session + }); + }); + }); + +}); + +connection.onstreamended = function() {}; + +connection.onleave = function(event) { + if (event.userid !== audioMonitoring.userid) return; + + connection.getSocket(function(socket) { + socket.emit('can-not-relay-broadcast'); + + connection.isUpperUserLeft = true; + + if (allRecordedBlobs.length) { + // playing lats recorded blob + var lastBlob = allRecordedBlobs[allRecordedBlobs.length - 1]; + audioMonitoring.src = URL.createObjectURL(lastBlob); + audioMonitoring.play(); + allRecordedBlobs = []; + } else if (connection.currentRecorder) { + var recorder = connection.currentRecorder; + connection.currentRecorder = null; + recorder.stopRecording(function() { + if (!connection.isUpperUserLeft) return; + + audioMonitoring.src = URL.createObjectURL(recorder.getBlob()); + audioMonitoring.play(); + }); + } + + if (connection.currentRecorder) { + connection.currentRecorder.stopRecording(); + connection.currentRecorder = null; + } + }); +}; + +var allRecordedBlobs = []; + +function repeatedlyRecordStream(stream) { + if (!enableRecordings) { + return; + } + + connection.currentRecorder = RecordRTC(stream, { + type: 'audio' + }); + + connection.currentRecorder.startRecording(); + + setTimeout(function() { + if (connection.isUpperUserLeft || !connection.currentRecorder) { + return; + } + + connection.currentRecorder.stopRecording(function() { + allRecordedBlobs.push(connection.currentRecorder.getBlob()); + + if (connection.isUpperUserLeft) { + return; + } + + connection.currentRecorder = null; + repeatedlyRecordStream(stream); + }); + }, 30 * 1000); // 30-seconds +}; + +function disableInputButtons() { + document.getElementById('open-or-join').disabled = true; + document.getElementById('broadcast-id').disabled = true; +} + +// ...................................................... +// ......................Handling broadcast-id................ +// ...................................................... + +var broadcastId = ''; +if (localStorage.getItem(connection.socketMessageEvent)) { + broadcastId = localStorage.getItem(connection.socketMessageEvent); +} else { + broadcastId = connection.token(); +} +broadcastId.value = broadcastId; + +// below section detects how many users are viewing your broadcast + +connection.onNumberOfBroadcastViewersUpdated = function(event) { + if (!connection.isInitiator) return; + + document.getElementById('broadcast-viewers-counter').innerHTML = 'Number of broadcast viewers: ' + event.numberOfBroadcastViewers + ''; +}; + + diff --git a/assets/play.js b/assets/play.js new file mode 100644 index 0000000..7ad80de --- /dev/null +++ b/assets/play.js @@ -0,0 +1,498 @@ +/*This is the part of code give here: +https://github.com/muaz-khan/RTCMultiConnection/blob/master/docs/getting-started.md +for +"Getting Started from Scratch" + +and from this exemple with room ID and chat box: +https://jsfiddle.net/zd9Lsdfk/50/ +*/ + + +const timeDisplay = document.querySelector("p"); +const startButton = document.getElementById("open-room"); +const stopButton = document.getElementById("leave-room"); +//const sendButton = document.getElementById("send-message"); + +let localAudio = document.querySelector("#localAudio"); +let localContainer = document.getElementById("localContainer"); +let remoteContainer = document.getElementById("remoteContainer"); +let roomID = document.getElementById("room-id"); + +let socket; + +let connection = new RTCMultiConnection(); + + +// ...................................................... +// .......................UI Code........................ +// ...................................................... + +startButton.addEventListener('click', function() { + disableInputButtons(); + connection.openOrJoin(roomID.value, function(isRoomExist, roomid, socket) { + + if (isRoomExist === false && connection.isInitiator === true) { + // if room doesn't exist, it means that current user will create the room + showRoomURL(roomid); + } + + if(isRoomExist) { + connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: true, + OfferToReceiveVideo: false + }; + } + + showRoomURL(connection.sessionid); + + + connection.socket.emit('clickedSend', roomID); + + connection.socket.on('broadcast-stopped', function(broadcastId) { + // alert('Broadcast has been stopped.'); + // location.reload(); + console.error('broadcast-stopped', broadcastId); + alert('This broadcast has been stopped.'); + }); + + // this event is emitted when a broadcast is absent. + connection.socket.on('start-broadcasting', function(typeOfStreams) { + console.log('start-broadcasting', typeOfStreams); + + // host i.e. sender should always use this! + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: false, + OfferToReceiveAudio: false + }; + connection.session = typeOfStreams; + + // "open" method here will capture media-stream + // we can skip this function always; it is totally optional here. + // we can use "connection.getUserMediaHandler" instead + connection.open(connection.userid); + }); + }); +}); + + + +stopButton.addEventListener('click',function() { + startButton.disabled = false; + connection.getAllParticipants().forEach(function(participantId) { + connection.disconnectWith(participantId); + }); + // close the URL display + document.getElementById('room-urls').style.display = "none"; + // close socket.io connection + connection.closeSocket(); +}); + + + + +// ...................................................... +// .....................Socket.io........................ +// ...................................................... + + + +/* + +/// connection.socket: + +ex 1: +connection.open('roomid', function() { + connection.socket.emit('clicked', roomID); + +}); + +ex 2: + +connection.onstream = function(event) { + if(event.type === 'remote') { + connection.socket.emit('get-remote-user-extra-data', event.userid, function(extra) { + alert( extra.joinTime ); + }); + } +}: +/// + +///This method allows you set custom socket listener before starting or joining a room. + +connection.socketCustomEvent = 'abcdef'; +connection.openOrJoin('roomid', function() { + connection.socket.on(connection.socketCustomEvent, function(message) { + alert(message); + }); + + connection.socket.emit(connection.socketCustomEvent, 'My userid is: ' + connection.userid); +}); + + +//Pour récupérer le nom de chaque participant +//This method allows you set custom socket listeners anytime, during a live session. + +connection.setCustomSocketEvent('abcdef'); +connection.socket.on('abcdef', function(message) { + alert(message); +}); + +connection.socket.emit('abcdef', 'My userid is: ' + connection.userid); + +/////////////////RTCMutliconnection.js///////////// + + //Connexion automatique à l'ouverture de la page + connection.socket.on('connect', function() { + if (connection.enableLogs) { + console.info('socket.io connection is opened.'); + } + //fait quelques choses + }); + // A l'ouverture de la session envoie des infos + function openRoom(callback) { + if (connection.enableLogs) { + console.log('Sending open-room signal to socket.io'); + } + + connection.waitingForLocalMedia = false; + connection.socket.emit('open-room', { + sessionid: connection.sessionid, + session: connection.session, + mediaConstraints: connection.mediaConstraints, + sdpConstraints: connection.sdpConstraints, + streams: getStreamInfoForAdmin(), + extra: connection.extra, + identifier: connection.publicRoomIdentifier, + password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : '' + }, function(isRoomOpened, error) { + if (isRoomOpened === true) { + if (connection.enableLogs) { + console.log('isRoomOpened: ', isRoomOpened, ' roomid: ', connection.sessionid); + } + callback(isRoomOpened, connection.sessionid); + } + + if (isRoomOpened === false) { + if (connection.enableLogs) { + console.warn('isRoomOpened: ', error, ' roomid: ', connection.sessionid); + } + + callback(isRoomOpened, connection.sessionid, error); + } + }); + } +*/ + + +// ...................................................... +// ..................RTCMultiConnection Code............. +// ...................................................... + + +//WebRTC Supported Detection +if (connection.DetectRTC.isWebRTCSupported === false) { + alert('Please try a WebRTC compatible web browser e.g. Chrome, Firefox or Opera.'); +} + + +connection.enableFileSharing = true; // by default, it is "false". + +// keep room opened even if owner leaves +connection.autoCloseEntireSession = true; + +// this line is VERY_important +connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/'; + + +connection.onEntireSessionClosed = function(event) { + console.info('Entire session is closed: ', event.sessionid, event.extra); +}; + +connection.userid = 'unique-userid' || "User" + Math.ceil(Math.random() * 100); + +// all below lines are optional; however recommended. +connection.session = { + audio: true, + video: false, + data: true +}; + +// allow 6 users +connection.maxParticipantsAllowed = 7; + +connection.onRoomFull = function(roomid) { + alert('Room is full.'); +}; + +connection.mediaConstraints = { + audio: { + sampleRate: 48000, + channelCount: 2, + volume: 1.0, + echoCancellation:false, + autoGainControl:false, + noiseSuppression:false, + highPassFilter:false + }, + video: false +}; + +connection.sdpConstraints.mandatory = { + OfferToReceiveAudio: { + sampleRate: 48000, + channelCount: 2, + volume: 1.0, + echoCancellation:false, + autoGainControl:false, + noiseSuppression:false, + highPassFilter:false + }, + OfferToReceiveVideo: false +}; + +// https://www.rtcmulticonnection.org/docs/iceServers/ +// use your own TURN-server here! +connection.iceServers = [{ + 'urls': [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + 'stun:stun.l.google.com:19302?transport=udp', + ] +}]; + +connection.onmessage = appendDIV; +connection.filesContainer = document.getElementById('file-container'); + + +connection.onopen = function() { + document.getElementById('input-text-chat').disabled = false; + document.getElementById('share-file').disabled = false; + +}; + +connection.onclose = connection.onleave = function(event) { + alert(event.userid + ' leave the session'); +}; + +function disableInputButtons() { + roomID.onkeyup(); + startButton.disabled = true; + stopButton.disabled = false; + roomID.disabled = true; +} + + +connection.onstream = function(event) { + var audioElement = event.mediaElement; + //Create each element type into a particuliar container + if (event.type === 'local') { + localContainer.appendChild(audioElement); + } + if (event.type === 'remote') { + remoteContainer.appendChild(audioElement); + alert(event.userid + ' join the session'); + } +}; + +connection.onstreamended = function(event) { + var mediaElement = document.getElementById(event.streamid); + if (mediaElement) { + mediaElement.parentNode.removeChild(mediaElement); + } +}; + +// ...................................................... +// .................Select Input Part.................... +// ...................................................... +/* +connection.onMediaError = function(e) { + if (e.message === 'Concurrent mic process limit.') { + if (DetectRTC.audioInputDevices.length <= 1) { + alert('Please select external microphone. Check github issue number 483.'); + return; + } + + var secondaryMic = DetectRTC.audioInputDevices[1].deviceId; + connection.mediaConstraints.audio = { + deviceId: secondaryMic + }; + + connection.join(connection.sessionid); + } +}; +*/ +// ...................................................... +// .............Scalable Broadcast Part.................. +// ...................................................... + +/* +//document.getElementById('broadcast-id').value = connection.userid; + +// user need to connect server, so that others can reach him. +connection.socket.on(function(socket) { + + socket.on('broadcast-stopped', function(broadcastId) { + // alert('Broadcast has been stopped.'); + // location.reload(); + console.error('broadcast-stopped', broadcastId); + alert('This broadcast has been stopped.'); + }); + + // this event is emitted when a broadcast is absent. + socket.on('start-broadcasting', function(typeOfStreams) { + console.log('start-broadcasting', typeOfStreams); + + // host i.e. sender should always use this! + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: false, + OfferToReceiveAudio: false + }; + connection.session = typeOfStreams; + + // "open" method here will capture media-stream + // we can skip this function always; it is totally optional here. + // we can use "connection.getUserMediaHandler" instead + connection.open(connection.userid); + }); +}); + +window.onbeforeunload = function() { + // Firefox is ugly. + startButton.disabled = false; +}; + +/*document.getElementById('share-session').onclick = function(){ + this.disabled = true; + var allUserStreams = connection.getRemoteStreams(); + connection.dontCaptureUserMedia = false; + connection.mediaConstraints.audio = true; + connection.attachStreams = [allUserStreams]; + connection.addStream({ + audio: true, + oneway: true, + allUserStreams: true + }); +}; +*/ + +// ...................................................... +// .................MultiStreamsMixer.................... +// ...................................................... + +//// all remote users +//var allUserStreams = connection.getRemoteStreams(); + + +//connection.addStream(audioMixer.getMixedStream()); + +// ...................................................... +// ................FileSharing/TextChat Code............. +// ...................................................... + + +document.getElementById('share-file').onclick = function() { + var fileSelector = new FileSelector(); + fileSelector.selectSingleFile(function(file) { + connection.send(file); + }); +}; + +document.getElementById('input-text-chat').onkeyup = function send(e) { + if(e.keyCode != 13) return; + + // removing trailing/leading whitespace + this.value = this.value.replace(/^\s+|\s+$/g, ''); + + if (!this.value.length) return; + + connection.send(this.value); + appendDIV(this.value); + this.value = ''; +}; + +var chatContainer = document.querySelector('#conversation-panel'); +function appendDIV(event) { + var div = document.createElement('div'); + div.innerHTML = event.data || event ; + chatContainer.insertBefore(div, chatContainer.firstChild); + div.tabIndex = 0; + div.focus(); + + document.getElementById('input-text-chat').focus(); +} + + + +// ...................................................... +// ......................Handling Room-ID................ +// ...................................................... + +function showRoomURL(roomid) { + var roomHashURL = '#' + roomid; + + var html = '

Session Open

'; + + html += 'Share URL: ' + roomHashURL + ''; + + var roomURLsDiv = document.getElementById('room-urls'); + roomURLsDiv.innerHTML = html; + + roomURLsDiv.style.display = 'block'; +} + +(function() { + var params = {}, + r = /([^&=]+)=?([^&]*)/g; + + function d(s) { + return decodeURIComponent(s.replace(/\+/g, ' ')); + } + var match, search = window.location.search; + while (match = r.exec(search.substring(1))) + params[d(match[1])] = d(match[2]); + window.params = params; +})(); + +var roomid = ''; +if (localStorage.getItem(connection.socketMessageEvent)) { + roomid = localStorage.getItem(connection.socketMessageEvent); +} else { + roomid = connection.token(); +} +document.getElementById('room-id').value = roomid; +document.getElementById('room-id').onkeyup = function() { + localStorage.setItem(connection.socketMessageEvent, this.value); +}; + +var hashString = location.hash.replace('#', ''); +if(hashString.length && hashString.indexOf('comment-') == 0) { + hashString = ''; +} + +var roomid = params.roomid; +if(!roomid && hashString.length) { + roomid = hashString; +} + +if(roomid && roomid.length) { + document.getElementById('room-id').value = roomid; + localStorage.setItem(connection.socketMessageEvent, roomid); + + // auto-join-room + (function reCheckRoomPresence() { + connection.checkPresence(roomid, function(isRoomExists) { + if(isRoomExists) { + connection.join(roomid); + return; + } + + setTimeout(reCheckRoomPresence, 5000); + }); + })(); + + disableInputButtons(); +} + + + diff --git a/bundle.js b/bundle.js new file mode 100644 index 0000000..143f749 --- /dev/null +++ b/bundle.js @@ -0,0 +1,485 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i/g, '___').replace(/----/g, '(').replace(/___/g, ')'); + }); + + // this event is emitted when a broadcast is already created. + socket.on('join-broadcaster', function(hintsToJoinBroadcast) { + console.log('join-broadcaster', hintsToJoinBroadcast); + + connection.session = hintsToJoinBroadcast.typeOfStreams; + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: !!connection.session.video, + OfferToReceiveAudio: !!connection.session.audio + }; + connection.broadcastId = hintsToJoinBroadcast.broadcastId; + connection.join(hintsToJoinBroadcast.userid); + }); + + socket.on('rejoin-broadcast', function(broadcastId) { + console.log('rejoin-broadcast', broadcastId); + + connection.attachStreams = []; + socket.emit('check-broadcast-presence', broadcastId, function(isBroadcastExists) { + if (!isBroadcastExists) { + // the first person (i.e. real-broadcaster) MUST set his user-id + connection.userid = broadcastId; + } + + socket.emit('join-broadcast', { + broadcastId: broadcastId, + userid: connection.userid, + typeOfStreams: connection.session + }); + }); + }); + + socket.on('broadcast-stopped', function(broadcastId) { + // alert('Broadcast has been stopped.'); + // location.reload(); + console.error('broadcast-stopped', broadcastId); + alert('This broadcast has been stopped.'); + }); + + // this event is emitted when a broadcast is absent. + socket.on('start-broadcasting', function(typeOfStreams) { + console.log('start-broadcasting', typeOfStreams); + + // host i.e. sender should always use this! + connection.sdpConstraints.mandatory = { + OfferToReceiveVideo: false, + OfferToReceiveAudio: false + }; + connection.session = typeOfStreams; + + // "open" method here will capture media-stream + // we can skip this function always; it is totally optional here. + // we can use "connection.getUserMediaHandler" instead + connection.open(connection.userid); + }); +}); + +window.onbeforeunload = function() { + // Firefox is ugly. + startButton.disabled = false; +}; + +/*document.getElementById('share-session').onclick = function(){ + this.disabled = true; + var allUserStreams = connection.getRemoteStreams(); + connection.dontCaptureUserMedia = false; + connection.mediaConstraints.audio = true; + connection.attachStreams = [allUserStreams]; + connection.addStream({ + audio: true, + oneway: true, + allUserStreams: true + }); +}; +*/ + +// ...................................................... +// .................MultiStreamsMixer.................... +// ...................................................... + +//// all remote users +//var allUserStreams = connection.getRemoteStreams(); + + +//connection.addStream(audioMixer.getMixedStream()); + +// ...................................................... +// ................FileSharing/TextChat Code............. +// ...................................................... + + +document.getElementById('share-file').onclick = function() { + var fileSelector = new FileSelector(); + fileSelector.selectSingleFile(function(file) { + connection.send(file); + }); +}; + +document.getElementById('input-text-chat').onkeyup = function send(e) { + if(e.keyCode != 13) return; + + // removing trailing/leading whitespace + this.value = this.value.replace(/^\s+|\s+$/g, ''); + + if (!this.value.length) return; + + connection.send(this.value); + appendDIV(this.value); + this.value = ''; +}; + +var chatContainer = document.querySelector('#conversation-panel'); +function appendDIV(event) { + var div = document.createElement('div'); + div.innerHTML = event.data || event ; + chatContainer.insertBefore(div, chatContainer.firstChild); + div.tabIndex = 0; + div.focus(); + + document.getElementById('input-text-chat').focus(); +} + + + +// ...................................................... +// ......................Handling Room-ID................ +// ...................................................... + +function showRoomURL(roomid) { + var roomHashURL = '#' + roomid; + + var html = '

Session Open

'; + + html += 'Share URL: ' + roomHashURL + ''; + + var roomURLsDiv = document.getElementById('room-urls'); + roomURLsDiv.innerHTML = html; + + roomURLsDiv.style.display = 'block'; +} + +(function() { + var params = {}, + r = /([^&=]+)=?([^&]*)/g; + + function d(s) { + return decodeURIComponent(s.replace(/\+/g, ' ')); + } + var match, search = window.location.search; + while (match = r.exec(search.substring(1))) + params[d(match[1])] = d(match[2]); + window.params = params; +})(); + +var roomid = ''; +if (localStorage.getItem(connection.socketMessageEvent)) { + roomid = localStorage.getItem(connection.socketMessageEvent); +} else { + roomid = connection.token(); +} +document.getElementById('room-id').value = roomid; +document.getElementById('room-id').onkeyup = function() { + localStorage.setItem(connection.socketMessageEvent, this.value); +}; + +var hashString = location.hash.replace('#', ''); +if(hashString.length && hashString.indexOf('comment-') == 0) { + hashString = ''; +} + +var roomid = params.roomid; +if(!roomid && hashString.length) { + roomid = hashString; +} + +if(roomid && roomid.length) { + document.getElementById('room-id').value = roomid; + localStorage.setItem(connection.socketMessageEvent, roomid); + + // auto-join-room + (function reCheckRoomPresence() { + connection.checkPresence(roomid, function(isRoomExists) { + if(isRoomExists) { + connection.join(roomid); + return; + } + + setTimeout(reCheckRoomPresence, 5000); + }); + })(); + + disableInputButtons(); +} + + + + +},{}]},{},[1]); diff --git a/config.json b/config.json new file mode 100644 index 0000000..ebc14b5 --- /dev/null +++ b/config.json @@ -0,0 +1,17 @@ +{ + "socketURL": "/", + "dirPath": "", + "homePage": "index.html", + "socketMessageEvent": "RTCMultiConnection-Message", + "socketCustomEvent": "RTCMultiConnection-Custom-Message", + "port": "8080", + "enableLogs": "false", + "autoRebootServerOnFailure": "false", + "isUseHTTPs": "false", + "sslKey": "./fake-keys/privatekey.pem", + "sslCert": "./fake-keys/certificate.pem", + "sslCabundle": "", + "enableAdmin": "true", + "adminUserName": "username", + "adminPassword": "password" +} diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..3af1807 --- /dev/null +++ b/css/main.css @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + +* { + box-sizing: border-box; +} + +.logo img{ + width: 100%; + max-width: 60px; + height: auto; +} + + +.hidden { + display: none; +} + +.highlight { + background-color: #eee; + font-size: 1.2em; + margin: 0 0 30px 0; + padding: 0.2em 1.5em; +} + +.warning { + color: red; + font-weight: 400; +} + +@media screen and (min-width: 1000px) { + /* hack! to detect non-touch devices */ + div#links a { + line-height: 0.8em; + } +} +/* +body { + font-family: 'Roboto', sans-serif; + font-weight: 300; + margin: 0; + padding: 1em; + word-break: break-word; +} +*/ + +button { + background-color: #d84a38; + border: none; + border-radius: 2px; + color: white; + font-family: 'Arial', sans-serif; + font-size: 0.8em; + margin: 0 20px 1em 0; + padding: 0.5em 0.7em 0.6em 0.7em; + width: 96px; +} + +button:active { + background-color: #cf402f; +} + +button:hover { + background-color: #cf402f; +} + +button[disabled] { + color: #ccc; +} + +button[disabled]:hover { + background-color: #d84a38; +} + +canvas { + background-color: #ccc; + max-width: 100%; + width: 100%; +} + +code { + font-family: 'Roboto', sans-serif; + font-weight: 400; +} + +div#container { + margin: 0 auto 0 auto; + max-width: 60em; + padding: 1em 1.5em 1.3em 1.5em; +} + +div#links { + padding: 0.5em 0 0 0; +} + +h1 { + border-bottom: 1px solid #ccc; + font-family: 'Roboto', sans-serif; + font-weight: 500; + margin: 0 0 0.8em 0; + padding: 0 0 0.2em 0; +} + +h2 { + color: #444; + font-weight: 500; +} + +h3 { + border-top: 1px solid #eee; + color: #666; + font-weight: 500; + margin: 10px 0 10px 0; + white-space: nowrap; +} + +li { + margin: 0 0 0.4em 0; +} + +html { + /* avoid annoying page width change + when moving from the home page */ + overflow-y: scroll; +} + +img { + border: none; + max-width: 100%; +} + +input[type=radio] { + position: relative; + top: -1px; +} + +p { + color: #444; + font-weight: 300; +} + +p#data { + border-top: 1px dotted #666; + font-family: Courier New, monospace; + line-height: 1.3em; + max-height: 1000px; + overflow-y: auto; + padding: 1em 0 0 0; +} + +p.borderBelow { + border-bottom: 1px solid #aaa; + padding: 0 0 20px 0; +} + +section p:last-of-type { + margin: 0; +} + +section { + border-bottom: 1px solid #eee; + margin: 0 0 30px 0; + padding: 0 0 20px 0; +} + +section:last-of-type { + border-bottom: none; + padding: 0 0 1em 0; +} + +select { + margin: 0 1em 1em 0; + position: relative; + top: -1px; +} + +h1 span { + white-space: nowrap; +} + +a { + color: #6fa8dc; + font-weight: 300; + text-decoration: none; +} + +h1 a { + font-weight: 300; + margin: 0 10px 0 0; + white-space: nowrap; +} + +a:hover { + color: #3d85c6; + text-decoration: underline; +} + +a#viewSource { + display: block; + margin: 1.3em 0 0 0; + border-top: 1px solid #999; + padding: 1em 0 0 0; +} + +div#errorMsg p { + color: #F00; +} + +div#links a { + display: block; + line-height: 1.3em; + margin: 0 0 1.5em 0; +} + +div.outputSelector { + margin: -1.3em 0 2em 0; +} + +p.description { + margin: 0 0 0.5em 0; +} + +strong { + font-weight: 500; +} + +textarea { + resize: none; + font-family: 'Roboto', sans-serif; +} + +ul { + margin: 0 0 0.5em 0; +} + +@media screen and (max-width: 650px) { + .highlight { + font-size: 1em; + margin: 0 0 20px 0; + padding: 0.2em 1em; + } + + h1 { + font-size: 24px; + } +} + + +/* Header/logo Title */ +.header { + padding: 80px; + text-align: center; + background: #1abc9c; + color: white; +} + +/* Increase the font size of the heading */ +.header h1 { + font-size: 40px; +} + +/* Style the top navigation bar */ +.navbar { + overflow: hidden; + background-color: #f1f1f1; +} + +/* Style the navigation bar links */ +.navbar a { + float: left; + display: block; + color: black; + text-align: center; + padding: 14px 20px; + text-decoration: none; +} + +/* Right-aligned link */ +.navbar a.right { + float: right; +} + +/* Change color on hover */ +.navbar a:hover { + background-color: #ddd; + color: black; +} + +/* Column container */ +.row { + display: -ms-flexbox; /* IE10 */ + display: flex; + -ms-flex-wrap: wrap; /* IE10 */ + flex-wrap: wrap; +} + +/* Responsive layout - when the screen is less than 700px wide, make the two columns stack on top of each other instead of next to each other */ +@media screen and (max-width: 480px) { + .row { + flex-direction: column; + } +} + +@media screen and (max-width: 400px) { + .navbar a { + float: none; + width: 100%; + } +} + + +@media screen and (max-width: 550px) { + button:active { + background-color: darkRed; + } + + h1 { + font-size: 22px; + } +} + +@media screen and (max-width: 450px) { + h1 { + font-size: 20px; + } +} + + + diff --git a/dev/FileBufferReader.js b/dev/FileBufferReader.js new file mode 100644 index 0000000..d42945b --- /dev/null +++ b/dev/FileBufferReader.js @@ -0,0 +1,1236 @@ +// Last time updated: 2017-08-27 5:48:35 AM UTC + +// ________________ +// FileBufferReader + +// Open-Sourced: https://github.com/muaz-khan/FileBufferReader + +// -------------------------------------------------- +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// -------------------------------------------------- + +'use strict'; + +(function() { + + function FileBufferReader() { + var fbr = this; + var fbrHelper = new FileBufferReaderHelper(); + + fbr.chunks = {}; + fbr.users = {}; + + fbr.readAsArrayBuffer = function(file, callback, extra) { + var options = { + file: file, + earlyCallback: function(chunk) { + callback(fbrClone(chunk, { + currentPosition: -1 + })); + }, + extra: extra || { + userid: 0 + } + }; + + if (file.extra && Object.keys(file.extra).length) { + Object.keys(file.extra).forEach(function(key) { + options.extra[key] = file.extra[key]; + }); + } + + fbrHelper.readAsArrayBuffer(fbr, options); + }; + + fbr.getNextChunk = function(fileUUID, callback, userid) { + var currentPosition; + + if (typeof fileUUID.currentPosition !== 'undefined') { + currentPosition = fileUUID.currentPosition; + fileUUID = fileUUID.uuid; + } + + var allFileChunks = fbr.chunks[fileUUID]; + if (!allFileChunks) { + return; + } + + if (typeof userid !== 'undefined') { + if (!fbr.users[userid + '']) { + fbr.users[userid + ''] = { + fileUUID: fileUUID, + userid: userid, + currentPosition: -1 + }; + } + + if (typeof currentPosition !== 'undefined') { + fbr.users[userid + ''].currentPosition = currentPosition; + } + + fbr.users[userid + ''].currentPosition++; + currentPosition = fbr.users[userid + ''].currentPosition; + } else { + if (typeof currentPosition !== 'undefined') { + fbr.chunks[fileUUID].currentPosition = currentPosition; + } + + fbr.chunks[fileUUID].currentPosition++; + currentPosition = fbr.chunks[fileUUID].currentPosition; + } + + var nextChunk = allFileChunks[currentPosition]; + if (!nextChunk) { + delete fbr.chunks[fileUUID]; + fbr.convertToArrayBuffer({ + chunkMissing: true, + currentPosition: currentPosition, + uuid: fileUUID + }, callback); + return; + } + + nextChunk = fbrClone(nextChunk); + + if (typeof userid !== 'undefined') { + nextChunk.remoteUserId = userid + ''; + } + + if (!!nextChunk.start) { + fbr.onBegin(nextChunk); + } + + if (!!nextChunk.end) { + fbr.onEnd(nextChunk); + } + + fbr.onProgress(nextChunk); + + fbr.convertToArrayBuffer(nextChunk, function(buffer) { + if (nextChunk.currentPosition == nextChunk.maxChunks) { + callback(buffer, true); + return; + } + + callback(buffer, false); + }); + }; + + var fbReceiver = new FileBufferReceiver(fbr); + + fbr.addChunk = function(chunk, callback) { + if (!chunk) { + return; + } + + fbReceiver.receive(chunk, function(chunk) { + fbr.convertToArrayBuffer({ + readyForNextChunk: true, + currentPosition: chunk.currentPosition, + uuid: chunk.uuid + }, callback); + }); + }; + + fbr.chunkMissing = function(chunk) { + delete fbReceiver.chunks[chunk.uuid]; + delete fbReceiver.chunksWaiters[chunk.uuid]; + }; + + fbr.onBegin = function() {}; + fbr.onEnd = function() {}; + fbr.onProgress = function() {}; + + fbr.convertToObject = FileConverter.ConvertToObject; + fbr.convertToArrayBuffer = FileConverter.ConvertToArrayBuffer + + // for backward compatibility----it is redundant. + fbr.setMultipleUsers = function() {}; + + // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned + function fbrClone(from, to) { + if (from == null || typeof from != "object") return from; + if (from.constructor != Object && from.constructor != Array) return from; + if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || + from.constructor == String || from.constructor == Number || from.constructor == Boolean) + return new from.constructor(from); + + to = to || new from.constructor(); + + for (var name in from) { + to[name] = typeof to[name] == "undefined" ? fbrClone(from[name], null) : to[name]; + } + + return to; + } + } + + function FileBufferReaderHelper() { + var fbrHelper = this; + + function processInWebWorker(_function) { + var blob = URL.createObjectURL(new Blob([_function.toString(), + 'this.onmessage = function (e) {' + _function.name + '(e.data);}' + ], { + type: 'application/javascript' + })); + + var worker = new Worker(blob); + return worker; + } + + fbrHelper.readAsArrayBuffer = function(fbr, options) { + var earlyCallback = options.earlyCallback; + delete options.earlyCallback; + + function processChunk(chunk) { + if (!fbr.chunks[chunk.uuid]) { + fbr.chunks[chunk.uuid] = { + currentPosition: -1 + }; + } + + options.extra = options.extra || { + userid: 0 + }; + + chunk.userid = options.userid || options.extra.userid || 0; + chunk.extra = options.extra; + + fbr.chunks[chunk.uuid][chunk.currentPosition] = chunk; + + if (chunk.end && earlyCallback) { + earlyCallback(chunk.uuid); + earlyCallback = null; + } + + // for huge files + if ((chunk.maxChunks > 200 && chunk.currentPosition == 200) && earlyCallback) { + earlyCallback(chunk.uuid); + earlyCallback = null; + } + } + if (false && typeof Worker !== 'undefined') { + var webWorker = processInWebWorker(fileReaderWrapper); + + webWorker.onmessage = function(event) { + processChunk(event.data); + }; + + webWorker.postMessage(options); + } else { + fileReaderWrapper(options, processChunk); + } + }; + + function fileReaderWrapper(options, callback) { + callback = callback || function(chunk) { + postMessage(chunk); + }; + + var file = options.file; + if (!file.uuid) { + file.uuid = (Math.random() * 100).toString().replace(/\./g, ''); + } + + var chunkSize = options.chunkSize || 15 * 1000; + if (options.extra && options.extra.chunkSize) { + chunkSize = options.extra.chunkSize; + } + + var sliceId = 0; + var cacheSize = chunkSize; + + var chunksPerSlice = Math.floor(Math.min(100000000, cacheSize) / chunkSize); + var sliceSize = chunksPerSlice * chunkSize; + var maxChunks = Math.ceil(file.size / chunkSize); + + file.maxChunks = maxChunks; + + var numOfChunksInSlice; + var currentPosition = 0; + var hasEntireFile; + var chunks = []; + + callback({ + currentPosition: currentPosition, + uuid: file.uuid, + maxChunks: maxChunks, + size: file.size, + name: file.name, + type: file.type, + lastModifiedDate: (file.lastModifiedDate || new Date()).toString(), + start: true + }); + + var blob, reader = new FileReader(); + + reader.onloadend = function(evt) { + if (evt.target.readyState == FileReader.DONE) { + addChunks(file.name, evt.target.result, function() { + sliceId++; + if ((sliceId + 1) * sliceSize < file.size) { + blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize); + reader.readAsArrayBuffer(blob); + } else if (sliceId * sliceSize < file.size) { + blob = file.slice(sliceId * sliceSize, file.size); + reader.readAsArrayBuffer(blob); + } else { + file.url = URL.createObjectURL(file); + callback({ + currentPosition: currentPosition, + uuid: file.uuid, + maxChunks: maxChunks, + size: file.size, + name: file.name, + lastModifiedDate: (file.lastModifiedDate || new Date()).toString(), + url: URL.createObjectURL(file), + type: file.type, + end: true + }); + } + }); + } + }; + + currentPosition += 1; + + blob = file.slice(sliceId * sliceSize, (sliceId + 1) * sliceSize); + reader.readAsArrayBuffer(blob); + + function addChunks(fileName, binarySlice, addChunkCallback) { + numOfChunksInSlice = Math.ceil(binarySlice.byteLength / chunkSize); + for (var i = 0; i < numOfChunksInSlice; i++) { + var start = i * chunkSize; + chunks[currentPosition] = binarySlice.slice(start, Math.min(start + chunkSize, binarySlice.byteLength)); + + callback({ + uuid: file.uuid, + buffer: chunks[currentPosition], + currentPosition: currentPosition, + maxChunks: maxChunks, + + size: file.size, + name: file.name, + lastModifiedDate: (file.lastModifiedDate || new Date()).toString(), + type: file.type + }); + + currentPosition++; + } + + if (currentPosition == maxChunks) { + hasEntireFile = true; + } + + addChunkCallback(); + } + } + } + + function FileSelector() { + var selector = this; + + var noFileSelectedCallback = function() {}; + + selector.selectSingleFile = function(callback, failure) { + if (failure) { + noFileSelectedCallback = failure; + } + + selectFile(callback); + }; + selector.selectMultipleFiles = function(callback, failure) { + if (failure) { + noFileSelectedCallback = failure; + } + + selectFile(callback, true); + }; + selector.selectDirectory = function(callback, failure) { + if (failure) { + noFileSelectedCallback = failure; + } + + selectFile(callback, true, true); + }; + + selector.accept = '*.*'; + + function selectFile(callback, multiple, directory) { + callback = callback || function() {}; + + var file = document.createElement('input'); + file.type = 'file'; + + if (multiple) { + file.multiple = true; + } + + if (directory) { + file.webkitdirectory = true; + } + + file.accept = selector.accept; + + file.onclick = function() { + file.clickStarted = true; + }; + + document.body.onfocus = function() { + setTimeout(function() { + if (!file.clickStarted) return; + file.clickStarted = false; + + if (!file.value) { + noFileSelectedCallback(); + } + }, 500); + }; + + file.onchange = function() { + if (multiple) { + if (!file.files.length) { + console.error('No file selected.'); + return; + } + + var arr = []; + Array.from(file.files).forEach(function(file) { + file.url = file.webkitRelativePath; + arr.push(file); + }); + callback(arr); + return; + } + + if (!file.files[0]) { + console.error('No file selected.'); + return; + } + + callback(file.files[0]); + + file.parentNode.removeChild(file); + }; + file.style.display = 'none'; + (document.body || document.documentElement).appendChild(file); + fireClickEvent(file); + } + + function getValidFileName(fileName) { + if (!fileName) { + fileName = 'file' + (new Date).toISOString().replace(/:|\.|-/g, '') + } + + var a = fileName; + a = a.replace(/^.*[\\\/]([^\\\/]*)$/i, "$1"); + a = a.replace(/\s/g, "_"); + a = a.replace(/,/g, ''); + a = a.toLowerCase(); + return a; + } + + function fireClickEvent(element) { + if (typeof element.click === 'function') { + element.click(); + return; + } + + if (typeof element.change === 'function') { + element.change(); + return; + } + + if (typeof document.createEvent('Event') !== 'undefined') { + var event = document.createEvent('Event'); + + if (typeof event.initEvent === 'function' && typeof element.dispatchEvent === 'function') { + event.initEvent('click', true, true); + element.dispatchEvent(event); + return; + } + } + + var event = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: true + }); + + element.dispatchEvent(event); + } + } + + function FileBufferReceiver(fbr) { + var fbReceiver = this; + + fbReceiver.chunks = {}; + fbReceiver.chunksWaiters = {}; + + function receive(chunk, callback) { + if (!chunk.uuid) { + fbr.convertToObject(chunk, function(object) { + receive(object); + }); + return; + } + + if (chunk.start && !fbReceiver.chunks[chunk.uuid]) { + fbReceiver.chunks[chunk.uuid] = {}; + if (fbr.onBegin) fbr.onBegin(chunk); + } + + if (!chunk.end && chunk.buffer) { + fbReceiver.chunks[chunk.uuid][chunk.currentPosition] = chunk.buffer; + } + + if (chunk.end) { + var chunksObject = fbReceiver.chunks[chunk.uuid]; + var chunksArray = []; + Object.keys(chunksObject).forEach(function(item, idx) { + chunksArray.push(chunksObject[item]); + }); + + var blob = new Blob(chunksArray, { + type: chunk.type + }); + blob = merge(blob, chunk); + blob.url = URL.createObjectURL(blob); + blob.uuid = chunk.uuid; + + if (!blob.size) console.error('Something went wrong. Blob Size is 0.'); + + if (fbr.onEnd) fbr.onEnd(blob); + + // clear system memory + delete fbReceiver.chunks[chunk.uuid]; + delete fbReceiver.chunksWaiters[chunk.uuid]; + } + + if (chunk.buffer && fbr.onProgress) fbr.onProgress(chunk); + + if (!chunk.end) { + callback(chunk); + + fbReceiver.chunksWaiters[chunk.uuid] = function() { + function looper() { + if (!chunk.buffer) { + return; + } + + if (!fbReceiver.chunks[chunk.uuid]) { + return; + } + + if (chunk.currentPosition != chunk.maxChunks && !fbReceiver.chunks[chunk.uuid][chunk.currentPosition]) { + callback(chunk); + setTimeout(looper, 5000); + } + } + setTimeout(looper, 5000); + }; + + fbReceiver.chunksWaiters[chunk.uuid](); + } + } + + fbReceiver.receive = receive; + } + + var FileConverter = { + ConvertToArrayBuffer: function(object, callback) { + binarize.pack(object, function(dataView) { + callback(dataView.buffer); + }); + }, + ConvertToObject: function(buffer, callback) { + binarize.unpack(buffer, callback); + } + }; + + function merge(mergein, mergeto) { + if (!mergein) mergein = {}; + if (!mergeto) return mergein; + + for (var item in mergeto) { + try { + mergein[item] = mergeto[item]; + } catch (e) {} + } + return mergein; + } + + var debug = false; + + var BIG_ENDIAN = false, + LITTLE_ENDIAN = true, + TYPE_LENGTH = Uint8Array.BYTES_PER_ELEMENT, + LENGTH_LENGTH = Uint16Array.BYTES_PER_ELEMENT, + BYTES_LENGTH = Uint32Array.BYTES_PER_ELEMENT; + + var Types = { + NULL: 0, + UNDEFINED: 1, + STRING: 2, + NUMBER: 3, + BOOLEAN: 4, + ARRAY: 5, + OBJECT: 6, + INT8ARRAY: 7, + INT16ARRAY: 8, + INT32ARRAY: 9, + UINT8ARRAY: 10, + UINT16ARRAY: 11, + UINT32ARRAY: 12, + FLOAT32ARRAY: 13, + FLOAT64ARRAY: 14, + ARRAYBUFFER: 15, + BLOB: 16, + FILE: 16, + BUFFER: 17 // Special type for node.js + }; + + if (debug) { + var TypeNames = [ + 'NULL', + 'UNDEFINED', + 'STRING', + 'NUMBER', + 'BOOLEAN', + 'ARRAY', + 'OBJECT', + 'INT8ARRAY', + 'INT16ARRAY', + 'INT32ARRAY', + 'UINT8ARRAY', + 'UINT16ARRAY', + 'UINT32ARRAY', + 'FLOAT32ARRAY', + 'FLOAT64ARRAY', + 'ARRAYBUFFER', + 'BLOB', + 'BUFFER' + ]; + } + + var Length = [ + null, // Types.NULL + null, // Types.UNDEFINED + 'Uint16', // Types.STRING + 'Float64', // Types.NUMBER + 'Uint8', // Types.BOOLEAN + null, // Types.ARRAY + null, // Types.OBJECT + 'Int8', // Types.INT8ARRAY + 'Int16', // Types.INT16ARRAY + 'Int32', // Types.INT32ARRAY + 'Uint8', // Types.UINT8ARRAY + 'Uint16', // Types.UINT16ARRAY + 'Uint32', // Types.UINT32ARRAY + 'Float32', // Types.FLOAT32ARRAY + 'Float64', // Types.FLOAT64ARRAY + 'Uint8', // Types.ARRAYBUFFER + 'Uint8', // Types.BLOB, Types.FILE + 'Uint8' // Types.BUFFER + ]; + + var binary_dump = function(view, start, length) { + var table = [], + endianness = BIG_ENDIAN, + ROW_LENGTH = 40; + table[0] = []; + for (var i = 0; i < ROW_LENGTH; i++) { + table[0][i] = i < 10 ? '0' + i.toString(10) : i.toString(10); + } + for (i = 0; i < length; i++) { + var code = view.getUint8(start + i, endianness); + var index = ~~(i / ROW_LENGTH) + 1; + if (typeof table[index] === 'undefined') table[index] = []; + table[index][i % ROW_LENGTH] = code < 16 ? '0' + code.toString(16) : code.toString(16); + } + console.log('%c' + table[0].join(' '), 'font-weight: bold;'); + for (i = 1; i < table.length; i++) { + console.log(table[i].join(' ')); + } + }; + + var find_type = function(obj) { + var type = undefined; + + if (obj === undefined) { + type = Types.UNDEFINED; + + } else if (obj === null) { + type = Types.NULL; + + } else { + var const_name = obj.constructor.name; + var const_name_reflection = obj.constructor.toString().match(/\w+/g)[1]; + if (const_name !== undefined && Types[const_name.toUpperCase()] !== undefined) { + // return type by .constructor.name if possible + type = Types[const_name.toUpperCase()]; + + } else if (const_name_reflection !== undefined && Types[const_name_reflection.toUpperCase()] !== undefined) { + type = Types[const_name_reflection.toUpperCase()]; + + } else { + // Work around when constructor.name is not defined + switch (typeof obj) { + case 'string': + type = Types.STRING; + break; + + case 'number': + type = Types.NUMBER; + break; + + case 'boolean': + type = Types.BOOLEAN; + break; + + case 'object': + if (obj instanceof Array) { + type = Types.ARRAY; + + } else if (obj instanceof Int8Array) { + type = Types.INT8ARRAY; + + } else if (obj instanceof Int16Array) { + type = Types.INT16ARRAY; + + } else if (obj instanceof Int32Array) { + type = Types.INT32ARRAY; + + } else if (obj instanceof Uint8Array) { + type = Types.UINT8ARRAY; + + } else if (obj instanceof Uint16Array) { + type = Types.UINT16ARRAY; + + } else if (obj instanceof Uint32Array) { + type = Types.UINT32ARRAY; + + } else if (obj instanceof Float32Array) { + type = Types.FLOAT32ARRAY; + + } else if (obj instanceof Float64Array) { + type = Types.FLOAT64ARRAY; + + } else if (obj instanceof ArrayBuffer) { + type = Types.ARRAYBUFFER; + + } else if (obj instanceof Blob) { // including File + type = Types.BLOB; + + } else if (obj instanceof Buffer) { // node.js only + type = Types.BUFFER; + + } else if (obj instanceof Object) { + type = Types.OBJECT; + + } + break; + + default: + break; + } + } + } + return type; + }; + + var utf16_utf8 = function(string) { + return unescape(encodeURIComponent(string)); + }; + + var utf8_utf16 = function(bytes) { + return decodeURIComponent(escape(bytes)); + }; + + /** + * packs seriarized elements array into a packed ArrayBuffer + * @param {Array} serialized Serialized array of elements. + * @return {DataView} view of packed binary + */ + var pack = function(serialized) { + var cursor = 0, + i = 0, + j = 0, + endianness = BIG_ENDIAN; + + var ab = new ArrayBuffer(serialized[0].byte_length + serialized[0].header_size); + var view = new DataView(ab); + + for (i = 0; i < serialized.length; i++) { + var start = cursor, + header_size = serialized[i].header_size, + type = serialized[i].type, + length = serialized[i].length, + value = serialized[i].value, + byte_length = serialized[i].byte_length, + type_name = Length[type], + unit = type_name === null ? 0 : window[type_name + 'Array'].BYTES_PER_ELEMENT; + + // Set type + if (type === Types.BUFFER) { + // on node.js Blob is emulated using Buffer type + view.setUint8(cursor, Types.BLOB, endianness); + } else { + view.setUint8(cursor, type, endianness); + } + cursor += TYPE_LENGTH; + + if (debug) { + console.info('Packing', type, TypeNames[type]); + } + + // Set length if required + if (type === Types.ARRAY || type === Types.OBJECT) { + view.setUint16(cursor, length, endianness); + cursor += LENGTH_LENGTH; + + if (debug) { + console.info('Content Length', length); + } + } + + // Set byte length + view.setUint32(cursor, byte_length, endianness); + cursor += BYTES_LENGTH; + + if (debug) { + console.info('Header Size', header_size, 'bytes'); + console.info('Byte Length', byte_length, 'bytes'); + } + + switch (type) { + case Types.NULL: + case Types.UNDEFINED: + // NULL and UNDEFINED doesn't have any payload + break; + + case Types.STRING: + if (debug) { + console.info('Actual Content %c"' + value + '"', 'font-weight:bold;'); + } + for (j = 0; j < length; j++, cursor += unit) { + view.setUint16(cursor, value.charCodeAt(j), endianness); + } + break; + + case Types.NUMBER: + case Types.BOOLEAN: + if (debug) { + console.info('%c' + value.toString(), 'font-weight:bold;'); + } + view['set' + type_name](cursor, value, endianness); + cursor += unit; + break; + + case Types.INT8ARRAY: + case Types.INT16ARRAY: + case Types.INT32ARRAY: + case Types.UINT8ARRAY: + case Types.UINT16ARRAY: + case Types.UINT32ARRAY: + case Types.FLOAT32ARRAY: + case Types.FLOAT64ARRAY: + var _view = new Uint8Array(view.buffer, cursor, byte_length); + _view.set(new Uint8Array(value.buffer)); + cursor += byte_length; + break; + + case Types.ARRAYBUFFER: + case Types.BUFFER: + var _view = new Uint8Array(view.buffer, cursor, byte_length); + _view.set(new Uint8Array(value)); + cursor += byte_length; + break; + + case Types.BLOB: + case Types.ARRAY: + case Types.OBJECT: + break; + + default: + throw 'TypeError: Unexpected type found.'; + } + + if (debug) { + binary_dump(view, start, cursor - start); + } + } + + return view; + }; + + /** + * Unpack binary data into an object with value and cursor + * @param {DataView} view [description] + * @param {Number} cursor [description] + * @return {Object} + */ + var unpack = function(view, cursor) { + var i = 0, + endianness = BIG_ENDIAN, + start = cursor; + var type, length, byte_length, value, elem; + + // Retrieve "type" + type = view.getUint8(cursor, endianness); + cursor += TYPE_LENGTH; + + if (debug) { + console.info('Unpacking', type, TypeNames[type]); + } + + // Retrieve "length" + if (type === Types.ARRAY || type === Types.OBJECT) { + length = view.getUint16(cursor, endianness); + cursor += LENGTH_LENGTH; + + if (debug) { + console.info('Content Length', length); + } + } + + // Retrieve "byte_length" + byte_length = view.getUint32(cursor, endianness); + cursor += BYTES_LENGTH; + + if (debug) { + console.info('Byte Length', byte_length, 'bytes'); + } + + var type_name = Length[type]; + var unit = type_name === null ? 0 : window[type_name + 'Array'].BYTES_PER_ELEMENT; + + switch (type) { + case Types.NULL: + case Types.UNDEFINED: + if (debug) { + binary_dump(view, start, cursor - start); + } + // NULL and UNDEFINED doesn't have any octet + value = null; + break; + + case Types.STRING: + length = byte_length / unit; + var string = []; + for (i = 0; i < length; i++) { + var code = view.getUint16(cursor, endianness); + cursor += unit; + string.push(String.fromCharCode(code)); + } + value = string.join(''); + if (debug) { + console.info('Actual Content %c"' + value + '"', 'font-weight:bold;'); + binary_dump(view, start, cursor - start); + } + break; + + case Types.NUMBER: + value = view.getFloat64(cursor, endianness); + cursor += unit; + if (debug) { + console.info('Actual Content %c"' + value.toString() + '"', 'font-weight:bold;'); + binary_dump(view, start, cursor - start); + } + break; + + case Types.BOOLEAN: + value = view.getUint8(cursor, endianness) === 1 ? true : false; + cursor += unit; + if (debug) { + console.info('Actual Content %c"' + value.toString() + '"', 'font-weight:bold;'); + binary_dump(view, start, cursor - start); + } + break; + + case Types.INT8ARRAY: + case Types.INT16ARRAY: + case Types.INT32ARRAY: + case Types.UINT8ARRAY: + case Types.UINT16ARRAY: + case Types.UINT32ARRAY: + case Types.FLOAT32ARRAY: + case Types.FLOAT64ARRAY: + case Types.ARRAYBUFFER: + elem = view.buffer.slice(cursor, cursor + byte_length); + cursor += byte_length; + + // If ArrayBuffer + if (type === Types.ARRAYBUFFER) { + value = elem; + + // If other TypedArray + } else { + value = new window[type_name + 'Array'](elem); + } + + if (debug) { + binary_dump(view, start, cursor - start); + } + break; + + case Types.BLOB: + if (debug) { + binary_dump(view, start, cursor - start); + } + // If Blob is available (on browser) + if (window.Blob) { + var mime = unpack(view, cursor); + var buffer = unpack(view, mime.cursor); + cursor = buffer.cursor; + value = new Blob([buffer.value], { + type: mime.value + }); + } else { + // node.js implementation goes here + elem = view.buffer.slice(cursor, cursor + byte_length); + cursor += byte_length; + // node.js implementatino uses Buffer to help Blob + value = new Buffer(elem); + } + break; + + case Types.ARRAY: + if (debug) { + binary_dump(view, start, cursor - start); + } + value = []; + for (i = 0; i < length; i++) { + // Retrieve array element + elem = unpack(view, cursor); + cursor = elem.cursor; + value.push(elem.value); + } + break; + + case Types.OBJECT: + if (debug) { + binary_dump(view, start, cursor - start); + } + value = {}; + for (i = 0; i < length; i++) { + // Retrieve object key and value in sequence + var key = unpack(view, cursor); + var val = unpack(view, key.cursor); + cursor = val.cursor; + value[key.value] = val.value; + } + break; + + default: + throw 'TypeError: Type not supported.'; + } + return { + value: value, + cursor: cursor + }; + }; + + /** + * deferred function to process multiple serialization in order + * @param {array} array [description] + * @param {Function} callback [description] + * @return {void} no return value + */ + var deferredSerialize = function(array, callback) { + var length = array.length, + results = [], + count = 0, + byte_length = 0; + for (var i = 0; i < array.length; i++) { + (function(index) { + serialize(array[index], function(result) { + // store results in order + results[index] = result; + // count byte length + byte_length += result[0].header_size + result[0].byte_length; + // when all results are on table + if (++count === length) { + // finally concatenate all reuslts into a single array in order + var array = []; + for (var j = 0; j < results.length; j++) { + array = array.concat(results[j]); + } + callback(array, byte_length); + } + }); + })(i); + } + }; + + /** + * Serializes object and return byte_length + * @param {mixed} obj JavaScript object you want to serialize + * @return {Array} Serialized array object + */ + var serialize = function(obj, callback) { + var subarray = [], + unit = 1, + header_size = TYPE_LENGTH + BYTES_LENGTH, + type, byte_length = 0, + length = 0, + value = obj; + + type = find_type(obj); + + unit = Length[type] === undefined || Length[type] === null ? 0 : + window[Length[type] + 'Array'].BYTES_PER_ELEMENT; + + switch (type) { + case Types.UNDEFINED: + case Types.NULL: + break; + + case Types.NUMBER: + case Types.BOOLEAN: + byte_length = unit; + break; + + case Types.STRING: + length = obj.length; + byte_length += length * unit; + break; + + case Types.INT8ARRAY: + case Types.INT16ARRAY: + case Types.INT32ARRAY: + case Types.UINT8ARRAY: + case Types.UINT16ARRAY: + case Types.UINT32ARRAY: + case Types.FLOAT32ARRAY: + case Types.FLOAT64ARRAY: + length = obj.length; + byte_length += length * unit; + break; + + case Types.ARRAY: + deferredSerialize(obj, function(subarray, byte_length) { + callback([{ + type: type, + length: obj.length, + header_size: header_size + LENGTH_LENGTH, + byte_length: byte_length, + value: null + }].concat(subarray)); + }); + return; + + case Types.OBJECT: + var deferred = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + deferred.push(key); + deferred.push(obj[key]); + length++; + } + } + deferredSerialize(deferred, function(subarray, byte_length) { + callback([{ + type: type, + length: length, + header_size: header_size + LENGTH_LENGTH, + byte_length: byte_length, + value: null + }].concat(subarray)); + }); + return; + + case Types.ARRAYBUFFER: + byte_length += obj.byteLength; + break; + + case Types.BLOB: + var mime_type = obj.type; + var reader = new FileReader(); + reader.onload = function(e) { + deferredSerialize([mime_type, e.target.result], function(subarray, byte_length) { + callback([{ + type: type, + length: length, + header_size: header_size, + byte_length: byte_length, + value: null + }].concat(subarray)); + }); + }; + reader.onerror = function(e) { + throw 'FileReader Error: ' + e; + }; + reader.readAsArrayBuffer(obj); + return; + + case Types.BUFFER: + byte_length += obj.length; + break; + + default: + throw 'TypeError: Type "' + obj.constructor.name + '" not supported.'; + } + + callback([{ + type: type, + length: length, + header_size: header_size, + byte_length: byte_length, + value: value + }].concat(subarray)); + }; + + /** + * Deserialize binary and return JavaScript object + * @param ArrayBuffer buffer ArrayBuffer you want to deserialize + * @return mixed Retrieved JavaScript object + */ + var deserialize = function(buffer, callback) { + var view = buffer instanceof DataView ? buffer : new DataView(buffer); + var result = unpack(view, 0); + return result.value; + }; + + if (debug) { + window.Test = { + BIG_ENDIAN: BIG_ENDIAN, + LITTLE_ENDIAN: LITTLE_ENDIAN, + Types: Types, + pack: pack, + unpack: unpack, + serialize: serialize, + deserialize: deserialize + }; + } + + var binarize = { + pack: function(obj, callback) { + try { + if (debug) console.info('%cPacking Start', 'font-weight: bold; color: red;', obj); + serialize(obj, function(array) { + if (debug) console.info('Serialized Object', array); + callback(pack(array)); + }); + } catch (e) { + throw e; + } + }, + unpack: function(buffer, callback) { + try { + if (debug) console.info('%cUnpacking Start', 'font-weight: bold; color: red;', buffer); + var result = deserialize(buffer); + if (debug) console.info('Deserialized Object', result); + callback(result); + } catch (e) { + throw e; + } + } + }; + + window.FileConverter = FileConverter; + window.FileSelector = FileSelector; + window.FileBufferReader = FileBufferReader; +})(); diff --git a/dev/MultiStreamsMixer.js b/dev/MultiStreamsMixer.js new file mode 100644 index 0000000..5abb002 --- /dev/null +++ b/dev/MultiStreamsMixer.js @@ -0,0 +1,571 @@ +// Last time updated: 2019-06-24 6:39:09 PM UTC + +// ________________________ +// MultiStreamsMixer v1.2.3 + +// Open-Sourced: https://github.com/muaz-khan/MultiStreamsMixer + +// -------------------------------------------------- +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// -------------------------------------------------- + +function MultiStreamsMixer(arrayOfMediaStreams, elementClass) { + + var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45'; + + (function(that) { + if (typeof RecordRTC !== 'undefined') { + return; + } + + 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.log === 'undefined' || typeof global.console.error === 'undefined') { + global.console.error = global.console.log = global.console.log || function() { + console.log(arguments); + }; + } + + if (typeof document === 'undefined') { + /*global document:true */ + that.document = { + documentElement: { + appendChild: function() { + return ''; + } + } + }; + + document.createElement = document.captureStream = document.mozCaptureStream = function() { + var obj = { + getContext: function() { + return obj; + }, + play: function() {}, + pause: function() {}, + drawImage: function() {}, + toDataURL: function() { + return ''; + }, + style: {} + }; + return obj; + }; + + that.HTMLVideoElement = function() {}; + } + + 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 + }; + } + + 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); + + // requires: chrome://flags/#enable-experimental-web-platform-features + + elementClass = elementClass || 'multi-streams-mixer'; + + var videos = []; + var isStopDrawingFrames = false; + + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.style.opacity = 0; + canvas.style.position = 'absolute'; + canvas.style.zIndex = -1; + canvas.style.top = '-1000em'; + canvas.style.left = '-1000em'; + canvas.className = elementClass; + (document.body || document.documentElement).appendChild(canvas); + + this.disableLogs = false; + this.frameInterval = 10; + + this.width = 360; + this.height = 240; + + // use gain node to prevent echo + this.useGainNode = true; + + var self = this; + + // _____________________________ + // Cross-Browser-Declarations.js + + // WebAudio API representer + var AudioContext = window.AudioContext; + + if (typeof AudioContext === 'undefined') { + if (typeof webkitAudioContext !== 'undefined') { + /*global AudioContext:true */ + AudioContext = webkitAudioContext; + } + + if (typeof mozAudioContext !== 'undefined') { + /*global AudioContext:true */ + AudioContext = mozAudioContext; + } + } + + /*jshint -W079 */ + var URL = window.URL; + + if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') { + /*global URL:true */ + URL = webkitURL; + } + + if (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator? + if (typeof navigator.webkitGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.webkitGetUserMedia; + } + + if (typeof navigator.mozGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.mozGetUserMedia; + } + } + + var MediaStream = window.MediaStream; + + if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') { + MediaStream = webkitMediaStream; + } + + /*global MediaStream:true */ + if (typeof MediaStream !== 'undefined') { + // override "stop" method for all browsers + if (typeof MediaStream.prototype.stop === 'undefined') { + MediaStream.prototype.stop = function() { + this.getTracks().forEach(function(track) { + track.stop(); + }); + }; + } + } + + var Storage = {}; + + if (typeof AudioContext !== 'undefined') { + Storage.AudioContext = AudioContext; + } else if (typeof webkitAudioContext !== 'undefined') { + Storage.AudioContext = webkitAudioContext; + } + + function setSrcObject(stream, element) { + if ('srcObject' in element) { + element.srcObject = stream; + } else if ('mozSrcObject' in element) { + element.mozSrcObject = stream; + } else { + element.srcObject = stream; + } + } + + this.startDrawingFrames = function() { + drawVideosToCanvas(); + }; + + function drawVideosToCanvas() { + if (isStopDrawingFrames) { + return; + } + + var videosLength = videos.length; + + var fullcanvas = false; + var remaining = []; + videos.forEach(function(video) { + if (!video.stream) { + video.stream = {}; + } + + if (video.stream.fullcanvas) { + fullcanvas = video; + } else { + // todo: video.stream.active or video.stream.live to fix blank frames issues? + remaining.push(video); + } + }); + + if (fullcanvas) { + canvas.width = fullcanvas.stream.width; + canvas.height = fullcanvas.stream.height; + } else if (remaining.length) { + canvas.width = videosLength > 1 ? remaining[0].width * 2 : remaining[0].width; + + var height = 1; + if (videosLength === 3 || videosLength === 4) { + height = 2; + } + if (videosLength === 5 || videosLength === 6) { + height = 3; + } + if (videosLength === 7 || videosLength === 8) { + height = 4; + } + if (videosLength === 9 || videosLength === 10) { + height = 5; + } + canvas.height = remaining[0].height * height; + } else { + canvas.width = self.width || 360; + canvas.height = self.height || 240; + } + + if (fullcanvas && fullcanvas instanceof HTMLVideoElement) { + drawImage(fullcanvas); + } + + remaining.forEach(function(video, idx) { + drawImage(video, idx); + }); + + setTimeout(drawVideosToCanvas, self.frameInterval); + } + + function drawImage(video, idx) { + if (isStopDrawingFrames) { + return; + } + + var x = 0; + var y = 0; + var width = video.width; + var height = video.height; + + if (idx === 1) { + x = video.width; + } + + if (idx === 2) { + y = video.height; + } + + if (idx === 3) { + x = video.width; + y = video.height; + } + + if (idx === 4) { + y = video.height * 2; + } + + if (idx === 5) { + x = video.width; + y = video.height * 2; + } + + if (idx === 6) { + y = video.height * 3; + } + + if (idx === 7) { + x = video.width; + y = video.height * 3; + } + + if (typeof video.stream.left !== 'undefined') { + x = video.stream.left; + } + + if (typeof video.stream.top !== 'undefined') { + y = video.stream.top; + } + + if (typeof video.stream.width !== 'undefined') { + width = video.stream.width; + } + + if (typeof video.stream.height !== 'undefined') { + height = video.stream.height; + } + + context.drawImage(video, x, y, width, height); + + if (typeof video.stream.onRender === 'function') { + video.stream.onRender(context, x, y, width, height, idx); + } + } + + function getMixedStream() { + isStopDrawingFrames = false; + var mixedVideoStream = getMixedVideoStream(); + + var mixedAudioStream = getMixedAudioStream(); + if (mixedAudioStream) { + mixedAudioStream.getTracks().filter(function(t) { + return t.kind === 'audio'; + }).forEach(function(track) { + mixedVideoStream.addTrack(track); + }); + } + + var fullcanvas; + arrayOfMediaStreams.forEach(function(stream) { + if (stream.fullcanvas) { + fullcanvas = true; + } + }); + + // mixedVideoStream.prototype.appendStreams = appendStreams; + // mixedVideoStream.prototype.resetVideoStreams = resetVideoStreams; + // mixedVideoStream.prototype.clearRecordedData = clearRecordedData; + + return mixedVideoStream; + } + + function getMixedVideoStream() { + resetVideoStreams(); + + var capturedStream; + + if ('captureStream' in canvas) { + capturedStream = canvas.captureStream(); + } else if ('mozCaptureStream' in canvas) { + capturedStream = canvas.mozCaptureStream(); + } else if (!self.disableLogs) { + console.error('Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features'); + } + + var videoStream = new MediaStream(); + + capturedStream.getTracks().filter(function(t) { + return t.kind === 'video'; + }).forEach(function(track) { + videoStream.addTrack(track); + }); + + canvas.stream = videoStream; + + return videoStream; + } + + function getMixedAudioStream() { + // via: @pehrsons + if (!Storage.AudioContextConstructor) { + Storage.AudioContextConstructor = new Storage.AudioContext(); + } + + self.audioContext = Storage.AudioContextConstructor; + + self.audioSources = []; + + if (self.useGainNode === true) { + self.gainNode = self.audioContext.createGain(); + self.gainNode.connect(self.audioContext.destination); + self.gainNode.gain.value = 0; // don't hear self + } + + var audioTracksLength = 0; + arrayOfMediaStreams.forEach(function(stream) { + if (!stream.getTracks().filter(function(t) { + return t.kind === 'audio'; + }).length) { + return; + } + + audioTracksLength++; + + var audioSource = self.audioContext.createMediaStreamSource(stream); + + if (self.useGainNode === true) { + audioSource.connect(self.gainNode); + } + + self.audioSources.push(audioSource); + }); + + self.audioDestination = self.audioContext.createMediaStreamDestination(); + self.audioSources.forEach(function(audioSource) { + audioSource.connect(self.audioDestination); + }); + return self.audioDestination.stream; + } + + function getVideo(stream) { + var video = document.createElement('video'); + + setSrcObject(stream, video); + + video.className = elementClass; + + video.muted = true; + video.volume = 0; + + video.width = stream.width || self.width || 360; + video.height = stream.height || self.height || 240; + + video.play(); + + return video; + } + + this.appendStreams = function(streams) { + if (!streams) { + throw 'First parameter is required.'; + } + + if (!(streams instanceof Array)) { + streams = [streams]; + } + + streams.forEach(function(stream) { + arrayOfMediaStreams.push(stream); + + var newStream = new MediaStream(); + + if (stream.getTracks().filter(function(t) { + return t.kind === 'video'; + }).length) { + var video = getVideo(stream); + video.stream = stream; + videos.push(video); + + newStream.addTrack(stream.getTracks().filter(function(t) { + return t.kind === 'video'; + })[0]); + } + + if (stream.getTracks().filter(function(t) { + return t.kind === 'audio'; + }).length) { + var audioSource = self.audioContext.createMediaStreamSource(stream); + // self.audioDestination = self.audioContext.createMediaStreamDestination(); + audioSource.connect(self.audioDestination); + + newStream.addTrack(self.audioDestination.stream.getTracks().filter(function(t) { + return t.kind === 'audio'; + })[0]); + } + }); + }; + + this.releaseStreams = function() { + videos = []; + isStopDrawingFrames = true; + + if (self.gainNode) { + self.gainNode.disconnect(); + self.gainNode = null; + } + + if (self.audioSources.length) { + self.audioSources.forEach(function(source) { + source.disconnect(); + }); + self.audioSources = []; + } + + if (self.audioDestination) { + self.audioDestination.disconnect(); + self.audioDestination = null; + } + + if (self.audioContext) { + self.audioContext.close(); + } + + self.audioContext = null; + + context.clearRect(0, 0, canvas.width, canvas.height); + + if (canvas.stream) { + canvas.stream.stop(); + canvas.stream = null; + } + }; + + this.resetVideoStreams = function(streams) { + if (streams && !(streams instanceof Array)) { + streams = [streams]; + } + + resetVideoStreams(streams); + }; + + function resetVideoStreams(streams) { + videos = []; + streams = streams || arrayOfMediaStreams; + + // via: @adrian-ber + streams.forEach(function(stream) { + if (!stream.getTracks().filter(function(t) { + return t.kind === 'video'; + }).length) { + return; + } + + var video = getVideo(stream); + video.stream = stream; + videos.push(video); + }); + } + + // for debugging + this.name = 'MultiStreamsMixer'; + this.toString = function() { + return this.name; + }; + + this.getMixedStream = getMixedStream; + +} + +if (typeof RecordRTC === 'undefined') { + if (typeof module !== 'undefined' /* && !!module.exports*/ ) { + module.exports = MultiStreamsMixer; + } + + if (typeof define === 'function' && define.amd) { + define('MultiStreamsMixer', [], function() { + return MultiStreamsMixer; + }); + } +} diff --git a/dev/MultiStreamsMixer.min.js b/dev/MultiStreamsMixer.min.js new file mode 100644 index 0000000..3b795b8 --- /dev/null +++ b/dev/MultiStreamsMixer.min.js @@ -0,0 +1,13 @@ +// Last time updated: 2019-06-24 6:39:10 PM UTC + +// ________________________ +// MultiStreamsMixer v1.2.3 + +// Open-Sourced: https://github.com/muaz-khan/MultiStreamsMixer + +// -------------------------------------------------- +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// -------------------------------------------------- + +function MultiStreamsMixer(arrayOfMediaStreams,elementClass){function setSrcObject(stream,element){"srcObject"in element?element.srcObject=stream:"mozSrcObject"in element?element.mozSrcObject=stream:element.srcObject=stream}function drawVideosToCanvas(){if(!isStopDrawingFrames){var videosLength=videos.length,fullcanvas=!1,remaining=[];if(videos.forEach(function(video){video.stream||(video.stream={}),video.stream.fullcanvas?fullcanvas=video:remaining.push(video)}),fullcanvas)canvas.width=fullcanvas.stream.width,canvas.height=fullcanvas.stream.height;else if(remaining.length){canvas.width=videosLength>1?2*remaining[0].width:remaining[0].width;var height=1;3!==videosLength&&4!==videosLength||(height=2),5!==videosLength&&6!==videosLength||(height=3),7!==videosLength&&8!==videosLength||(height=4),9!==videosLength&&10!==videosLength||(height=5),canvas.height=remaining[0].height*height}else canvas.width=self.width||360,canvas.height=self.height||240;fullcanvas&&fullcanvas instanceof HTMLVideoElement&&drawImage(fullcanvas),remaining.forEach(function(video,idx){drawImage(video,idx)}),setTimeout(drawVideosToCanvas,self.frameInterval)}}function drawImage(video,idx){if(!isStopDrawingFrames){var x=0,y=0,width=video.width,height=video.height;1===idx&&(x=video.width),2===idx&&(y=video.height),3===idx&&(x=video.width,y=video.height),4===idx&&(y=2*video.height),5===idx&&(x=video.width,y=2*video.height),6===idx&&(y=3*video.height),7===idx&&(x=video.width,y=3*video.height),"undefined"!=typeof video.stream.left&&(x=video.stream.left),"undefined"!=typeof video.stream.top&&(y=video.stream.top),"undefined"!=typeof video.stream.width&&(width=video.stream.width),"undefined"!=typeof video.stream.height&&(height=video.stream.height),context.drawImage(video,x,y,width,height),"function"==typeof video.stream.onRender&&video.stream.onRender(context,x,y,width,height,idx)}}function getMixedStream(){isStopDrawingFrames=!1;var mixedVideoStream=getMixedVideoStream(),mixedAudioStream=getMixedAudioStream();mixedAudioStream&&mixedAudioStream.getTracks().filter(function(t){return"audio"===t.kind}).forEach(function(track){mixedVideoStream.addTrack(track)});var fullcanvas;return arrayOfMediaStreams.forEach(function(stream){stream.fullcanvas&&(fullcanvas=!0)}),mixedVideoStream}function getMixedVideoStream(){resetVideoStreams();var capturedStream;"captureStream"in canvas?capturedStream=canvas.captureStream():"mozCaptureStream"in canvas?capturedStream=canvas.mozCaptureStream():self.disableLogs||console.error("Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features");var videoStream=new MediaStream;return capturedStream.getTracks().filter(function(t){return"video"===t.kind}).forEach(function(track){videoStream.addTrack(track)}),canvas.stream=videoStream,videoStream}function getMixedAudioStream(){Storage.AudioContextConstructor||(Storage.AudioContextConstructor=new Storage.AudioContext),self.audioContext=Storage.AudioContextConstructor,self.audioSources=[],self.useGainNode===!0&&(self.gainNode=self.audioContext.createGain(),self.gainNode.connect(self.audioContext.destination),self.gainNode.gain.value=0);var audioTracksLength=0;return arrayOfMediaStreams.forEach(function(stream){if(stream.getTracks().filter(function(t){return"audio"===t.kind}).length){audioTracksLength++;var audioSource=self.audioContext.createMediaStreamSource(stream);self.useGainNode===!0&&audioSource.connect(self.gainNode),self.audioSources.push(audioSource)}}),self.audioDestination=self.audioContext.createMediaStreamDestination(),self.audioSources.forEach(function(audioSource){audioSource.connect(self.audioDestination)}),self.audioDestination.stream}function getVideo(stream){var video=document.createElement("video");return setSrcObject(stream,video),video.className=elementClass,video.muted=!0,video.volume=0,video.width=stream.width||self.width||360,video.height=stream.height||self.height||240,video.play(),video}function resetVideoStreams(streams){videos=[],streams=streams||arrayOfMediaStreams,streams.forEach(function(stream){if(stream.getTracks().filter(function(t){return"video"===t.kind}).length){var video=getVideo(stream);video.stream=stream,videos.push(video)}})}var browserFakeUserAgent="Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45";!function(that){"undefined"==typeof RecordRTC&&that&&"undefined"==typeof window&&"undefined"!=typeof global&&(global.navigator={userAgent:browserFakeUserAgent,getUserMedia:function(){}},global.console||(global.console={}),"undefined"!=typeof global.console.log&&"undefined"!=typeof global.console.error||(global.console.error=global.console.log=global.console.log||function(){console.log(arguments)}),"undefined"==typeof document&&(that.document={documentElement:{appendChild:function(){return""}}},document.createElement=document.captureStream=document.mozCaptureStream=function(){var obj={getContext:function(){return obj},play:function(){},pause:function(){},drawImage:function(){},toDataURL:function(){return""},style:{}};return obj},that.HTMLVideoElement=function(){}),"undefined"==typeof location&&(that.location={protocol:"file:",href:"",hash:""}),"undefined"==typeof screen&&(that.screen={width:0,height:0}),"undefined"==typeof URL&&(that.URL={createObjectURL:function(){return""},revokeObjectURL:function(){return""}}),that.window=global)}("undefined"!=typeof global?global:null),elementClass=elementClass||"multi-streams-mixer";var videos=[],isStopDrawingFrames=!1,canvas=document.createElement("canvas"),context=canvas.getContext("2d");canvas.style.opacity=0,canvas.style.position="absolute",canvas.style.zIndex=-1,canvas.style.top="-1000em",canvas.style.left="-1000em",canvas.className=elementClass,(document.body||document.documentElement).appendChild(canvas),this.disableLogs=!1,this.frameInterval=10,this.width=360,this.height=240,this.useGainNode=!0;var self=this,AudioContext=window.AudioContext;"undefined"==typeof AudioContext&&("undefined"!=typeof webkitAudioContext&&(AudioContext=webkitAudioContext),"undefined"!=typeof mozAudioContext&&(AudioContext=mozAudioContext));var URL=window.URL;"undefined"==typeof URL&&"undefined"!=typeof webkitURL&&(URL=webkitURL),"undefined"!=typeof navigator&&"undefined"==typeof navigator.getUserMedia&&("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia));var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"!=typeof MediaStream&&"undefined"==typeof MediaStream.prototype.stop&&(MediaStream.prototype.stop=function(){this.getTracks().forEach(function(track){track.stop()})});var Storage={};"undefined"!=typeof AudioContext?Storage.AudioContext=AudioContext:"undefined"!=typeof webkitAudioContext&&(Storage.AudioContext=webkitAudioContext),this.startDrawingFrames=function(){drawVideosToCanvas()},this.appendStreams=function(streams){if(!streams)throw"First parameter is required.";streams instanceof Array||(streams=[streams]),streams.forEach(function(stream){arrayOfMediaStreams.push(stream);var newStream=new MediaStream;if(stream.getTracks().filter(function(t){return"video"===t.kind}).length){var video=getVideo(stream);video.stream=stream,videos.push(video),newStream.addTrack(stream.getTracks().filter(function(t){return"video"===t.kind})[0])}if(stream.getTracks().filter(function(t){return"audio"===t.kind}).length){var audioSource=self.audioContext.createMediaStreamSource(stream);audioSource.connect(self.audioDestination),newStream.addTrack(self.audioDestination.stream.getTracks().filter(function(t){return"audio"===t.kind})[0])}})},this.releaseStreams=function(){videos=[],isStopDrawingFrames=!0,self.gainNode&&(self.gainNode.disconnect(),self.gainNode=null),self.audioSources.length&&(self.audioSources.forEach(function(source){source.disconnect()}),self.audioSources=[]),self.audioDestination&&(self.audioDestination.disconnect(),self.audioDestination=null),self.audioContext&&self.audioContext.close(),self.audioContext=null,context.clearRect(0,0,canvas.width,canvas.height),canvas.stream&&(canvas.stream.stop(),canvas.stream=null)},this.resetVideoStreams=function(streams){!streams||streams instanceof Array||(streams=[streams]),resetVideoStreams(streams)},this.name="MultiStreamsMixer",this.toString=function(){return this.name},this.getMixedStream=getMixedStream}"undefined"==typeof RecordRTC&&("undefined"!=typeof module&&(module.exports=MultiStreamsMixer),"function"==typeof define&&define.amd&&define("MultiStreamsMixer",[],function(){return MultiStreamsMixer})); \ No newline at end of file diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..0a226a6 --- /dev/null +++ b/dist/README.md @@ -0,0 +1,25 @@ +## Link Script Files + +```html + + + + + + + + + + +``` + +If you're sharing files, you also need to link: + +```html + + + + +``` + +> You can link multiple files from `dev` directory. Order doesn't matters. diff --git a/dist/RTCMultiConnection.js b/dist/RTCMultiConnection.js new file mode 100644 index 0000000..31e88c8 --- /dev/null +++ b/dist/RTCMultiConnection.js @@ -0,0 +1,5910 @@ +'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 RTCMultiConnection = 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:9001/'; + throw '"socketURL" MUST end with a slash.'; + } + + if (connection.enableLogs) { + if (connection.socketURL == '/') { + console.info('socket.io url is: ', location.origin + '/'); + } else { + console.info('socket.io url is: ', connection.socketURL); + } + } + + try { + connection.socket = io(connection.socketURL + parameters); + } catch (e) { + connection.socket = io.connect(connection.socketURL + parameters, connection.socketOptions); + } + + 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') { + stream.stream[action](type); + } + 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() { + var unknown = '-'; + + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; + + var os = unknown; + var 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)/ + }]; + for (var i = 0, cs; cs = clientStrings[i]; i++) { + if (cs.r.test(nAgt)) { + os = cs.s; + break; + } + } + + var osVersion = unknown; + + if (/Windows/.test(os)) { + if (/Windows (.*)/.test(os)) { + osVersion = /Windows (.*)/.exec(os)[1]; + } + os = 'Windows'; + } + + switch (os) { + case 'Mac OS X': + if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) { + osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'Android': + if (/Android ([\.\_\d]+)/.test(nAgt)) { + osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'iOS': + if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) { + osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); + osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); + } + break; + } + + return { + osName: os, + osVersion: osVersion + }; + } + + var osName = 'Unknown OS'; + var osVersion = 'Unknown OS Version'; + + function getAndroidVersion(ua) { + ua = (ua || navigator.userAgent).toLowerCase(); + var match = ua.match(/android\s([0-9\.]*)/); + return match ? match[1] : false; + } + + var osInfo = detectDesktopOS(); + + if (osInfo && osInfo.osName && osInfo.osName != '-') { + osName = osInfo.osName; + osVersion = osInfo.osVersion; + } else if (isMobile.any()) { + osName = isMobile.getOsName(); + + if (osName == 'Android') { + osVersion = getAndroidVersion(); + } + } + + var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node; + + if (osName === 'Unknown OS' && isNodejs) { + osName = 'Nodejs'; + osVersion = process.versions.node.toString().replace('v', ''); + } + + var isCanvasSupportsStreamCapturing = false; + var isVideoSupportsStreamCapturing = false; + ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) { + if (typeof document === 'undefined' || typeof document.createElement !== 'function') { + return; + } + + if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) { + isCanvasSupportsStreamCapturing = true; + } + + if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) { + isVideoSupportsStreamCapturing = true; + } + }); + + var regexIpv4Local = /^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/, + regexIpv4 = /([0-9]{1,3}(\.[0-9]{1,3}){3})/, + regexIpv6 = /[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}/; + + // via: https://github.com/diafygi/webrtc-ips + function DetectLocalIPAddress(callback, stream) { + if (!DetectRTC.isWebRTCSupported) { + return; + } + + var isPublic = true, + isIpv4 = true; + getIPs(function(ip) { + if (!ip) { + callback(); // Pass nothing to tell that ICE-gathering-ended + } else if (ip.match(regexIpv4Local)) { + isPublic = false; + callback('Local: ' + ip, isPublic, isIpv4); + } else if (ip.match(regexIpv6)) { //via https://ourcodeworld.com/articles/read/257/how-to-get-the-client-ip-address-with-javascript-only + isIpv4 = false; + callback('Public: ' + ip, isPublic, isIpv4); + } else { + callback('Public: ' + ip, isPublic, isIpv4); + } + }, stream); + } + + function getIPs(callback, stream) { + if (typeof document === 'undefined' || typeof document.getElementById !== 'function') { + return; + } + + var ipDuplicates = {}; + + var 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) { + return; + } + + var peerConfig = null; + + if (DetectRTC.browser === 'Chrome' && DetectRTC.browser.version < 58) { + // todo: add support for older Opera + peerConfig = { + optional: [{ + RtpDataChannels: true + }] + }; + } + + var servers = { + iceServers: [{ + urls: 'stun:stun.l.google.com:19302' + }] + }; + + var pc = new RTCPeerConnection(servers, peerConfig); + + if (stream) { + if (pc.addStream) { + pc.addStream(stream); + } else if (pc.addTrack && stream.getTracks()[0]) { + pc.addTrack(stream.getTracks()[0], stream); + } + } + + function handleCandidate(candidate) { + if (!candidate) { + callback(); // Pass nothing to tell that ICE-gathering-ended + return; + } + + var match = regexIpv4.exec(candidate); + if (!match) { + return; + } + var ipAddress = match[1]; + var isPublic = (candidate.match(regexIpv4Local)), + isIpv4 = true; + + if (ipDuplicates[ipAddress] === undefined) { + callback(ipAddress, isPublic, isIpv4); + } + + ipDuplicates[ipAddress] = true; + } + + // listen for candidate events + pc.onicecandidate = function(event) { + if (event.candidate && event.candidate.candidate) { + handleCandidate(event.candidate.candidate); + } else { + handleCandidate(); // Pass nothing to tell that ICE-gathering-ended + } + }; + + // create data channel + if (!stream) { + try { + pc.createDataChannel('sctp', {}); + } catch (e) {} + } + + // create an offer sdp + if (DetectRTC.isPromisesSupported) { + pc.createOffer().then(function(result) { + pc.setLocalDescription(result).then(afterCreateOffer); + }); + } else { + pc.createOffer(function(result) { + pc.setLocalDescription(result, afterCreateOffer, function() {}); + }, function() {}); + } + + function afterCreateOffer() { + var lines = pc.localDescription.sdp.split('\n'); + + lines.forEach(function(line) { + if (line && line.indexOf('a=candidate:') === 0) { + handleCandidate(line); + } + }); + } + } + + var MediaDevices = []; + + var audioInputDevices = []; + var audioOutputDevices = []; + var videoInputDevices = []; + + if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { + // Firefox 38+ seems having support of enumerateDevices + // Thanks @xdumaine/enumerateDevices + navigator.enumerateDevices = function(callback) { + var enumerateDevices = navigator.mediaDevices.enumerateDevices(); + if (enumerateDevices && enumerateDevices.then) { + navigator.mediaDevices.enumerateDevices().then(callback).catch(function() { + callback([]); + }); + } else { + callback([]); + } + }; + } + + // Media Devices detection + var canEnumerate = false; + + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) { + canEnumerate = true; + } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) { + canEnumerate = true; + } + + var hasMicrophone = false; + var hasSpeakers = false; + var hasWebcam = false; + + var isWebsiteHasMicrophonePermissions = false; + var isWebsiteHasWebcamPermissions = false; + + // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices + function checkDeviceSupport(callback) { + if (!canEnumerate) { + if (callback) { + callback(); + } + return; + } + + if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) { + navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack); + } + + if (!navigator.enumerateDevices && navigator.enumerateDevices) { + navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator); + } + + if (!navigator.enumerateDevices) { + if (callback) { + callback(); + } + return; + } + + MediaDevices = []; + + audioInputDevices = []; + audioOutputDevices = []; + videoInputDevices = []; + + hasMicrophone = false; + hasSpeakers = false; + hasWebcam = false; + + isWebsiteHasMicrophonePermissions = false; + isWebsiteHasWebcamPermissions = false; + + // to prevent duplication + var alreadyUsedDevices = {}; + + navigator.enumerateDevices(function(devices) { + devices.forEach(function(_device) { + var device = {}; + for (var d in _device) { + try { + if (typeof _device[d] !== 'function') { + device[d] = _device[d]; + } + } catch (e) {} + } + + if (alreadyUsedDevices[device.deviceId + device.label + device.kind]) { + return; + } + + // if it is MediaStreamTrack.getSources + if (device.kind === 'audio') { + device.kind = 'audioinput'; + } + + if (device.kind === 'video') { + device.kind = 'videoinput'; + } + + if (!device.deviceId) { + device.deviceId = device.id; + } + + if (!device.id) { + device.id = device.deviceId; + } + + if (!device.label) { + device.isCustomLabel = true; + + if (device.kind === 'videoinput') { + device.label = 'Camera ' + (videoInputDevices.length + 1); + } else if (device.kind === 'audioinput') { + device.label = 'Microphone ' + (audioInputDevices.length + 1); + } else if (device.kind === 'audiooutput') { + device.label = 'Speaker ' + (audioOutputDevices.length + 1); + } else { + device.label = 'Please invoke getUserMedia once.'; + } + + if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) { + if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) { + device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; + } + } + } else { + // Firefox on Android still returns empty label + if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { + isWebsiteHasWebcamPermissions = true; + } + + if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) { + isWebsiteHasMicrophonePermissions = true; + } + } + + if (device.kind === 'audioinput') { + hasMicrophone = true; + + if (audioInputDevices.indexOf(device) === -1) { + audioInputDevices.push(device); + } + } + + if (device.kind === 'audiooutput') { + hasSpeakers = true; + + if (audioOutputDevices.indexOf(device) === -1) { + audioOutputDevices.push(device); + } + } + + if (device.kind === 'videoinput') { + hasWebcam = true; + + if (videoInputDevices.indexOf(device) === -1) { + videoInputDevices.push(device); + } + } + + // there is no 'videoouput' in the spec. + MediaDevices.push(device); + + alreadyUsedDevices[device.deviceId + device.label + device.kind] = device; + }); + + if (typeof DetectRTC !== 'undefined') { + // to sync latest outputs + 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; + } + + if (callback) { + callback(); + } + }); + } + + var DetectRTC = window.DetectRTC || {}; + + // ---------- + // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion + DetectRTC.browser = getBrowserInfo(); + + detectPrivateMode(function(isPrivateBrowsing) { + DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing; + }); + + // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge + DetectRTC.browser['is' + DetectRTC.browser.name] = true; + + // ----------- + DetectRTC.osName = osName; + DetectRTC.osVersion = osVersion; + + var isNodeWebkit = typeof process === 'object' && typeof process.versions === 'object' && process.versions['node-webkit']; + + // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1. + var isWebRTCSupported = false; + ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) { + if (isWebRTCSupported) { + return; + } + + if (item in window) { + isWebRTCSupported = true; + } + }); + DetectRTC.isWebRTCSupported = isWebRTCSupported; + + //------- + DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'; + + // --------- Detect if system supports screen capturing API + var isScreenCapturingSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) { + isScreenCapturingSupported = true; + } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) { + isScreenCapturingSupported = true; + } else if (DetectRTC.browser.isEdge && DetectRTC.browser.version >= 17) { + isScreenCapturingSupported = true; // navigator.getDisplayMedia + } else if (DetectRTC.osName === 'Android' && DetectRTC.browser.isChrome) { + isScreenCapturingSupported = true; + } + + if (!/^(https:|chrome-extension:)$/g.test(location.protocol || '')) { + var isNonLocalHost = typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1; + if (isNonLocalHost && (DetectRTC.browser.isChrome || DetectRTC.browser.isEdge || DetectRTC.browser.isOpera)) { + isScreenCapturingSupported = false; + } else if (DetectRTC.browser.isFirefox) { + isScreenCapturingSupported = false; + } + } + DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported; + + // --------- Detect if WebAudio API are supported + var webAudio = { + isSupported: false, + isCreateMediaStreamSourceSupported: false + }; + + ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) { + if (webAudio.isSupported) { + return; + } + + if (item in window) { + webAudio.isSupported = true; + + if (window[item] && 'createMediaStreamSource' in window[item].prototype) { + webAudio.isCreateMediaStreamSourceSupported = true; + } + } + }); + DetectRTC.isAudioContextSupported = webAudio.isSupported; + DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported; + + // ---------- Detect if SCTP/RTP channels are supported. + + var isRtpDataChannelsSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) { + isRtpDataChannelsSupported = true; + } + DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported; + + var isSCTPSupportd = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) { + isSCTPSupportd = true; + } + DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd; + + // --------- + + DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js" + + // ------ + var isGetUserMediaSupported = false; + if (navigator.getUserMedia) { + isGetUserMediaSupported = true; + } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + isGetUserMediaSupported = true; + } + + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) { + if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) { + isGetUserMediaSupported = 'Requires HTTPs'; + } + } + + if (DetectRTC.osName === 'Nodejs') { + isGetUserMediaSupported = false; + } + DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported; + + var displayResolution = ''; + if (screen.width) { + var width = (screen.width) ? screen.width : ''; + var height = (screen.height) ? screen.height : ''; + displayResolution += '' + width + ' x ' + height; + } + DetectRTC.displayResolution = displayResolution; + + function getAspectRatio(w, h) { + function gcd(a, b) { + return (b == 0) ? a : gcd(b, a % b); + } + var r = gcd(w, h); + return (w / r) / (h / r); + } + + DetectRTC.displayAspectRatio = getAspectRatio(screen.width, screen.height).toFixed(2); + + // ---------- + DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing; + DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing; + + if (DetectRTC.browser.name == 'Chrome' && DetectRTC.browser.version >= 53) { + if (!DetectRTC.isCanvasSupportsStreamCapturing) { + DetectRTC.isCanvasSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features'; + } + + if (!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; + + if (DetectRTC.osName === 'Nodejs') { + DetectRTC.isWebSocketsSupported = true; + DetectRTC.isWebSocketsBlocked = false; + } + + DetectRTC.checkWebSocketsSupport = function(callback) { + callback = callback || function() {}; + try { + var starttime; + var websocket = new WebSocket('wss://echo.websocket.org:443/'); + websocket.onopen = function() { + DetectRTC.isWebSocketsBlocked = false; + 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 = true; + callback(); + }; + } catch (e) { + DetectRTC.isWebSocketsBlocked = true; + callback(); + } + }; + + // ------- + DetectRTC.load = function(callback) { + callback = callback || function() {}; + checkDeviceSupport(callback); + }; + + // check for microphone/camera support! + if (typeof checkDeviceSupport === 'function') { + // checkDeviceSupport(); + } + + if (typeof MediaDevices !== 'undefined') { + DetectRTC.MediaDevices = MediaDevices; + } else { + 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 = false; + if (typeof document !== 'undefined' && typeof document.createElement === 'function' && 'setSinkId' in document.createElement('video')) { + isSetSinkIdSupported = true; + } + DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported; + + // ----- + var isRTPSenderReplaceTracksSupported = false; + if (DetectRTC.browser.isFirefox && typeof mozRTCPeerConnection !== 'undefined' /*&& DetectRTC.browser.version > 39*/ ) { + /*global mozRTCPeerConnection:true */ + if ('getSenders' in mozRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') { + /*global webkitRTCPeerConnection:true */ + if ('getSenders' in webkitRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } + DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported; + + //------ + var isRemoteStreamProcessingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) { + isRemoteStreamProcessingSupported = true; + } + DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported; + + //------- + var isApplyConstraintsSupported = false; + + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) { + isApplyConstraintsSupported = true; + } + DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported; + + //------- + var isMultiMonitorScreenCapturingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) { + // version 43 merely supports platforms for multi-monitors + // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing. + isMultiMonitorScreenCapturingSupported = true; + } + DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported; + + DetectRTC.isPromisesSupported = !!('Promise' in window); + + // version is generated by "grunt" + DetectRTC.version = '1.3.9'; + + if (typeof DetectRTC === 'undefined') { + window.DetectRTC = {}; + } + + var MediaStream = window.MediaStream; + + if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') { + MediaStream = webkitMediaStream; + } + + if (typeof MediaStream !== 'undefined' && typeof MediaStream === 'function') { + DetectRTC.MediaStream = Object.keys(MediaStream.prototype); + } else DetectRTC.MediaStream = false; + + if (typeof MediaStreamTrack !== 'undefined') { + DetectRTC.MediaStreamTrack = Object.keys(MediaStreamTrack.prototype); + } else DetectRTC.MediaStreamTrack = false; + + var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; + + if (typeof RTCPeerConnection !== 'undefined') { + DetectRTC.RTCPeerConnection = Object.keys(RTCPeerConnection.prototype); + } else DetectRTC.RTCPeerConnection = false; + + window.DetectRTC = DetectRTC; + + if (typeof module !== 'undefined' /* && !!module.exports*/ ) { + module.exports = DetectRTC; + } + + if (typeof define === 'function' && define.amd) { + define('DetectRTC', [], function() { + return DetectRTC; + }); + } + })(); + + // globals.js + + if (typeof cordova !== 'undefined') { + DetectRTC.isMobileDevice = true; + DetectRTC.browser.name = 'Chrome'; + } + + if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { + DetectRTC.isMobileDevice = true; + DetectRTC.browser.name = 'Chrome'; + } + + function fireEvent(obj, eventName, args) { + if (typeof CustomEvent === 'undefined') { + return; + } + + var eventDetail = { + arguments: args, + __exposedProps__: args + }; + + var event = new CustomEvent(eventName, eventDetail); + obj.dispatchEvent(event); + } + + function setHarkEvents(connection, streamEvent) { + if (!streamEvent.stream || !getTracks(streamEvent.stream, 'audio').length) return; + + if (!connection || !streamEvent) { + throw 'Both arguments are required.'; + } + + if (!connection.onspeaking || !connection.onsilence) { + return; + } + + if (typeof hark === 'undefined') { + throw 'hark.js not found.'; + } + + hark(streamEvent.stream, { + onspeaking: function() { + connection.onspeaking(streamEvent); + }, + onsilence: function() { + connection.onsilence(streamEvent); + }, + onvolumechange: function(volume, threshold) { + if (!connection.onvolumechange) { + return; + } + connection.onvolumechange(merge({ + volume: volume, + threshold: threshold + }, streamEvent)); + } + }); + } + + function setMuteHandlers(connection, streamEvent) { + if (!streamEvent.stream || !streamEvent.stream || !streamEvent.stream.addEventListener) return; + + streamEvent.stream.addEventListener('mute', function(event) { + event = connection.streamEvents[streamEvent.streamid]; + + event.session = { + audio: event.muteType === 'audio', + video: event.muteType === 'video' + }; + + connection.onmute(event); + }, false); + + streamEvent.stream.addEventListener('unmute', function(event) { + event = connection.streamEvents[streamEvent.streamid]; + + event.session = { + audio: event.unmuteType === 'audio', + video: event.unmuteType === 'video' + }; + + connection.onunmute(event); + }, false); + } + + function getRandomString() { + if (window.crypto && window.crypto.getRandomValues && navigator.userAgent.indexOf('Safari') === -1) { + var a = window.crypto.getRandomValues(new Uint32Array(3)), + token = ''; + for (var i = 0, l = a.length; i < l; i++) { + token += a[i].toString(36); + } + return token; + } else { + return (Math.random() * new Date().getTime()).toString(36).replace(/\./g, ''); + } + } + + // Get HTMLAudioElement/HTMLVideoElement accordingly + // todo: add API documentation for connection.autoCreateMediaElement + + function getRMCMediaElement(stream, callback, connection) { + if (!connection.autoCreateMediaElement) { + callback({}); + return; + } + + var isAudioOnly = false; + if (!getTracks(stream, 'video').length && !stream.isVideo && !stream.isScreen) { + isAudioOnly = true; + } + + if (DetectRTC.browser.name === 'Firefox') { + if (connection.session.video || connection.session.screen) { + isAudioOnly = false; + } + } + + var mediaElement = document.createElement(isAudioOnly ? 'audio' : 'video'); + + mediaElement.srcObject = stream; + + mediaElement.setAttribute('autoplay', true); + mediaElement.setAttribute('playsinline', true); + mediaElement.setAttribute('controls', true); + mediaElement.setAttribute('muted', false); + mediaElement.setAttribute('volume', 1); + + // http://goo.gl/WZ5nFl + // Firefox don't yet support onended for any stream (remote/local) + if (DetectRTC.browser.name === 'Firefox') { + var streamEndedEvent = 'ended'; + + if ('oninactive' in mediaElement) { + streamEndedEvent = 'inactive'; + } + + mediaElement.addEventListener(streamEndedEvent, function() { + // fireEvent(stream, streamEndedEvent, stream); + currentUserMediaRequest.remove(stream.idInstance); + + if (stream.type === 'local') { + streamEndedEvent = 'ended'; + + if ('oninactive' in stream) { + streamEndedEvent = 'inactive'; + } + + StreamsHandler.onSyncNeeded(stream.streamid, streamEndedEvent); + + connection.attachStreams.forEach(function(aStream, idx) { + if (stream.streamid === aStream.streamid) { + delete connection.attachStreams[idx]; + } + }); + + var newStreamsArray = []; + connection.attachStreams.forEach(function(aStream) { + if (aStream) { + newStreamsArray.push(aStream); + } + }); + connection.attachStreams = newStreamsArray; + + var streamEvent = connection.streamEvents[stream.streamid]; + + if (streamEvent) { + connection.onstreamended(streamEvent); + return; + } + if (this.parentNode) { + this.parentNode.removeChild(this); + } + } + }, false); + } + + var played = mediaElement.play(); + if (typeof played !== 'undefined') { + var cbFired = false; + setTimeout(function() { + if (!cbFired) { + cbFired = true; + callback(mediaElement); + } + }, 1000); + played.then(function() { + if (cbFired) return; + cbFired = true; + callback(mediaElement); + }).catch(function(error) { + if (cbFired) return; + cbFired = true; + callback(mediaElement); + }); + } else { + callback(mediaElement); + } + } + + // if IE + if (!window.addEventListener) { + window.addEventListener = function(el, eventName, eventHandler) { + if (!el.attachEvent) { + return; + } + el.attachEvent('on' + eventName, eventHandler); + }; + } + + function listenEventHandler(eventName, eventHandler) { + window.removeEventListener(eventName, eventHandler); + window.addEventListener(eventName, eventHandler, false); + } + + window.attachEventListener = function(video, type, listener, useCapture) { + video.addEventListener(type, listener, useCapture); + }; + + function removeNullEntries(array) { + var newArray = []; + array.forEach(function(item) { + if (item) { + newArray.push(item); + } + }); + return newArray; + } + + + function isData(session) { + return !session.audio && !session.video && !session.screen && session.data; + } + + function isNull(obj) { + return typeof obj === 'undefined'; + } + + function isString(obj) { + return typeof obj === 'string'; + } + + var MediaStream = window.MediaStream; + + if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') { + MediaStream = webkitMediaStream; + } + + /*global MediaStream:true */ + if (typeof MediaStream !== 'undefined') { + if (!('stop' in MediaStream.prototype)) { + MediaStream.prototype.stop = function() { + this.getTracks().forEach(function(track) { + track.stop(); + }); + }; + } + } + + function isAudioPlusTab(connection, audioPlusTab) { + if (connection.session.audio && connection.session.audio === 'two-way') { + return false; + } + + if (DetectRTC.browser.name === 'Firefox' && audioPlusTab !== false) { + return true; + } + + if (DetectRTC.browser.name !== 'Chrome' || DetectRTC.browser.version < 50) return false; + + if (typeof audioPlusTab === true) { + return true; + } + + if (typeof audioPlusTab === 'undefined' && connection.session.audio && connection.session.screen && !connection.session.video) { + audioPlusTab = true; + return true; + } + + return false; + } + + function getAudioScreenConstraints(screen_constraints) { + if (DetectRTC.browser.name === 'Firefox') { + return true; + } + + if (DetectRTC.browser.name !== 'Chrome') return false; + + return { + mandatory: { + chromeMediaSource: screen_constraints.mandatory.chromeMediaSource, + chromeMediaSourceId: screen_constraints.mandatory.chromeMediaSourceId + } + }; + } + + window.iOSDefaultAudioOutputDevice = window.iOSDefaultAudioOutputDevice || 'speaker'; // earpiece or speaker + + function getTracks(stream, kind) { + if (!stream || !stream.getTracks) { + return []; + } + + return stream.getTracks().filter(function(t) { + return t.kind === (kind || 'audio'); + }); + } + + function isUnifiedPlanSupportedDefault() { + var canAddTransceiver = false; + + try { + if (typeof RTCRtpTransceiver === 'undefined') return false; + if (!('currentDirection' in RTCRtpTransceiver.prototype)) return false; + + var tempPc = new RTCPeerConnection(); + + try { + tempPc.addTransceiver('audio'); + canAddTransceiver = true; + } catch (e) {} + + tempPc.close(); + } catch (e) { + canAddTransceiver = false; + } + + return canAddTransceiver && isUnifiedPlanSuppored(); + } + + function isUnifiedPlanSuppored() { + var isUnifiedPlanSupported = false; + + try { + var pc = new RTCPeerConnection({ + sdpSemantics: 'unified-plan' + }); + + try { + var config = pc.getConfiguration(); + if (config.sdpSemantics == 'unified-plan') + isUnifiedPlanSupported = true; + else if (config.sdpSemantics == 'plan-b') + isUnifiedPlanSupported = false; + else + isUnifiedPlanSupported = false; + } catch (e) { + isUnifiedPlanSupported = false; + } + } catch (e) { + isUnifiedPlanSupported = false; + } + + return isUnifiedPlanSupported; + } + + // ios-hacks.js + + function setCordovaAPIs() { + // if (DetectRTC.osName !== 'iOS') return; + if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; + + 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*'); + if (typeof iosrtc.selectAudioOutput == 'function') { + iosrtc.selectAudioOutput(window.iOSDefaultAudioOutputDevice || 'speaker'); // earpiece or speaker + } + iosrtc.registerGlobals(); + } + + document.addEventListener('deviceready', setCordovaAPIs, false); + setCordovaAPIs(); + + // RTCPeerConnection.js + + var defaults = {}; + + function setSdpConstraints(config) { + var sdpConstraints = { + OfferToReceiveAudio: !!config.OfferToReceiveAudio, + OfferToReceiveVideo: !!config.OfferToReceiveVideo + }; + + return sdpConstraints; + } + + var RTCPeerConnection; + if (typeof window.RTCPeerConnection !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } else if (typeof mozRTCPeerConnection !== 'undefined') { + RTCPeerConnection = mozRTCPeerConnection; + } else if (typeof webkitRTCPeerConnection !== 'undefined') { + RTCPeerConnection = webkitRTCPeerConnection; + } + + var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; + var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + var MediaStreamTrack = window.MediaStreamTrack; + + function PeerInitiator(config) { + if (typeof window.RTCPeerConnection !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } else if (typeof mozRTCPeerConnection !== 'undefined') { + RTCPeerConnection = mozRTCPeerConnection; + } else if (typeof webkitRTCPeerConnection !== 'undefined') { + RTCPeerConnection = webkitRTCPeerConnection; + } + + RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; + RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + MediaStreamTrack = window.MediaStreamTrack; + + if (!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; + + if (config.remoteSdp) { + this.connectionDescription = config.remoteSdp.connectionDescription; + } + + var allRemoteStreams = {}; + + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: true, + OfferToReceiveVideo: true + }); + + var peer; + + var renegotiatingPeer = !!config.renegotiatingPeer; + if (config.remoteSdp) { + renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer; + } + + var localStreams = []; + connection.attachStreams.forEach(function(stream) { + if (!!stream) { + localStreams.push(stream); + } + }); + + if (!renegotiatingPeer) { + var iceTransports = 'all'; + if (connection.candidates.turn || connection.candidates.relay) { + if (!connection.candidates.stun && !connection.candidates.reflexive && !connection.candidates.host) { + iceTransports = 'relay'; + } + } + + try { + // ref: developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration + var params = { + iceServers: connection.iceServers, + iceTransportPolicy: connection.iceTransportPolicy || iceTransports + }; + + if (typeof connection.iceCandidatePoolSize !== 'undefined') { + params.iceCandidatePoolSize = connection.iceCandidatePoolSize; + } + + if (typeof connection.bundlePolicy !== 'undefined') { + params.bundlePolicy = connection.bundlePolicy; + } + + if (typeof connection.rtcpMuxPolicy !== 'undefined') { + params.rtcpMuxPolicy = connection.rtcpMuxPolicy; + } + + if (!!connection.sdpSemantics) { + params.sdpSemantics = connection.sdpSemantics || 'unified-plan'; + } + + if (!connection.iceServers || !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(); + } + } + } else { + peer = config.peerRef; + } + + if (!peer.getRemoteStreams && peer.getReceivers) { + peer.getRemoteStreams = function() { + var stream = new MediaStream(); + peer.getReceivers().forEach(function(receiver) { + stream.addTrack(receiver.track); + }); + return [stream]; + }; + } + + if (!peer.getLocalStreams && peer.getSenders) { + peer.getLocalStreams = function() { + var stream = new MediaStream(); + peer.getSenders().forEach(function(sender) { + stream.addTrack(sender.track); + }); + return [stream]; + }; + } + + peer.onicecandidate = function(event) { + if (!event.candidate) { + if (!connection.trickleIce) { + var localSdp = peer.localDescription; + config.onLocalSdp({ + type: localSdp.type, + sdp: localSdp.sdp, + remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, + renegotiatingPeer: !!config.renegotiatingPeer || false, + connectionDescription: self.connectionDescription, + dontGetRemoteStream: !!config.dontGetRemoteStream, + extra: connection ? connection.extra : {}, + streamsToShare: streamsToShare + }); + } + return; + } + + if (!connection.trickleIce) return; + config.onLocalCandidate({ + candidate: event.candidate.candidate, + sdpMid: event.candidate.sdpMid, + sdpMLineIndex: event.candidate.sdpMLineIndex + }); + }; + + localStreams.forEach(function(localStream) { + if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) { + return; + } + + if (config.dontAttachLocalStream) { + return; + } + + localStream = connection.beforeAddingStream(localStream, self); + + if (!localStream) return; + + peer.getLocalStreams().forEach(function(stream) { + if (localStream && stream.id == localStream.id) { + localStream = null; + } + }); + + if (localStream && localStream.getTracks) { + localStream.getTracks().forEach(function(track) { + try { + // last parameter is redundant for unified-plan + // starting from chrome version 72 + peer.addTrack(track, localStream); + } catch (e) {} + }); + } + }); + + peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() { + var extra = self.extra; + if (connection.peers[self.userid]) { + extra = connection.peers[self.userid].extra || extra; + } + + if (!peer) { + return; + } + + config.onPeerStateChanged({ + iceConnectionState: peer.iceConnectionState, + iceGatheringState: peer.iceGatheringState, + signalingState: peer.signalingState, + extra: extra, + userid: self.userid + }); + + if (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 + }; + + if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints; + + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); + + var streamObject; + var dontDuplicate = {}; + + peer.ontrack = function(event) { + if (!event || event.type !== 'track') return; + + event.stream = event.streams[event.streams.length - 1]; + + if (!event.stream.id) { + event.stream.id = event.track.id; + } + + if (dontDuplicate[event.stream.id] && DetectRTC.browser.name !== 'Safari') { + if (event.track) { + event.track.onended = function() { // event.track.onmute = + peer && peer.onremovestream(event); + }; + } + return; + } + + dontDuplicate[event.stream.id] = event.stream.id; + + var streamsToShare = {}; + if (config.remoteSdp && config.remoteSdp.streamsToShare) { + streamsToShare = config.remoteSdp.streamsToShare; + } else if (config.streamsToShare) { + streamsToShare = config.streamsToShare; + } + + var streamToShare = streamsToShare[event.stream.id]; + if (streamToShare) { + event.stream.isAudio = streamToShare.isAudio; + event.stream.isVideo = streamToShare.isVideo; + event.stream.isScreen = streamToShare.isScreen; + } else { + event.stream.isVideo = !!getTracks(event.stream, 'video').length; + event.stream.isAudio = !event.stream.isVideo; + event.stream.isScreen = false; + } + + event.stream.streamid = event.stream.id; + + allRemoteStreams[event.stream.id] = event.stream; + config.onRemoteStream(event.stream); + + event.stream.getTracks().forEach(function(track) { + track.onended = function() { // track.onmute = + peer && peer.onremovestream(event); + }; + }); + + event.stream.onremovetrack = function() { + peer && peer.onremovestream(event); + }; + }; + + peer.onremovestream = function(event) { + // this event doesn't works anymore + event.stream.streamid = event.stream.id; + + if (allRemoteStreams[event.stream.id]) { + delete allRemoteStreams[event.stream.id]; + } + + config.onRemoteStreamRemoved(event.stream); + }; + + if (typeof peer.removeStream !== 'function') { + // removeStream backward compatibility + peer.removeStream = function(stream) { + stream.getTracks().forEach(function(track) { + peer.removeTrack(track, stream); + }); + }; + } + + this.addRemoteCandidate = function(remoteCandidate) { + peer.addIceCandidate(new RTCIceCandidate(remoteCandidate)); + }; + + function oldAddRemoteSdp(remoteSdp, cb) { + cb = cb || function() {}; + + if (DetectRTC.browser.name !== 'Safari') { + remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); + } + peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), cb, function(error) { + if (!!connection.enableLogs) { + console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp); + } + + cb(); + }); + } + + this.addRemoteSdp = function(remoteSdp, cb) { + cb = cb || function() {}; + + if (DetectRTC.browser.name !== 'Safari') { + remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); + } + + peer.setRemoteDescription(new RTCSessionDescription(remoteSdp)).then(cb, function(error) { + if (!!connection.enableLogs) { + console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp); + } + + cb(); + }).catch(function(error) { + if (!!connection.enableLogs) { + console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp); + } + + cb(); + }); + }; + + var isOfferer = true; + + if (config.remoteSdp) { + isOfferer = false; + } + + this.createDataChannel = function() { + var channel = peer.createDataChannel('sctp', {}); + setChannelEvents(channel); + }; + + if (connection.session.data === true && !renegotiatingPeer) { + if (!isOfferer) { + peer.ondatachannel = function(event) { + var channel = event.channel; + setChannelEvents(channel); + }; + } else { + this.createDataChannel(); + } + } + + this.enableDisableVideoEncoding = function(enable) { + var rtcp; + peer.getSenders().forEach(function(sender) { + if (!rtcp && sender.track.kind === 'video') { + rtcp = sender; + } + }); + + if (!rtcp || !rtcp.getParameters) return; + + var parameters = rtcp.getParameters(); + parameters.encodings[1] && (parameters.encodings[1].active = !!enable); + parameters.encodings[2] && (parameters.encodings[2].active = !!enable); + rtcp.setParameters(parameters); + }; + + if (config.remoteSdp) { + if (config.remoteSdp.remotePeerSdpConstraints) { + sdpConstraints = config.remoteSdp.remotePeerSdpConstraints; + } + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); + this.addRemoteSdp(config.remoteSdp, function() { + createOfferOrAnswer('createAnswer'); + }); + } + + function setChannelEvents(channel) { + // force ArrayBuffer in Firefox; which uses "Blob" by default. + 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) { + if (channel.readyState !== 'open') { + return; + } + + channel.internalSend(data); + }; + + peer.channel = channel; + } + + if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') { + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio), + OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (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 + }; + }); + + function oldCreateOfferOrAnswer(_method) { + peer[_method](function(localSdp) { + if (DetectRTC.browser.name !== 'Safari') { + localSdp.sdp = connection.processSdp(localSdp.sdp); + } + peer.setLocalDescription(localSdp, function() { + if (!connection.trickleIce) return; + + config.onLocalSdp({ + type: localSdp.type, + sdp: localSdp.sdp, + remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, + renegotiatingPeer: !!config.renegotiatingPeer || false, + connectionDescription: self.connectionDescription, + dontGetRemoteStream: !!config.dontGetRemoteStream, + extra: connection ? connection.extra : {}, + streamsToShare: streamsToShare + }); + + connection.onSettingLocalDescription(self); + }, function(error) { + if (!!connection.enableLogs) { + console.error('setLocalDescription-error', error); + } + }); + }, function(error) { + if (!!connection.enableLogs) { + console.error('sdp-' + _method + '-error', error); + } + }, defaults.sdpConstraints); + } + + function createOfferOrAnswer(_method) { + peer[_method](defaults.sdpConstraints).then(function(localSdp) { + if (DetectRTC.browser.name !== 'Safari') { + localSdp.sdp = connection.processSdp(localSdp.sdp); + } + peer.setLocalDescription(localSdp).then(function() { + if (!connection.trickleIce) return; + + config.onLocalSdp({ + type: localSdp.type, + sdp: localSdp.sdp, + remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, + renegotiatingPeer: !!config.renegotiatingPeer || false, + connectionDescription: self.connectionDescription, + dontGetRemoteStream: !!config.dontGetRemoteStream, + extra: connection ? connection.extra : {}, + streamsToShare: streamsToShare + }); + + connection.onSettingLocalDescription(self); + }, function(error) { + if (!connection.enableLogs) return; + console.error('setLocalDescription error', error); + }); + }, function(error) { + if (!!connection.enableLogs) { + console.error('sdp-error', error); + } + }); + } + + if (isOfferer) { + createOfferOrAnswer('createOffer'); + } + + peer.nativeClose = peer.close; + peer.close = function() { + if (!peer) { + return; + } + + try { + if (peer.nativeClose !== peer.close) { + peer.nativeClose(); + } + } catch (e) {} + + peer = null; + self.peer = null; + }; + + this.peer = peer; + } + + // CodecsHandler.js + + var CodecsHandler = (function() { + function preferCodec(sdp, codecName) { + var info = splitLines(sdp); + + if (!info.videoCodecNumbers) { + return sdp; + } + + if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) { + return sdp; + } + + if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) { + return sdp; + } + + if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) { + return sdp; + } + + sdp = preferCodecHelper(sdp, codecName, info); + + return sdp; + } + + function preferCodecHelper(sdp, codec, info, ignore) { + var preferCodecNumber = ''; + + if (codec === 'vp8') { + if (!info.vp8LineNumber) { + return sdp; + } + preferCodecNumber = info.vp8LineNumber; + } + + if (codec === 'vp9') { + if (!info.vp9LineNumber) { + return sdp; + } + preferCodecNumber = info.vp9LineNumber; + } + + if (codec === 'h264') { + if (!info.h264LineNumber) { + return sdp; + } + + preferCodecNumber = info.h264LineNumber; + } + + var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF '; + + var newOrder = [preferCodecNumber]; + + if (ignore) { + newOrder = []; + } + + info.videoCodecNumbers.forEach(function(codecNumber) { + if (codecNumber === preferCodecNumber) return; + newOrder.push(codecNumber); + }); + + newLine += newOrder.join(' '); + + sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine); + return sdp; + } + + function splitLines(sdp) { + var info = {}; + sdp.split('\n').forEach(function(line) { + if (line.indexOf('m=video') === 0) { + info.videoCodecNumbers = []; + line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) { + codecNumber = codecNumber.trim(); + if (!codecNumber || !codecNumber.length) return; + info.videoCodecNumbers.push(codecNumber); + info.videoCodecNumbersOriginal = line; + }); + } + + if (line.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) { + info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0]; + } + + if (line.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) { + info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0]; + } + + if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) { + info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0]; + } + }); + + return info; + } + + function removeVPX(sdp) { + var info = splitLines(sdp); + + // last parameter below means: ignore these codecs + sdp = preferCodecHelper(sdp, 'vp9', info, true); + sdp = preferCodecHelper(sdp, 'vp8', info, true); + + return sdp; + } + + function disableNACK(sdp) { + if (!sdp || typeof sdp !== 'string') { + throw 'Invalid arguments.'; + } + + 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'); + + return sdp; + } + + function prioritize(codecMimeType, peer) { + if (!peer || !peer.getSenders || !peer.getSenders().length) { + return; + } + + if (!codecMimeType || typeof codecMimeType !== 'string') { + throw 'Invalid arguments.'; + } + + peer.getSenders().forEach(function(sender) { + var params = sender.getParameters(); + for (var 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) { + if (!bandwidth) { + return sdp; + } + + if (typeof isFirefox !== 'undefined' && isFirefox) { + return sdp; + } + + if (isScreen) { + if (!bandwidth.screen) { + console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.'); + } else if (bandwidth.screen < 300) { + console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.'); + } + } + + // if screen; must use at least 300kbs + if (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'); + } + + // remove existing bandwidth lines + if (bandwidth.audio || bandwidth.video) { + sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + } + + if (bandwidth.audio) { + sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n'); + } + + if (bandwidth.screen) { + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n'); + } else if (bandwidth.video) { + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.video + '\r\n'); + } + + return sdp; + } + + // Find the line in sdpLines that starts with |prefix|, and, if specified, + // contains |substr| (case-insensitive search). + function findLine(sdpLines, prefix, substr) { + return findLineInRange(sdpLines, 0, -1, prefix, substr); + } + + // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| + // and, if specified, contains |substr| (case-insensitive search). + function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { + var realEndLine = endLine !== -1 ? endLine : sdpLines.length; + for (var i = startLine; i < realEndLine; ++i) { + if (sdpLines[i].indexOf(prefix) === 0) { + if (!substr || + sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { + return i; + } + } + } + return null; + } + + // Gets the codec payload type from an a=rtpmap:X line. + function getCodecPayloadType(sdpLine) { + var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); + var result = sdpLine.match(pattern); + return (result && result.length === 2) ? result[1] : null; + } + + function setVideoBitrates(sdp, params) { + params = params || {}; + var xgoogle_min_bitrate = params.min; + var xgoogle_max_bitrate = params.max; + + var sdpLines = sdp.split('\r\n'); + + // VP8 + var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000'); + var vp8Payload; + if (vp8Index) { + vp8Payload = getCodecPayloadType(sdpLines[vp8Index]); + } + + if (!vp8Payload) { + return sdp; + } + + var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000'); + var rtxPayload; + if (rtxIndex) { + rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]); + } + + if (!rtxIndex) { + return sdp; + } + + var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString()); + if (rtxFmtpLineIndex !== null) { + 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 sdpLines = sdp.split('\r\n'); + + // Opus + var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'); + var opusPayload; + if (opusIndex) { + opusPayload = getCodecPayloadType(sdpLines[opusIndex]); + } + + if (!opusPayload) { + return sdp; + } + + var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString()); + if (opusFmtpLineIndex === null) { + return sdp; + } + + var appendOpusNext = ''; + appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1'); + appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1'); + + if (typeof params.maxaveragebitrate != 'undefined') { + appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8); + } + + if (typeof params.maxplaybackrate != 'undefined') { + appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8); + } + + if (typeof params.cbr != 'undefined') { + appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1'); + } + + if (typeof params.useinbandfec != 'undefined') { + appendOpusNext += '; useinbandfec=' + params.useinbandfec; + } + + if (typeof params.usedtx != 'undefined') { + appendOpusNext += '; usedtx=' + params.usedtx; + } + + if (typeof params.maxptime != 'undefined') { + appendOpusNext += '\r\na=maxptime:' + params.maxptime; + } + + sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext); + + sdp = sdpLines.join('\r\n'); + return sdp; + } + + // forceStereoAudio => via webrtcexample.com + // requires getUserMedia => echoCancellation:false + function forceStereoAudio(sdp) { + var sdpLines = sdp.split('\r\n'); + var fmtpLineIndex = null; + for (var 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; + } + } + } + if (fmtpLineIndex === null) return sdp; + sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1; sprop-stereo=1'); + sdp = sdpLines.join('\r\n'); + return sdp; + } + + 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 + }; + })(); + + // backward compatibility + window.BandwidthHandler = CodecsHandler; + + // OnIceCandidateHandler.js + + var OnIceCandidateHandler = (function() { + function processCandidates(connection, icePair) { + var candidate = icePair.candidate; + + var iceRestrictions = connection.candidates; + var stun = iceRestrictions.stun; + var turn = iceRestrictions.turn; + + if (!isNull(iceRestrictions.reflexive)) { + stun = iceRestrictions.reflexive; + } + + if (!isNull(iceRestrictions.relay)) { + turn = iceRestrictions.relay; + } + + if (!iceRestrictions.host && !!candidate.match(/typ host/g)) { + return; + } + + if (!turn && !!candidate.match(/typ relay/g)) { + return; + } + + if (!stun && !!candidate.match(/typ srflx/g)) { + return; + } + + var protocol = connection.iceProtocols; + + if (!protocol.udp && !!candidate.match(/ udp /g)) { + return; + } + + if (!protocol.tcp && !!candidate.match(/ tcp /g)) { + return; + } + + if (connection.enableLogs) { + console.debug('Your candidate pairs:', candidate); + } + + return { + candidate: candidate, + sdpMid: icePair.sdpMid, + sdpMLineIndex: icePair.sdpMLineIndex + }; + } + + return { + processCandidates: processCandidates + }; + })(); + + // IceServersHandler.js + + var IceServersHandler = (function() { + function getIceServers(connection) { + // resiprocate: 3344+4433 + // pions: 7575 + var iceServers = [{ + 'urls': [ + 'stun:stun.l.google.com:19302', + 'stun:stun1.l.google.com:19302', + 'stun:stun2.l.google.com:19302', + 'stun:stun.l.google.com:19302?transport=udp', + ] + }]; + + return iceServers; + } + + return { + getIceServers: getIceServers + }; + })(); + + // getUserMediaHandler.js + + function setStreamType(constraints, stream) { + if (constraints.mandatory && constraints.mandatory.chromeMediaSource) { + stream.isScreen = true; + } else if (constraints.mozMediaSource || constraints.mediaSource) { + stream.isScreen = true; + } else if (constraints.video) { + stream.isVideo = true; + } else if (constraints.audio) { + stream.isAudio = true; + } + } + + // allow users to manage this object (to support re-capturing of screen/etc.) + window.currentUserMediaRequest = { + streams: [], + mutex: false, + queueRequests: [], + remove: function(idInstance) { + this.mutex = false; + + var stream = this.streams[idInstance]; + if (!stream) { + return; + } + + stream = stream.stream; + + var options = stream.currentUserMediaRequestOptions; + + if (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]; + } + }; + + function getUserMediaHandler(options) { + if (currentUserMediaRequest.mutex === true) { + currentUserMediaRequest.queueRequests.push(options); + return; + } + currentUserMediaRequest.mutex = true; + + // easy way to match + var idInstance = JSON.stringify(options.localMediaConstraints); + + function streaming(stream, returnBack) { + setStreamType(options.localMediaConstraints, stream); + + var streamEndedEvent = 'ended'; + + if ('oninactive' in stream) { + streamEndedEvent = 'inactive'; + } + stream.addEventListener(streamEndedEvent, function() { + delete currentUserMediaRequest.streams[idInstance]; + + currentUserMediaRequest.mutex = false; + if (currentUserMediaRequest.queueRequests.indexOf(options)) { + delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)]; + currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests); + } + }, false); + + currentUserMediaRequest.streams[idInstance] = { + stream: stream + }; + currentUserMediaRequest.mutex = false; + + if (currentUserMediaRequest.queueRequests.length) { + getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()); + } + + // callback + options.onGettingLocalMedia(stream, returnBack); + } + + if (currentUserMediaRequest.streams[idInstance]) { + streaming(currentUserMediaRequest.streams[idInstance].stream, true); + } else { + var isBlackBerry = !!(/BB10|BlackBerry/i.test(navigator.userAgent || '')); + if (isBlackBerry || typeof navigator.mediaDevices === 'undefined' || typeof navigator.mediaDevices.getUserMedia !== 'function') { + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + 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); + }); + return; + } + + if (typeof navigator.mediaDevices === 'undefined') { + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + var getUserMediaSuccess = function() {}; + var getUserMediaFailure = function() {}; + + var getUserMediaStream, getUserMediaError; + navigator.mediaDevices = { + getUserMedia: function(hints) { + navigator.getUserMedia(hints, function(getUserMediaSuccess) { + getUserMediaSuccess(stream); + getUserMediaStream = stream; + }, function(error) { + getUserMediaFailure(error); + getUserMediaError = error; + }); + + return { + then: function(successCB) { + if (getUserMediaStream) { + successCB(getUserMediaStream); + return; + } + + getUserMediaSuccess = successCB; + + return { + then: function(failureCB) { + if (getUserMediaError) { + failureCB(getUserMediaError); + return; + } + + getUserMediaFailure = failureCB; + } + } + } + } + } + }; + } + + if (options.localMediaConstraints.isScreen === true) { + if (navigator.mediaDevices.getDisplayMedia) { + navigator.mediaDevices.getDisplayMedia(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); + }); + } else if (navigator.getDisplayMedia) { + navigator.getDisplayMedia(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); + }); + } else { + throw new Error('getDisplayMedia API is not availabe in this browser.'); + } + return; + } + + 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); + }); + } + } + + // StreamsHandler.js + + var StreamsHandler = (function() { + function handleType(type) { + if (!type) { + return; + } + + if (typeof type === 'string' || typeof type === 'undefined') { + return type; + } + + if (type.audio && type.video) { + return null; + } + + if (type.audio) { + return 'audio'; + } + + if (type.video) { + return 'video'; + } + + return; + } + + function setHandlers(stream, syncAction, connection) { + if (!stream || !stream.addEventListener) return; + + if (typeof syncAction == 'undefined' || syncAction == true) { + var streamEndedEvent = 'ended'; + + if ('oninactive' in stream) { + streamEndedEvent = 'inactive'; + } + + stream.addEventListener(streamEndedEvent, function() { + StreamsHandler.onSyncNeeded(this.streamid, streamEndedEvent); + }, false); + } + + stream.mute = function(type, isSyncAction) { + type = handleType(type); + + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; + } + + if (typeof type == 'undefined' || type == 'audio') { + getTracks(stream, 'audio').forEach(function(track) { + track.enabled = false; + connection.streamEvents[stream.streamid].isAudioMuted = true; + }); + } + + if (typeof type == 'undefined' || type == 'video') { + getTracks(stream, 'video').forEach(function(track) { + track.enabled = false; + }); + } + + if (typeof syncAction == 'undefined' || syncAction == true) { + 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); + + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; + } + + graduallyIncreaseVolume(); + + if (typeof type == 'undefined' || type == 'audio') { + getTracks(stream, 'audio').forEach(function(track) { + track.enabled = true; + connection.streamEvents[stream.streamid].isAudioMuted = false; + }); + } + + if (typeof type == 'undefined' || type == 'video') { + getTracks(stream, 'video').forEach(function(track) { + track.enabled = true; + }); + + // make sure that video unmute doesn't affects audio + if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) { + (function looper(times) { + if (!times) { + times = 0; + } + + times++; + + // check until five-seconds + if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) { + stream.mute('audio'); + + setTimeout(function() { + looper(times); + }, 50); + } + })(); + } + } + + if (typeof syncAction == 'undefined' || syncAction == true) { + StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type); + } + + connection.streamEvents[stream.streamid].unmuteType = type || 'both'; + + fireEvent(stream, 'unmute', type); + }; + + function graduallyIncreaseVolume() { + if (!connection.streamEvents[stream.streamid].mediaElement) { + return; + } + + var mediaElement = connection.streamEvents[stream.streamid].mediaElement; + mediaElement.volume = 0; + afterEach(200, 5, function() { + try { + mediaElement.volume += .20; + } catch (e) { + mediaElement.volume = 1; + } + }); + } + } + + function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) { + startedTimes = (startedTimes || 0) + 1; + if (startedTimes >= numberOfTimes) return; + + setTimeout(function() { + callback(); + afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes); + }, setTimeoutInteval); + } + + return { + setHandlers: setHandlers, + onSyncNeeded: function(streamid, action, type) {} + }; + })(); + + // TextReceiver.js & TextSender.js + + function TextReceiver(connection) { + var content = {}; + + function receive(data, userid, extra) { + // uuid is used to uniquely identify sending instance + var uuid = data.uuid; + if (!content[uuid]) { + content[uuid] = []; + } + + content[uuid].push(data.message); + + if (data.last) { + var message = content[uuid].join(''); + if (data.isobject) { + message = JSON.parse(message); + } + + // latency detection + var receivingTime = new Date().getTime(); + var latency = receivingTime - data.sendingTime; + + var e = { + data: message, + userid: userid, + extra: extra, + latency: latency + }; + + if (connection.autoTranslateText) { + e.original = e.data; + connection.Translator.TranslateText(e.data, function(translatedText) { + e.data = translatedText; + connection.onmessage(e); + }); + } else { + connection.onmessage(e); + } + + delete content[uuid]; + } + } + + return { + receive: receive + }; + } + + // TextSender.js + var TextSender = { + send: function(config) { + var connection = config.connection; + + var channel = config.channel, + remoteUserId = config.remoteUserId, + initialText = config.text, + packetSize = connection.chunkSize || 1000, + textToTransfer = '', + isobject = false; + + if (!isString(initialText)) { + isobject = true; + initialText = JSON.stringify(initialText); + } + + // uuid is used to uniquely identify sending instance + var uuid = getRandomString(); + var sendingTime = new Date().getTime(); + + sendText(initialText); + + function sendText(textMessage, text) { + var data = { + type: 'text', + uuid: uuid, + sendingTime: sendingTime + }; + + if (textMessage) { + text = textMessage; + data.packets = parseInt(text.length / packetSize); + } + + if (text.length > packetSize) { + data.message = text.slice(0, packetSize); + } else { + data.message = text; + data.last = true; + data.isobject = isobject; + } + + channel.send(data, remoteUserId); + + textToTransfer = text.slice(data.message.length); + + if (textToTransfer.length) { + setTimeout(function() { + sendText(null, textToTransfer); + }, connection.chunkInterval || 100); + } + } + } + }; + + // FileProgressBarHandler.js + + var FileProgressBarHandler = (function() { + function handle(connection) { + var progressHelper = {}; + + // www.RTCMultiConnection.org/docs/onFileStart/ + connection.onFileStart = function(file) { + var div = document.createElement('div'); + div.title = file.name; + div.innerHTML = ' '; + + if (file.remoteUserId) { + div.innerHTML += ' (Sharing with:' + file.remoteUserId + ')'; + } + + if (!connection.filesContainer) { + connection.filesContainer = document.body || document.documentElement; + } + + connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild); + + if (!file.remoteUserId) { + progressHelper[file.uuid] = { + div: div, + progress: div.querySelector('progress'), + label: div.querySelector('label') + }; + progressHelper[file.uuid].progress.max = file.maxChunks; + return; + } + + if (!progressHelper[file.uuid]) { + progressHelper[file.uuid] = {}; + } + + progressHelper[file.uuid][file.remoteUserId] = { + div: div, + progress: div.querySelector('progress'), + label: div.querySelector('label') + }; + progressHelper[file.uuid][file.remoteUserId].progress.max = file.maxChunks; + }; + + // www.RTCMultiConnection.org/docs/onFileProgress/ + connection.onFileProgress = function(chunk) { + var helper = progressHelper[chunk.uuid]; + if (!helper) { + return; + } + if (chunk.remoteUserId) { + helper = progressHelper[chunk.uuid][chunk.remoteUserId]; + if (!helper) { + return; + } + } + + helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max; + updateLabel(helper.progress, helper.label); + }; + + // www.RTCMultiConnection.org/docs/onFileEnd/ + connection.onFileEnd = function(file) { + var helper = progressHelper[file.uuid]; + if (!helper) { + console.error('No such progress-helper element exist.', file); + return; + } + + if (file.remoteUserId) { + helper = progressHelper[file.uuid][file.remoteUserId]; + if (!helper) { + return; + } + } + + var div = helper.div; + if (file.type.indexOf('image') != -1) { + div.innerHTML = 'Download ' + file.name + '
'; + } else { + div.innerHTML = 'Download ' + file.name + '
'; + } + }; + + function updateLabel(progress, label) { + if (progress.position === -1) { + return; + } + + var position = +progress.position.toFixed(2).split('.')[1] || 100; + label.innerHTML = position + '%'; + } + } + + return { + handle: handle + }; + })(); + + // TranslationHandler.js + + var TranslationHandler = (function() { + function handle(connection) { + connection.autoTranslateText = false; + connection.language = 'en'; + connection.googKey = 'AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE'; + + // www.RTCMultiConnection.org/docs/Translator/ + connection.Translator = { + TranslateText: function(text, callback) { + // if(location.protocol === 'https:') return callback(text); + + var newScript = document.createElement('script'); + newScript.type = 'text/javascript'; + + var sourceText = encodeURIComponent(text); // escape + + var randomNumber = 'method' + connection.token(); + window[randomNumber] = function(response) { + if (response.data && response.data.translations[0] && callback) { + callback(response.data.translations[0].translatedText); + return; + } + + if (response.error && response.error.message === 'Daily Limit Exceeded') { + console.error('Text translation failed. Error message: "Daily Limit Exceeded."'); + return; + } + + if (response.error) { + console.error(response.error.message); + return; + } + + 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) { + callback(response.data.languages); + return; + } + + if (response.error && response.error.message === 'Daily Limit Exceeded') { + console.error('Text translation failed. Error message: "Daily Limit Exceeded."'); + return; + } + + if (response.error) { + console.error(response.error.message); + return; + } + + console.error(response); + } + } + var url = 'https://www.googleapis.com/language/translate/v2/languages?key=' + connection.googKey + '&target=en'; + xhr.open('GET', url, true); + xhr.send(null); + } + }; + } + + return { + handle: handle + }; + })(); + + // _____________________ + // RTCMultiConnection.js + + (function(connection) { + forceOptions = forceOptions || { + useDefaultDevices: true + }; + + connection.channel = connection.sessionid = (roomid || location.href.replace(/\/|:|#|\?|\$|\^|%|\.|`|~|!|\+|@|\[|\||]|\|*. /g, '').split('\n').join('').split('\r').join('')) + ''; + + var mPeer = new MultiPeers(connection); + + var preventDuplicateOnStreamEvents = {}; + mPeer.onGettingLocalMedia = function(stream, callback) { + callback = callback || function() {}; + + if (preventDuplicateOnStreamEvents[stream.streamid]) { + callback(); + return; + } + preventDuplicateOnStreamEvents[stream.streamid] = true; + + try { + stream.type = 'local'; + } catch (e) {} + + connection.setStreamEndHandler(stream); + + getRMCMediaElement(stream, function(mediaElement) { + mediaElement.id = stream.streamid; + mediaElement.muted = true; + mediaElement.volume = 0; + + if (connection.attachStreams.indexOf(stream) === -1) { + connection.attachStreams.push(stream); + } + + if (typeof StreamsHandler !== 'undefined') { + StreamsHandler.setHandlers(stream, true, connection); + } + + connection.streamEvents[stream.streamid] = { + stream: stream, + type: 'local', + mediaElement: mediaElement, + userid: connection.userid, + extra: connection.extra, + streamid: stream.streamid, + isAudioMuted: true + }; + + try { + setHarkEvents(connection, connection.streamEvents[stream.streamid]); + setMuteHandlers(connection, connection.streamEvents[stream.streamid]); + + connection.onstream(connection.streamEvents[stream.streamid]); + } catch (e) { + // + } + + 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; + + if (typeof StreamsHandler !== 'undefined') { + StreamsHandler.setHandlers(stream, false, 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]; + if (!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 + }; + } + + if (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) { + callback = callback || function() {}; + + remoteUserId = remoteUserId || message.remoteUserId; + message = message || ''; + + // usually a message looks like this + var messageToDeliver = { + remoteUserId: remoteUserId, + message: message, + sender: connection.userid + }; + + if (message.remoteUserId && message.message && message.sender) { + // if a code is manually passing required data + messageToDeliver = message; + } + + connectSocket(function() { + connection.socket.emit(connection.socketMessageEvent, messageToDeliver, callback); + }); + }; + + function onUserLeft(remoteUserId) { + connection.deletePeer(remoteUserId); + } + + mPeer.onUserLeft = onUserLeft; + mPeer.disconnectWith = function(remoteUserId, callback) { + if (connection.socket) { + connection.socket.emit('disconnect-with', remoteUserId, callback || function() {}); + } + + connection.deletePeer(remoteUserId); + }; + + connection.socketOptions = { + // 'force new connection': true, // For SocketIO version < 1.0 + // 'forceNew': true, // For SocketIO version >= 1.0 + 'transport': 'polling' // fixing transport:unknown issues + }; + + function connectSocket(connectCallback) { + connection.socketAutoReConnect = true; + + if (connection.socket) { // todo: check here readySate/etc. to make sure socket is still opened + if (connectCallback) { + connectCallback(connection.socket); + } + return; + } + + if (typeof SocketConnection === 'undefined') { + if (typeof FirebaseConnection !== 'undefined') { + window.SocketConnection = FirebaseConnection; + } else if (typeof PubNubConnection !== 'undefined') { + window.SocketConnection = PubNubConnection; + } else { + throw 'SocketConnection.js seems missed.'; + } + } + + new SocketConnection(connection, function(s) { + if (connectCallback) { + connectCallback(connection.socket); + } + }); + } + + // 1st paramter is roomid + // 2rd paramter is a callback function + connection.openOrJoin = function(roomid, callback) { + callback = callback || function() {}; + + connection.checkPresence(roomid, function(isRoomExist, roomid) { + if (isRoomExist) { + connection.sessionid = roomid; + + var localPeerSdpConstraints = false; + var remotePeerSdpConstraints = false; + var isOneWay = !!connection.session.oneway; + var 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: true, + isOneWay: isOneWay, + isDataOnly: isDataOnly, + localPeerSdpConstraints: localPeerSdpConstraints, + remotePeerSdpConstraints: remotePeerSdpConstraints + }, + sender: connection.userid + }; + + beforeJoin(connectionDescription.message, function() { + joinRoom(connectionDescription, callback); + }); + return; + } + + connection.waitingForLocalMedia = true; + connection.isInitiator = true; + + connection.sessionid = roomid || connection.sessionid; + + if (isData(connection.session)) { + openRoom(callback); + return; + } + + connection.captureUserMedia(function() { + openRoom(callback); + }); + }); + }; + + // don't allow someone to join this person until he has the media + connection.waitingForLocalMedia = false; + + connection.open = function(roomid, callback) { + callback = callback || function() {}; + + connection.waitingForLocalMedia = true; + connection.isInitiator = true; + + connection.sessionid = roomid || connection.sessionid; + + connectSocket(function() { + if (isData(connection.session)) { + openRoom(callback); + return; + } + + connection.captureUserMedia(function() { + openRoom(callback); + }); + }); + }; + + // this object keeps extra-data records for all connected users + // this object is never cleared so you can always access extra-data even if a user left + connection.peersBackup = {}; + + connection.deletePeer = function(remoteUserId) { + if (!remoteUserId || !connection.peers[remoteUserId]) { + return; + } + + 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); + + if (!!connection.peers[remoteUserId]) { + connection.peers[remoteUserId].streams.forEach(function(stream) { + stream.stop(); + }); + + var peer = connection.peers[remoteUserId].peer; + if (peer && peer.iceConnectionState !== 'closed') { + try { + peer.close(); + } catch (e) {} + } + + if (connection.peers[remoteUserId]) { + connection.peers[remoteUserId].peer = null; + delete connection.peers[remoteUserId]; + } + } + } + + connection.rejoin = function(connectionDescription) { + if (connection.isInitiator || !connectionDescription || !Object.keys(connectionDescription).length) { + return; + } + + var extra = {}; + + if (connection.peers[connectionDescription.remoteUserId]) { + extra = connection.peers[connectionDescription.remoteUserId].extra; + connection.deletePeer(connectionDescription.remoteUserId); + } + + if (connectionDescription && connectionDescription.remoteUserId) { + connection.join(connectionDescription.remoteUserId); + + connection.onReConnecting({ + userid: connectionDescription.remoteUserId, + extra: extra + }); + } + }; + + connection.join = function(remoteUserId, options) { + connection.sessionid = (remoteUserId ? remoteUserId.sessionid || remoteUserId.remoteUserId || remoteUserId : false) || connection.sessionid; + connection.sessionid += ''; + + var localPeerSdpConstraints = false; + var remotePeerSdpConstraints = false; + var isOneWay = false; + var isDataOnly = false; + + if ((remoteUserId && remoteUserId.session) || !remoteUserId || typeof remoteUserId === 'string') { + 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() {}; + if (typeof options === 'function') { + cb = options; + options = {}; + } + + if (typeof options.localPeerSdpConstraints !== 'undefined') { + localPeerSdpConstraints = options.localPeerSdpConstraints; + } + + if (typeof options.remotePeerSdpConstraints !== 'undefined') { + remotePeerSdpConstraints = options.remotePeerSdpConstraints; + } + + if (typeof options.isOneWay !== 'undefined') { + isOneWay = options.isOneWay; + } + + if (typeof options.isDataOnly !== 'undefined') { + isDataOnly = options.isDataOnly; + } + + var connectionDescription = { + remoteUserId: connection.sessionid, + message: { + newParticipationRequest: true, + isOneWay: isOneWay, + isDataOnly: isDataOnly, + localPeerSdpConstraints: localPeerSdpConstraints, + remotePeerSdpConstraints: remotePeerSdpConstraints + }, + sender: connection.userid + }; + + beforeJoin(connectionDescription.message, function() { + connectSocket(function() { + joinRoom(connectionDescription, cb); + }); + }); + return connectionDescription; + }; + + function joinRoom(connectionDescription, cb) { + connection.socket.emit('join-room', { + sessionid: connection.sessionid, + session: connection.session, + mediaConstraints: connection.mediaConstraints, + sdpConstraints: connection.sdpConstraints, + streams: getStreamInfoForAdmin(), + extra: connection.extra, + password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : '' + }, function(isRoomJoined, error) { + if (isRoomJoined === true) { + if (connection.enableLogs) { + console.log('isRoomJoined: ', isRoomJoined, ' roomid: ', connection.sessionid); + } + + if (!!connection.peers[connection.sessionid]) { + // on socket disconnect & reconnect + return; + } + + mPeer.onNegotiationNeeded(connectionDescription); + } + + if (isRoomJoined === false) { + if (connection.enableLogs) { + console.warn('isRoomJoined: ', error, ' roomid: ', connection.sessionid); + } + + // [disabled] retry after 3 seconds + false && setTimeout(function() { + joinRoom(connectionDescription, cb); + }, 3000); + } + + cb(isRoomJoined, connection.sessionid, error); + }); + } + + connection.publicRoomIdentifier = ''; + + function openRoom(callback) { + if (connection.enableLogs) { + console.log('Sending open-room signal to socket.io'); + } + + connection.waitingForLocalMedia = false; + connection.socket.emit('open-room', { + sessionid: connection.sessionid, + session: connection.session, + mediaConstraints: connection.mediaConstraints, + sdpConstraints: connection.sdpConstraints, + streams: getStreamInfoForAdmin(), + extra: connection.extra, + identifier: connection.publicRoomIdentifier, + password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : '' + }, function(isRoomOpened, error) { + if (isRoomOpened === true) { + if (connection.enableLogs) { + console.log('isRoomOpened: ', isRoomOpened, ' roomid: ', connection.sessionid); + } + callback(isRoomOpened, connection.sessionid); + } + + if (isRoomOpened === false) { + if (connection.enableLogs) { + console.warn('isRoomOpened: ', error, ' roomid: ', connection.sessionid); + } + + callback(isRoomOpened, connection.sessionid, error); + } + }); + } + + function getStreamInfoForAdmin() { + try { + return connection.streamEvents.selectAll('local').map(function(event) { + return { + streamid: event.streamid, + tracks: event.stream.getTracks().length + }; + }); + } catch (e) { + return []; + } + } + + function beforeJoin(userPreferences, callback) { + if (connection.dontCaptureUserMedia || userPreferences.isDataOnly) { + callback(); + return; + } + + var localMediaConstraints = {}; + + if (userPreferences.localPeerSdpConstraints.OfferToReceiveAudio) { + localMediaConstraints.audio = connection.mediaConstraints.audio; + } + + if (userPreferences.localPeerSdpConstraints.OfferToReceiveVideo) { + localMediaConstraints.video = connection.mediaConstraints.video; + } + + var session = userPreferences.session || connection.session; + + if (session.oneway && session.audio !== 'two-way' && session.video !== 'two-way' && session.screen !== 'two-way') { + callback(); + return; + } + + if (session.oneway && session.audio && session.audio === 'two-way') { + session = { + audio: true + }; + } + + if (session.audio || session.video || session.screen) { + if (session.screen) { + if (DetectRTC.browser.name === 'Edge') { + navigator.getDisplayMedia({ + video: true, + audio: isAudioPlusTab(connection) + }).then(function(screen) { + screen.isScreen = true; + mPeer.onGettingLocalMedia(screen); + + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + connection.invokeGetUserMedia(null, callback); + } else { + callback(screen); + } + }, function(error) { + console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.'); + }); + } else { + connection.invokeGetUserMedia({ + audio: isAudioPlusTab(connection), + video: true, + isScreen: true + }, (session.audio || session.video) && !isAudioPlusTab(connection) ? connection.invokeGetUserMedia(null, callback) : callback); + } + } else if (session.audio || session.video) { + connection.invokeGetUserMedia(null, callback, session); + } + } + } + + connection.getUserMedia = connection.captureUserMedia = function(callback, sessionForced) { + callback = callback || function() {}; + var session = sessionForced || connection.session; + + if (connection.dontCaptureUserMedia || isData(session)) { + callback(); + return; + } + + if (session.audio || session.video || session.screen) { + if (session.screen) { + if (DetectRTC.browser.name === 'Edge') { + navigator.getDisplayMedia({ + video: true, + audio: isAudioPlusTab(connection) + }).then(function(screen) { + screen.isScreen = true; + mPeer.onGettingLocalMedia(screen); + + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + var nonScreenSession = {}; + for (var s in session) { + if (s !== 'screen') { + nonScreenSession[s] = session[s]; + } + } + connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession); + return; + } + callback(screen); + }, function(error) { + console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.'); + }); + } else { + connection.invokeGetUserMedia({ + audio: isAudioPlusTab(connection), + video: true, + isScreen: true + }, function(stream) { + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + var nonScreenSession = {}; + for (var s in session) { + if (s !== 'screen') { + nonScreenSession[s] = session[s]; + } + } + connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession); + return; + } + callback(stream); + }); + } + } else if (session.audio || session.video) { + connection.invokeGetUserMedia(sessionForced, callback, session); + } + } + }; + + connection.onbeforeunload = function(arg1, dontCloseSocket) { + if (!connection.closeBeforeUnload) { + return; + } + + connection.peers.getAllParticipants().forEach(function(participant) { + mPeer.onNegotiationNeeded({ + userLeft: true + }, participant); + + if (connection.peers[participant] && connection.peers[participant].peer) { + connection.peers[participant].peer.close(); + } + + delete connection.peers[participant]; + }); + + if (!dontCloseSocket) { + connection.closeSocket(); + } + + connection.isInitiator = false; + }; + + if (!window.ignoreBeforeUnload) { + // user can implement its own version of window.onbeforeunload + connection.closeBeforeUnload = true; + window.addEventListener('beforeunload', connection.onbeforeunload, false); + } else { + connection.closeBeforeUnload = false; + } + + 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: true, + video: true + }; + + connection.enableFileSharing = false; + + // all values in kbps + connection.bandwidth = { + screen: false, + audio: false, + video: false + }; + + connection.codecs = { + audio: 'opus', + video: 'VP9' + }; + + connection.processSdp = function(sdp) { + // ignore SDP modification if unified-pan is supported + if (isUnifiedPlanSupportedDefault()) { + return sdp; + } + + if (DetectRTC.browser.name === 'Safari') { + return sdp; + } + + if (connection.codecs.video.toUpperCase() === 'VP8') { + sdp = CodecsHandler.preferCodec(sdp, 'vp8'); + } + + if (connection.codecs.video.toUpperCase() === 'VP9') { + sdp = CodecsHandler.preferCodec(sdp, 'vp9'); + } + + if (connection.codecs.video.toUpperCase() === 'H264') { + sdp = CodecsHandler.preferCodec(sdp, 'h264'); + } + + if (connection.codecs.audio === 'G722') { + sdp = CodecsHandler.removeNonG722(sdp); + } + + if (DetectRTC.browser.name === 'Firefox') { + return sdp; + } + + if (connection.bandwidth.video || connection.bandwidth.screen) { + sdp = CodecsHandler.setApplicationSpecificBandwidth(sdp, connection.bandwidth, !!connection.session.screen); + } + + if (connection.bandwidth.video) { + sdp = CodecsHandler.setVideoBitrates(sdp, { + min: connection.bandwidth.video * 8 * 1024, + max: connection.bandwidth.video * 8 * 1024 + }); + } + + if (connection.bandwidth.audio) { + sdp = CodecsHandler.setOpusAttributes(sdp, { + maxaveragebitrate: connection.bandwidth.audio * 8 * 1024, + maxplaybackrate: connection.bandwidth.audio * 8 * 1024, + stereo: 1, + maxptime: 3 + }); + } + + return sdp; + }; + + if (typeof CodecsHandler !== 'undefined') { + connection.BandwidthHandler = connection.CodecsHandler = CodecsHandler; + } + + connection.mediaConstraints = { + audio: { + mandatory: {}, + optional: connection.bandwidth.audio ? [{ + bandwidth: connection.bandwidth.audio * 8 * 1024 || 128 * 8 * 1024 + }] : [] + }, + video: { + mandatory: {}, + optional: connection.bandwidth.video ? [{ + bandwidth: connection.bandwidth.video * 8 * 1024 || 128 * 8 * 1024 + }, { + facingMode: 'user' + }] : [{ + facingMode: 'user' + }] + } + }; + + if (DetectRTC.browser.name === 'Firefox') { + connection.mediaConstraints = { + audio: true, + video: true + }; + } + + if (!forceOptions.useDefaultDevices && !DetectRTC.isMobileDevice) { + DetectRTC.load(function() { + var lastAudioDevice, lastVideoDevice; + // it will force RTCMultiConnection to capture last-devices + // i.e. if external microphone is attached to system, we should prefer it over built-in devices. + DetectRTC.MediaDevices.forEach(function(device) { + if (device.kind === 'audioinput' && connection.mediaConstraints.audio !== false) { + lastAudioDevice = device; + } + + if (device.kind === 'videoinput' && connection.mediaConstraints.video !== false) { + lastVideoDevice = device; + } + }); + + if (lastAudioDevice) { + if (DetectRTC.browser.name === 'Firefox') { + if (connection.mediaConstraints.audio !== true) { + connection.mediaConstraints.audio.deviceId = lastAudioDevice.id; + } else { + connection.mediaConstraints.audio = { + deviceId: lastAudioDevice.id + } + } + return; + } + + if (connection.mediaConstraints.audio == true) { + connection.mediaConstraints.audio = { + mandatory: {}, + optional: [] + } + } + + if (!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 (DetectRTC.browser.name === 'Firefox') { + if (connection.mediaConstraints.video !== true) { + connection.mediaConstraints.video.deviceId = lastVideoDevice.id; + } else { + connection.mediaConstraints.video = { + deviceId: lastVideoDevice.id + } + } + return; + } + + if (connection.mediaConstraints.video == true) { + connection.mediaConstraints.video = { + mandatory: {}, + optional: [] + } + } + + if (!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: true, + OfferToReceiveVideo: true + }, + optional: [{ + VoiceActivityDetection: false + }] + }; + + connection.sdpSemantics = null; // "unified-plan" or "plan-b", ref: webrtc.org/web-apis/chrome/unified-plan/ + connection.iceCandidatePoolSize = null; // 0 + connection.bundlePolicy = null; // max-bundle + connection.rtcpMuxPolicy = null; // "require" or "negotiate" + connection.iceTransportPolicy = null; // "relay" or "all" + connection.optionalArgument = { + optional: [{ + DtlsSrtpKeyAgreement: true + }, { + googImprovedWifiBwe: true + }, { + googScreencastMinBitrate: 300 + }, { + googIPv6: true + }, { + googDscp: true + }, { + googCpuUnderuseThreshold: 55 + }, { + googCpuOveruseThreshold: 85 + }, { + googSuspendBelowMinBitrate: true + }, { + googCpuOveruseDetection: true + }], + mandatory: {} + }; + + connection.iceServers = IceServersHandler.getIceServers(connection); + + connection.candidates = { + host: true, + stun: true, + turn: true + }; + + connection.iceProtocols = { + tcp: true, + udp: true + }; + + // EVENTs + connection.onopen = function(event) { + if (!!connection.enableLogs) { + console.info('Data connection has been opened between you & ', event.userid); + } + }; + + connection.onclose = function(event) { + if (!!connection.enableLogs) { + console.warn('Data connection has been closed between you & ', event.userid); + } + }; + + connection.onerror = function(error) { + if (!!connection.enableLogs) { + console.error(error.userid, 'data-error', error); + } + }; + + connection.onmessage = function(event) { + if (!!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() { + connection.onbeforeunload(false, true); + }; + + connection.closeEntireSession = function(callback) { + callback = callback || function() {}; + connection.socket.emit('close-entire-session', function looper() { + if (connection.getAllParticipants().length) { + setTimeout(looper, 100); + return; + } + + connection.onEntireSessionClosed({ + sessionid: connection.sessionid, + userid: connection.userid, + extra: connection.extra + }); + + connection.changeUserId(null, function() { + connection.close(); + callback(); + }); + }); + }; + + connection.onEntireSessionClosed = function(event) { + if (!connection.enableLogs) return; + 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(); + + if (typeof played !== 'undefined') { + played.catch(function() { + /*** iOS 11 doesn't allow automatic play and rejects ***/ + }).then(function() { + setTimeout(function() { + e.mediaElement.play(); + }, 2000); + }); + return; + } + + setTimeout(function() { + e.mediaElement.play(); + }, 2000); + }; + + connection.onstreamended = function(e) { + if (!e.mediaElement) { + e.mediaElement = document.getElementById(e.streamid); + } + + if (!e.mediaElement || !e.mediaElement.parentNode) { + return; + } + + e.mediaElement.parentNode.removeChild(e.mediaElement); + }; + + connection.direction = 'many-to-many'; + + connection.removeStream = function(streamid, remoteUserId) { + var stream; + connection.attachStreams.forEach(function(localStream) { + if (localStream.id === streamid) { + stream = localStream; + } + }); + + if (!stream) { + console.warn('No such stream exist.', streamid); + return; + } + + connection.peers.getAllParticipants().forEach(function(participant) { + if (remoteUserId && participant !== remoteUserId) { + return; + } + + var user = connection.peers[participant]; + try { + user.peer.removeStream(stream); + } catch (e) {} + }); + + connection.renegotiate(); + }; + + connection.addStream = function(session, remoteUserId) { + if (!!session.getTracks) { + if (connection.attachStreams.indexOf(session) === -1) { + if (!session.streamid) { + session.streamid = session.id; + } + + connection.attachStreams.push(session); + } + connection.renegotiate(remoteUserId); + return; + } + + if (isData(session)) { + connection.renegotiate(remoteUserId); + return; + } + + if (session.audio || session.video || session.screen) { + if (session.screen) { + if (DetectRTC.browser.name === 'Edge') { + navigator.getDisplayMedia({ + video: true, + audio: isAudioPlusTab(connection) + }).then(function(screen) { + screen.isScreen = true; + mPeer.onGettingLocalMedia(screen); + + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + connection.invokeGetUserMedia(null, function(stream) { + gumCallback(stream); + }); + } else { + gumCallback(screen); + } + }, function(error) { + console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.'); + }); + } else { + connection.invokeGetUserMedia({ + audio: isAudioPlusTab(connection), + video: true, + isScreen: true + }, function(stream) { + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + connection.invokeGetUserMedia(null, function(stream) { + gumCallback(stream); + }); + } else { + gumCallback(stream); + } + }); + } + } else if (session.audio || session.video) { + connection.invokeGetUserMedia(null, gumCallback); + } + } + + function gumCallback(stream) { + if (session.streamCallback) { + session.streamCallback(stream); + } + + connection.renegotiate(remoteUserId); + } + }; + + connection.invokeGetUserMedia = function(localMediaConstraints, callback, session) { + if (!session) { + session = connection.session; + } + + if (!localMediaConstraints) { + localMediaConstraints = connection.mediaConstraints; + } + + getUserMediaHandler({ + onGettingLocalMedia: function(stream) { + var videoConstraints = localMediaConstraints.video; + if (videoConstraints) { + if (videoConstraints.mediaSource || videoConstraints.mozMediaSource) { + stream.isScreen = true; + } else if (videoConstraints.mandatory && videoConstraints.mandatory.chromeMediaSource) { + stream.isScreen = true; + } + } + + if (!stream.isScreen) { + stream.isVideo = !!getTracks(stream, 'video').length; + stream.isAudio = !stream.isVideo && getTracks(stream, 'audio').length; + } + + mPeer.onGettingLocalMedia(stream, function() { + if (typeof callback === 'function') { + callback(stream); + } + }); + }, + onLocalMediaError: function(error, constraints) { + mPeer.onLocalMediaError(error, constraints); + }, + localMediaConstraints: localMediaConstraints || { + audio: session.audio ? localMediaConstraints.audio : false, + video: session.video ? localMediaConstraints.video : false + } + }); + }; + + function applyConstraints(stream, mediaConstraints) { + if (!stream) { + if (!!connection.enableLogs) { + console.error('No stream to applyConstraints.'); + } + return; + } + + if (mediaConstraints.audio) { + getTracks(stream, 'audio').forEach(function(track) { + track.applyConstraints(mediaConstraints.audio); + }); + } + + if (mediaConstraints.video) { + getTracks(stream, 'video').forEach(function(track) { + track.applyConstraints(mediaConstraints.video); + }); + } + } + + connection.applyConstraints = function(mediaConstraints, streamid) { + if (!MediaStreamTrack || !MediaStreamTrack.prototype.applyConstraints) { + alert('track.applyConstraints is NOT supported in your browser.'); + return; + } + + if (streamid) { + var stream; + if (connection.streamEvents[streamid]) { + stream = connection.streamEvents[streamid].stream; + } + applyConstraints(stream, mediaConstraints); + return; + } + + connection.attachStreams.forEach(function(stream) { + applyConstraints(stream, mediaConstraints); + }); + }; + + function replaceTrack(track, remoteUserId, isVideoTrack) { + if (remoteUserId) { + mPeer.replaceTrack(track, remoteUserId, isVideoTrack); + return; + } + + connection.peers.getAllParticipants().forEach(function(participant) { + mPeer.replaceTrack(track, participant, isVideoTrack); + }); + } + + connection.replaceTrack = function(session, remoteUserId, isVideoTrack) { + session = session || {}; + + if (!RTCPeerConnection.prototype.getSenders) { + connection.addStream(session); + return; + } + + if (session instanceof MediaStreamTrack) { + replaceTrack(session, remoteUserId, isVideoTrack); + return; + } + + if (session instanceof MediaStream) { + if (getTracks(session, 'video').length) { + replaceTrack(getTracks(session, 'video')[0], remoteUserId, true); + } + + if (getTracks(session, 'audio').length) { + replaceTrack(getTracks(session, 'audio')[0], remoteUserId, false); + } + return; + } + + if (isData(session)) { + throw 'connection.replaceTrack requires audio and/or video and/or screen.'; + return; + } + + if (session.audio || session.video || session.screen) { + if (session.screen) { + if (DetectRTC.browser.name === 'Edge') { + navigator.getDisplayMedia({ + video: true, + audio: isAudioPlusTab(connection) + }).then(function(screen) { + screen.isScreen = true; + mPeer.onGettingLocalMedia(screen); + + if ((session.audio || session.video) && !isAudioPlusTab(connection)) { + connection.invokeGetUserMedia(null, gumCallback); + } else { + gumCallback(screen); + } + }, function(error) { + console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.'); + }); + } else { + connection.invokeGetUserMedia({ + audio: isAudioPlusTab(connection), + video: true, + isScreen: true + }, (session.audio || session.video) && !isAudioPlusTab(connection) ? connection.invokeGetUserMedia(null, gumCallback) : gumCallback); + } + } else if (session.audio || session.video) { + connection.invokeGetUserMedia(null, gumCallback); + } + } + + function gumCallback(stream) { + connection.replaceTrack(stream, remoteUserId, isVideoTrack || session.video || session.screen); + } + }; + + connection.resetTrack = function(remoteUsersIds, isVideoTrack) { + if (!remoteUsersIds) { + remoteUsersIds = connection.getAllParticipants(); + } + + if (typeof remoteUsersIds == 'string') { + remoteUsersIds = [remoteUsersIds]; + } + + remoteUsersIds.forEach(function(participant) { + var peer = connection.peers[participant].peer; + + if ((typeof isVideoTrack === 'undefined' || isVideoTrack === true) && peer.lastVideoTrack) { + connection.replaceTrack(peer.lastVideoTrack, participant, true); + } + + if ((typeof isVideoTrack === 'undefined' || isVideoTrack === false) && peer.lastAudioTrack) { + connection.replaceTrack(peer.lastAudioTrack, participant, false); + } + }); + }; + + connection.renegotiate = function(remoteUserId) { + if (remoteUserId) { + mPeer.renegotiatePeer(remoteUserId); + return; + } + + connection.peers.getAllParticipants().forEach(function(participant) { + mPeer.renegotiatePeer(participant); + }); + }; + + connection.setStreamEndHandler = function(stream, isRemote) { + if (!stream || !stream.addEventListener) return; + + isRemote = !!isRemote; + + if (stream.alreadySetEndHandler) { + return; + } + stream.alreadySetEndHandler = true; + + var streamEndedEvent = 'ended'; + + if ('oninactive' in stream) { + streamEndedEvent = 'inactive'; + } + + stream.addEventListener(streamEndedEvent, function() { + if (stream.idInstance) { + currentUserMediaRequest.remove(stream.idInstance); + } + + if (!isRemote) { + // reset attachStreams + var streams = []; + connection.attachStreams.forEach(function(s) { + if (s.id != stream.id) { + streams.push(s); + } + }); + connection.attachStreams = streams; + } + + // connection.renegotiate(); + + 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 + }; + } + + if (isRemote && connection.peers[streamEvent.userid]) { + // reset remote "streams" + var peer = connection.peers[streamEvent.userid].peer; + var streams = []; + peer.getRemoteStreams().forEach(function(s) { + if (s.id != stream.id) { + streams.push(s); + } + }); + connection.peers[streamEvent.userid].streams = streams; + } + + if (streamEvent.userid === connection.userid && streamEvent.type === 'remote') { + return; + } + + if (connection.peersBackup[streamEvent.userid]) { + streamEvent.extra = connection.peersBackup[streamEvent.userid].extra; + } + + connection.onstreamended(streamEvent); + + delete connection.streamEvents[stream.streamid]; + }, false); + }; + + connection.onMediaError = function(error, constraints) { + if (!!connection.enableLogs) { + console.error(error, constraints); + } + }; + + connection.autoCloseEntireSession = false; + + connection.filesContainer = connection.videosContainer = document.body || document.documentElement; + connection.isInitiator = false; + + connection.shareFile = mPeer.shareFile; + if (typeof FileProgressBarHandler !== 'undefined') { + FileProgressBarHandler.handle(connection); + } + + if (typeof TranslationHandler !== 'undefined') { + TranslationHandler.handle(connection); + } + + connection.token = getRandomString; + + connection.onNewParticipant = function(participantId, userPreferences) { + connection.acceptParticipationRequest(participantId, userPreferences); + }; + + connection.acceptParticipationRequest = function(participantId, userPreferences) { + if (userPreferences.successCallback) { + userPreferences.successCallback(); + delete userPreferences.successCallback; + } + + mPeer.createNewPeer(participantId, userPreferences); + }; + + if (typeof StreamsHandler !== 'undefined') { + connection.StreamsHandler = StreamsHandler; + } + + connection.onleave = function(userid) {}; + + connection.invokeSelectFileDialog = function(callback) { + var selector = new FileSelector(); + selector.accept = '*.*'; + selector.selectSingleFile(callback); + }; + + connection.onmute = function(e) { + if (!e || !e.mediaElement) { + return; + } + + if (e.muteType === 'both' || e.muteType === 'video') { + e.mediaElement.src = null; + var paused = e.mediaElement.pause(); + if (typeof paused !== 'undefined') { + paused.then(function() { + e.mediaElement.poster = e.snapshot || 'https://cdn.webrtc-experiment.com/images/muted.png'; + }); + } else { + e.mediaElement.poster = e.snapshot || 'https://cdn.webrtc-experiment.com/images/muted.png'; + } + } else if (e.muteType === 'audio') { + e.mediaElement.muted = true; + } + }; + + connection.onunmute = function(e) { + if (!e || !e.mediaElement || !e.stream) { + return; + } + + if (e.unmuteType === 'both' || e.unmuteType === 'video') { + e.mediaElement.poster = null; + e.mediaElement.srcObject = e.stream; + e.mediaElement.play(); + } else if (e.unmuteType === 'audio') { + e.mediaElement.muted = false; + } + }; + + connection.onExtraDataUpdated = function(event) { + event.status = 'online'; + connection.onUserStatusChanged(event, true); + }; + + connection.getAllParticipants = function(sender) { + return connection.peers.getAllParticipants(sender); + }; + + if (typeof StreamsHandler !== 'undefined') { + StreamsHandler.onSyncNeeded = function(streamid, action, type) { + connection.peers.getAllParticipants().forEach(function(participant) { + mPeer.onNegotiationNeeded({ + streamid: streamid, + action: action, + streamSyncNeeded: true, + type: type || 'both' + }, participant); + }); + }; + } + + connection.connectSocket = function(callback) { + connectSocket(callback); + }; + + connection.closeSocket = function() { + try { + io.sockets = {}; + } catch (e) {}; + + if (!connection.socket) return; + + if (typeof connection.socket.disconnect === 'function') { + connection.socket.disconnect(); + } + + if (typeof connection.socket.resetProps === 'function') { + connection.socket.resetProps(); + } + + connection.socket = null; + }; + + connection.getSocket = function(callback) { + if (!callback && connection.enableLogs) { + console.warn('getSocket.callback paramter is required.'); + } + + callback = callback || function() {}; + + if (!connection.socket) { + connectSocket(function() { + callback(connection.socket); + }); + } else { + callback(connection.socket); + } + + return connection.socket; // callback is preferred over return-statement + }; + + connection.getRemoteStreams = mPeer.getRemoteStreams; + + var skipStreams = ['selectFirst', 'selectAll', 'forEach']; + + connection.streamEvents = { + selectFirst: function(options) { + return connection.streamEvents.selectAll(options)[0]; + }, + selectAll: function(options) { + if (!options) { + // default will always be all streams + options = { + local: true, + remote: true, + isScreen: true, + isAudio: true, + isVideo: true + }; + } + + if (options == 'local') { + options = { + local: true + }; + } + + if (options == 'remote') { + options = { + remote: true + }; + } + + if (options == 'screen') { + options = { + isScreen: true + }; + } + + if (options == 'audio') { + options = { + isAudio: true + }; + } + + if (options == 'video') { + options = { + isVideo: true + }; + } + + var streams = []; + Object.keys(connection.streamEvents).forEach(function(key) { + var event = connection.streamEvents[key]; + + if (skipStreams.indexOf(key) !== -1) return; + var ignore = true; + + if (options.local && event.type === 'local') { + ignore = false; + } + + if (options.remote && event.type === 'remote') { + ignore = false; + } + + if (options.isScreen && event.stream.isScreen) { + ignore = false; + } + + if (options.isVideo && event.stream.isVideo) { + ignore = false; + } + + if (options.isAudio && event.stream.isAudio) { + ignore = false; + } + + if (options.userid && event.userid === options.userid) { + ignore = false; + } + + if (ignore === false) { + streams.push(event); + } + }); + + return streams; + } + }; + + connection.socketURL = '/'; // generated via config.json + connection.socketMessageEvent = 'RTCMultiConnection-Message'; // generated via config.json + connection.socketCustomEvent = 'RTCMultiConnection-Custom-Message'; // generated via config.json + connection.DetectRTC = DetectRTC; + + connection.setCustomSocketEvent = function(customEvent) { + if (customEvent) { + connection.socketCustomEvent = customEvent; + } + + if (!connection.socket) { + return; + } + + connection.socket.emit('set-custom-socket-event-listener', connection.socketCustomEvent); + }; + + connection.getNumberOfBroadcastViewers = function(broadcastId, callback) { + if (!connection.socket || !broadcastId || !callback) return; + + connection.socket.emit('get-number-of-users-in-specific-broadcast', broadcastId, callback); + }; + + connection.onNumberOfBroadcastViewersUpdated = function(event) { + if (!connection.enableLogs || !connection.isInitiator) return; + console.info('Number of broadcast (', event.broadcastId, ') viewers', event.numberOfBroadcastViewers); + }; + + connection.onUserStatusChanged = function(event, dontWriteLogs) { + if (!!connection.enableLogs && !dontWriteLogs) { + console.info(event.userid, event.status); + } + }; + + connection.getUserMediaHandler = getUserMediaHandler; + connection.multiPeersHandler = mPeer; + connection.enableLogs = true; + connection.setCustomSocketHandler = function(customSocketHandler) { + if (typeof SocketConnection !== 'undefined') { + SocketConnection = customSocketHandler; + } + }; + + // default value should be 15k because [old]Firefox's receiving limit is 16k! + // however 64k works chrome-to-chrome + connection.chunkSize = 40 * 1000; + + connection.maxParticipantsAllowed = 1000; + + // eject or leave single user + connection.disconnectWith = mPeer.disconnectWith; + + // check if room exist on server + // we will pass roomid to the server and wait for callback (i.e. server's response) + connection.checkPresence = function(roomid, callback) { + roomid = roomid || connection.sessionid; + + if (SocketConnection.name === 'SSEConnection') { + SSEConnection.checkPresence(roomid, function(isRoomExist, _roomid, extra) { + if (!connection.socket) { + if (!isRoomExist) { + connection.userid = _roomid; + } + + connection.connectSocket(function() { + callback(isRoomExist, _roomid, extra); + }); + return; + } + callback(isRoomExist, _roomid); + }); + return; + } + + if (!connection.socket) { + connection.connectSocket(function() { + connection.checkPresence(roomid, callback); + }); + return; + } + + connection.socket.emit('check-presence', roomid + '', function(isRoomExist, _roomid, extra) { + if (connection.enableLogs) { + console.log('checkPresence.isRoomExist: ', isRoomExist, ' roomid: ', _roomid); + } + callback(isRoomExist, _roomid, extra); + }); + }; + + connection.onReadyForOffer = function(remoteUserId, userPreferences) { + connection.multiPeersHandler.createNewPeer(remoteUserId, userPreferences); + }; + + connection.setUserPreferences = function(userPreferences) { + if (connection.dontAttachStream) { + userPreferences.dontAttachLocalStream = true; + } + + if (connection.dontGetRemoteStream) { + userPreferences.dontGetRemoteStream = true; + } + + return userPreferences; + }; + + connection.updateExtraData = function() { + connection.socket.emit('extra-data-updated', connection.extra); + }; + + connection.enableScalableBroadcast = false; + connection.maxRelayLimitPerUser = 3; // each broadcast should serve only 3 users + + connection.dontCaptureUserMedia = false; + connection.dontAttachStream = false; + connection.dontGetRemoteStream = false; + + connection.onReConnecting = function(event) { + if (connection.enableLogs) { + console.info('ReConnecting with', event.userid, '...'); + } + }; + + connection.beforeAddingStream = function(stream) { + return stream; + }; + + connection.beforeRemovingStream = function(stream) { + return stream; + }; + + if (typeof isChromeExtensionAvailable !== 'undefined') { + connection.checkIfChromeExtensionAvailable = isChromeExtensionAvailable; + } + + if (typeof isFirefoxExtensionAvailable !== 'undefined') { + connection.checkIfChromeExtensionAvailable = isFirefoxExtensionAvailable; + } + + if (typeof getChromeExtensionStatus !== 'undefined') { + connection.getChromeExtensionStatus = getChromeExtensionStatus; + } + + connection.modifyScreenConstraints = function(screen_constraints) { + return screen_constraints; + }; + + connection.onPeerStateChanged = function(state) { + if (connection.enableLogs) { + if (state.iceConnectionState.search(/closed|failed/gi) !== -1) { + console.error('Peer connection is closed between you & ', state.userid, state.extra, 'state:', state.iceConnectionState); + } + } + }; + + connection.isOnline = true; + + listenEventHandler('online', function() { + connection.isOnline = true; + }); + + listenEventHandler('offline', function() { + connection.isOnline = false; + }); + + connection.isLowBandwidth = false; + if (navigator && navigator.connection && navigator.connection.type) { + connection.isLowBandwidth = navigator.connection.type.toString().toLowerCase().search(/wifi|cell/g) !== -1; + if (connection.isLowBandwidth) { + connection.bandwidth = { + audio: false, + video: false, + screen: false + }; + + if (connection.mediaConstraints.audio && connection.mediaConstraints.audio.optional && connection.mediaConstraints.audio.optional.length) { + var newArray = []; + connection.mediaConstraints.audio.optional.forEach(function(opt) { + if (typeof opt.bandwidth === 'undefined') { + 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) { + if (typeof opt.bandwidth === 'undefined') { + newArray.push(opt); + } + }); + connection.mediaConstraints.video.optional = newArray; + } + } + } + + connection.getExtraData = function(remoteUserId, callback) { + if (!remoteUserId) throw 'remoteUserId is required.'; + + if (typeof callback === 'function') { + connection.socket.emit('get-remote-user-extra-data', remoteUserId, function(extra, remoteUserId, error) { + callback(extra, remoteUserId, error); + }); + return; + } + + if (!connection.peers[remoteUserId]) { + if (connection.peersBackup[remoteUserId]) { + return connection.peersBackup[remoteUserId].extra; + } + return {}; + } + + return connection.peers[remoteUserId].extra; + }; + + if (!!forceOptions.autoOpenOrJoin) { + connection.openOrJoin(connection.sessionid); + } + + connection.onUserIdAlreadyTaken = function(useridAlreadyTaken, yourNewUserId) { + // via #683 + connection.close(); + connection.closeSocket(); + + connection.isInitiator = false; + connection.userid = connection.token(); + + connection.join(connection.sessionid); + + if (connection.enableLogs) { + console.warn('Userid already taken.', useridAlreadyTaken, 'Your new userid:', connection.userid); + } + }; + + connection.trickleIce = true; + connection.version = '3.6.9'; + + connection.onSettingLocalDescription = function(event) { + if (connection.enableLogs) { + console.info('Set local description for remote user', event.userid); + } + }; + + connection.resetScreen = function() { + sourceId = null; + if (DetectRTC && DetectRTC.screen) { + delete DetectRTC.screen.sourceId; + } + + currentUserMediaRequest = { + streams: [], + mutex: false, + queueRequests: [] + }; + }; + + // if disabled, "event.mediaElement" for "onstream" will be NULL + connection.autoCreateMediaElement = true; + + // set password + connection.password = null; + + // set password + connection.setPassword = function(password, callback) { + callback = callback || function() {}; + if (connection.socket) { + connection.socket.emit('set-password', password, callback); + } else { + connection.password = password; + callback(true, connection.sessionid, null); + } + }; + + connection.onSocketDisconnect = function(event) { + if (connection.enableLogs) { + console.warn('socket.io connection is closed'); + } + }; + + connection.onSocketError = function(event) { + if (connection.enableLogs) { + console.warn('socket.io connection is failed'); + } + }; + + // error messages + connection.errors = { + ROOM_NOT_AVAILABLE: 'Room not available', + INVALID_PASSWORD: 'Invalid password', + USERID_NOT_AVAILABLE: 'User ID does not exist', + ROOM_PERMISSION_DENIED: 'Room permission denied', + ROOM_FULL: 'Room full', + DID_NOT_JOIN_ANY_ROOM: 'Did not join any room yet', + INVALID_SOCKET: 'Invalid socket', + PUBLIC_IDENTIFIER_MISSING: 'publicRoomIdentifier is required', + INVALID_ADMIN_CREDENTIAL: 'Invalid username or password attempted' + }; + })(this); + +}; + +if (typeof module !== 'undefined' /* && !!module.exports*/ ) { + module.exports = exports = RTCMultiConnection; +} + +if (typeof define === 'function' && define.amd) { + define('RTCMultiConnection', [], function() { + return RTCMultiConnection; + }); +} diff --git a/dist/RTCMultiConnection.min.js b/dist/RTCMultiConnection.min.js new file mode 100644 index 0000000..939445d --- /dev/null +++ b/dist/RTCMultiConnection.min.js @@ -0,0 +1,18 @@ +'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 +// -------------------------------------------------- + +"use strict";var RTCMultiConnection=function(roomid,forceOptions){function SocketConnection(connection,connectCallback){function isData(session){return!session.audio&&!session.video&&!session.screen&&session.data}function updateExtraBackup(remoteUserId,extra){connection.peersBackup[remoteUserId]||(connection.peersBackup[remoteUserId]={userid:remoteUserId,extra:{}}),connection.peersBackup[remoteUserId].extra=extra}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}),updateExtraBackup(message.sender,message.extra)),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("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&&(connection.attachStreams.length&&(connection.waitingForLocalMedia=!1),connection.waitingForLocalMedia))return void setTimeout(function(){onMessageEvent(message)},1);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(){}};return void connection.onNewParticipant(message.sender,userPreferences)}return 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)),parameters+="&extra="+JSON.stringify(connection.extra||{}),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 url is: ",location.origin+"/"):console.info("socket.io url is: ",connection.socketURL));try{connection.socket=io(connection.socketURL+parameters)}catch(e){connection.socket=io.connect(connection.socketURL+parameters,connection.socketOptions)}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}),updateExtraBackup(remoteUserId,extra))}),connection.socket.on(connection.socketMessageEvent,onMessageEvent);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)},1e3),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){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){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.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("set-isInitiator-true",function(sessionid){sessionid==connection.sessionid&&(connection.isInitiator=!0)})}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.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)){if(connection.enableFileSharing)return void self.shareFile(data,remoteUserId);"string"!=typeof data&&(data=JSON.stringify(data))}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.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])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.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&&"video"===rtpSender.track.kind&&(connection.peers[remoteUserId].peer.lastVideoTrack=rtpSender.track,rtpSender.replaceTrack(track)),isVideoTrack||"audio"!==rtpSender.track.kind||(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.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){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:[]}}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&&getTracks(streamEvent.stream,"audio").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;i0?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){if(DetectRTC.isWebRTCSupported){var isPublic=!0,isIpv4=!0;getIPs(function(ip){ip?ip.match(regexIpv4Local)?(isPublic=!1,callback("Local: "+ip,isPublic,isIpv4)):ip.match(regexIpv6)?(isIpv4=!1,callback("Public: "+ip,isPublic,isIpv4)):callback("Public: "+ip,isPublic,isIpv4):callback()},stream)}}function getIPs(callback,stream){function handleCandidate(candidate){if(!candidate)return void callback();var match=regexIpv4.exec(candidate);if(match){var ipAddress=match[1],isPublic=candidate.match(regexIpv4Local),isIpv4=!0;void 0===ipDuplicates[ipAddress]&&callback(ipAddress,isPublic,isIpv4),ipDuplicates[ipAddress]=!0}}function afterCreateOffer(){var lines=pc.localDescription.sdp.split("\n");lines.forEach(function(line){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(event){event.candidate&&event.candidate.candidate?handleCandidate(event.candidate.candidate):handleCandidate()},!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 regexIpv4Local=/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/,regexIpv4=/([0-9]{1,3}(\.[0-9]{1,3}){3})/,regexIpv6=/[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}/,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.9","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",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=numberOfTimes||setTimeout(function(){callback(),afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes)},setTimeoutInteval)}return{setHandlers:setHandlers,onSyncNeeded:function(streamid,action,type){}}}(),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 joinRoom(connectionDescription,cb){connection.socket.emit("join-room",{sessionid:connection.sessionid,session:connection.session,mediaConstraints:connection.mediaConstraints,sdpConstraints:connection.sdpConstraints,streams:getStreamInfoForAdmin(),extra:connection.extra,password:"undefined"!=typeof connection.password&&"object"!=typeof connection.password?connection.password:""},function(isRoomJoined,error){if(isRoomJoined===!0){if(connection.enableLogs&&console.log("isRoomJoined: ",isRoomJoined," roomid: ",connection.sessionid),connection.peers[connection.sessionid])return;mPeer.onNegotiationNeeded(connectionDescription)}isRoomJoined===!1&&connection.enableLogs&&console.warn("isRoomJoined: ",error," roomid: ",connection.sessionid),cb(isRoomJoined,connection.sessionid,error)})}function openRoom(callback){connection.enableLogs&&console.log("Sending open-room signal to socket.io"),connection.waitingForLocalMedia=!1,connection.socket.emit("open-room",{sessionid:connection.sessionid,session:connection.session,mediaConstraints:connection.mediaConstraints,sdpConstraints:connection.sdpConstraints,streams:getStreamInfoForAdmin(),extra:connection.extra,identifier:connection.publicRoomIdentifier,password:"undefined"!=typeof connection.password&&"object"!=typeof connection.password?connection.password:""},function(isRoomOpened,error){isRoomOpened===!0&&(connection.enableLogs&&console.log("isRoomOpened: ",isRoomOpened," roomid: ",connection.sessionid),callback(isRoomOpened,connection.sessionid)),isRoomOpened===!1&&(connection.enableLogs&&console.warn("isRoomOpened: ",error," roomid: ",connection.sessionid),callback(isRoomOpened,connection.sessionid,error))})}function getStreamInfoForAdmin(){try{return connection.streamEvents.selectAll("local").map(function(event){return{streamid:event.streamid,tracks:event.stream.getTracks().length}})}catch(e){return[]}}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.invokeGetUserMedia({audio:isAudioPlusTab(connection),video:!0,isScreen:!0},!session.audio&&!session.video||isAudioPlusTab(connection)?callback:connection.invokeGetUserMedia(null,callback)):(session.audio||session.video)&&connection.invokeGetUserMedia(null,callback,session))))}function applyConstraints(stream,mediaConstraints){return stream?(mediaConstraints.audio&&getTracks(stream,"audio").forEach(function(track){track.applyConstraints(mediaConstraints.audio)}),void(mediaConstraints.video&&getTracks(stream,"video").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)})}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])return void callback();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};try{setHarkEvents(connection,connection.streamEvents[stream.streamid]),setMuteHandlers(connection,connection.streamEvents[stream.streamid]),connection.onstream(connection.streamEvents[stream.streamid])}catch(e){}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){callback=callback||function(){},remoteUserId=remoteUserId||message.remoteUserId,message=message||"";var messageToDeliver={remoteUserId:remoteUserId,message:message,sender:connection.userid};message.remoteUserId&&message.message&&message.sender&&(messageToDeliver=message),connectSocket(function(){connection.socket.emit(connection.socketMessageEvent,messageToDeliver,callback)})},mPeer.onUserLeft=onUserLeft,mPeer.disconnectWith=function(remoteUserId,callback){connection.socket&&connection.socket.emit("disconnect-with",remoteUserId,callback||function(){}),connection.deletePeer(remoteUserId)},connection.socketOptions={transport:"polling"},connection.openOrJoin=function(roomid,callback){callback=callback||function(){},connection.checkPresence(roomid,function(isRoomExist,roomid){if(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};return void beforeJoin(connectionDescription.message,function(){joinRoom(connectionDescription,callback)})}return connection.waitingForLocalMedia=!0,connection.isInitiator=!0,connection.sessionid=roomid||connection.sessionid,isData(connection.session)?void openRoom(callback):void connection.captureUserMedia(function(){openRoom(callback)})})},connection.waitingForLocalMedia=!1,connection.open=function(roomid,callback){callback=callback||function(){},connection.waitingForLocalMedia=!0,connection.isInitiator=!0,connection.sessionid=roomid||connection.sessionid,connectSocket(function(){return isData(connection.session)?void openRoom(callback):void connection.captureUserMedia(function(){openRoom(callback)})})},connection.peersBackup={},connection.deletePeer=function(remoteUserId){if(remoteUserId&&connection.peers[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])}}},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=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};return beforeJoin(connectionDescription.message,function(){connectSocket(function(){joinRoom(connectionDescription,cb)})}),connectionDescription},connection.publicRoomIdentifier="",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.invokeGetUserMedia({audio:isAudioPlusTab(connection),video:!0,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.onbeforeunload=function(arg1,dontCloseSocket){connection.closeBeforeUnload&&(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.isInitiator=!1)},window.ignoreBeforeUnload?connection.closeBeforeUnload=!1:(connection.closeBeforeUnload=!0, +window.addEventListener("beforeunload",connection.onbeforeunload,!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 isUnifiedPlanSupportedDefault()?sdp:"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.sdpSemantics=null,connection.iceCandidatePoolSize=null,connection.bundlePolicy=null,connection.rtcpMuxPolicy=null,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(){connection.onbeforeunload(!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.getTracks?(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.invokeGetUserMedia({audio:isAudioPlusTab(connection),video:!0,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=!!getTracks(stream,"video").length,stream.isAudio=!stream.isVideo&&getTracks(stream,"audio").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 getTracks(session,"video").length&&replaceTrack(getTracks(session,"video")[0],remoteUserId,!0),void(getTracks(session,"audio").length&&replaceTrack(getTracks(session,"audio")[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.invokeGetUserMedia({audio:isAudioPlusTab(connection),video:!0,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&¤tUserMediaRequest.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.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)},"undefined"!=typeof StreamsHandler&&(connection.StreamsHandler=StreamsHandler),connection.onleave=function(userid){},connection.invokeSelectFileDialog=function(callback){var selector=new FileSelector;selector.accept="*.*",selector.selectSingleFile(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.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!callback&&connection.enableLogs&&console.warn("getSocket.callback paramter is required."),callback=callback||function(){},connection.socket?callback(connection.socket):connectSocket(function(){callback(connection.socket)}),connection.socket},connection.getRemoteStreams=mPeer.getRemoteStreams;var skipStreams=["selectFirst","selectAll","forEach"];if(connection.streamEvents={selectFirst:function(options){return connection.streamEvents.selectAll(options)[0]},selectAll:function(options){options||(options={local:!0,remote:!0,isScreen:!0,isAudio:!0,isVideo:!0}),"local"==options&&(options={local:!0}),"remote"==options&&(options={remote:!0}),"screen"==options&&(options={isScreen:!0}),"audio"==options&&(options={isAudio:!0}),"video"==options&&(options={isVideo:!0});var streams=[];return Object.keys(connection.streamEvents).forEach(function(key){var event=connection.streamEvents[key];if(skipStreams.indexOf(key)===-1){var ignore=!0;options.local&&"local"===event.type&&(ignore=!1),options.remote&&"remote"===event.type&&(ignore=!1),options.isScreen&&event.stream.isScreen&&(ignore=!1),options.isVideo&&event.stream.isVideo&&(ignore=!1),options.isAudio&&event.stream.isAudio&&(ignore=!1),options.userid&&event.userid===options.userid&&(ignore=!1),ignore===!1&&streams.push(event)}}),streams}},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=4e4,connection.maxParticipantsAllowed=1e3,connection.disconnectWith=mPeer.disconnectWith,connection.checkPresence=function(roomid,callback){return roomid=roomid||connection.sessionid,"SSEConnection"===SocketConnection.name?void SSEConnection.checkPresence(roomid,function(isRoomExist,_roomid,extra){return connection.socket?void callback(isRoomExist,_roomid):(isRoomExist||(connection.userid=_roomid),void connection.connectSocket(function(){callback(isRoomExist,_roomid,extra)}))}):connection.socket?void connection.socket.emit("check-presence",roomid+"",function(isRoomExist,_roomid,extra){connection.enableLogs&&console.log("checkPresence.isRoomExist: ",isRoomExist," roomid: ",_roomid),callback(isRoomExist,_roomid,extra)}):void connection.connectSocket(function(){connection.checkPresence(roomid,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.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,callback){if(!remoteUserId)throw"remoteUserId is required.";return"function"==typeof callback?void connection.socket.emit("get-remote-user-extra-data",remoteUserId,function(extra,remoteUserId,error){callback(extra,remoteUserId,error)}):connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:connection.peersBackup[remoteUserId]?connection.peersBackup[remoteUserId].extra:{}},forceOptions.autoOpenOrJoin&&connection.openOrJoin(connection.sessionid),connection.onUserIdAlreadyTaken=function(useridAlreadyTaken,yourNewUserId){connection.close(),connection.closeSocket(),connection.isInitiator=!1,connection.userid=connection.token(),connection.join(connection.sessionid),connection.enableLogs&&console.warn("Userid already taken.",useridAlreadyTaken,"Your new userid:",connection.userid)},connection.trickleIce=!0,connection.version="3.6.9",connection.onSettingLocalDescription=function(event){connection.enableLogs&&console.info("Set local description for remote user",event.userid)},connection.resetScreen=function(){sourceId=null,DetectRTC&&DetectRTC.screen&&delete DetectRTC.screen.sourceId,currentUserMediaRequest={streams:[],mutex:!1,queueRequests:[]}},connection.autoCreateMediaElement=!0,connection.password=null,connection.setPassword=function(password,callback){callback=callback||function(){},connection.socket?connection.socket.emit("set-password",password,callback):(connection.password=password,callback(!0,connection.sessionid,null))},connection.onSocketDisconnect=function(event){connection.enableLogs&&console.warn("socket.io connection is closed")},connection.onSocketError=function(event){connection.enableLogs&&console.warn("socket.io connection is failed")},connection.errors={ROOM_NOT_AVAILABLE:"Room not available",INVALID_PASSWORD:"Invalid password",USERID_NOT_AVAILABLE:"User ID does not exist",ROOM_PERMISSION_DENIED:"Room permission denied",ROOM_FULL:"Room full",DID_NOT_JOIN_ANY_ROOM:"Did not join any room yet",INVALID_SOCKET:"Invalid socket",PUBLIC_IDENTIFIER_MISSING:"publicRoomIdentifier is required",INVALID_ADMIN_CREDENTIAL:"Invalid username or password attempted"}}(this)};"undefined"!=typeof module&&(module.exports=exports=RTCMultiConnection),"function"==typeof define&&define.amd&&define("RTCMultiConnection",[],function(){return RTCMultiConnection}); \ No newline at end of file diff --git a/home.html b/home.html new file mode 100644 index 0000000..5c3dbd1 --- /dev/null +++ b/home.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + EarthLoop About + + + + + + + + + + + + + + + + + + + +
+

+ EarthLoop +

+

Connected People for Networked Music

+
+ + + +
+ +
+

+

+ EarthLoop is a collaborative platform for sound and music experimentation +
using real-time communication +

+

+
+ +

Go to "Play"... and open or join a new session

+
+ +
+
© Laurent Di Biase - 2020
+
+ + \ No newline at end of file diff --git a/imgs/favicon.ico b/imgs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..304dab49c898128b82310a2583056a6d746faa50 GIT binary patch literal 128245 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 Y|699k&o@M6i~s-t07*qoM6N<$g1c33pa1{> literal 0 HcmV?d00001 diff --git a/imgs/menu-icon.png b/imgs/menu-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a2eac03dd472fd3acd2e435281c75be5c52b2786 GIT binary patch literal 3518 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

>~`9i3K=VM`Ne40V?W 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 z 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

IA5crfl8H7=c=XsIFz*EG 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..bb7b758 --- /dev/null +++ b/index.html @@ -0,0 +1,69 @@ + + + + + + + + + + + EarthLoop + + + + + + + + + + + + + + + + +

+ EarthLoop + +

EarthLoop

+
+
+
Laurent Di Biase - 2020 +
+
+ + + \ No newline at end of file diff --git a/js/bootstrap.min.js b/js/bootstrap.min.js new file mode 100644 index 0000000..00c895f --- /dev/null +++ b/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,u,s,l,c,f,d,p,h,g,v,y,m,b,x="sizzle"+1*new Date,w=e.document,C=0,T=0,E=ae(),N=ae(),k=ae(),A=function(e,t){return e===t&&(f=!0),0},D={}.hasOwnProperty,S=[],L=S.pop,j=S.push,q=S.push,O=S.slice,P=function(e,t){for(var n=0,r=e.length;n+~]|"+I+")"+I+"*"),_=new RegExp("="+I+"*([^\\]'\"]*?)"+I+"*\\]","g"),U=new RegExp(M),V=new RegExp("^"+R+"$"),X={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+B),PSEUDO:new RegExp("^"+M),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+I+"*(even|odd|(([+-]|)(\\d*)n|)"+I+"*(?:([+-]|)"+I+"*(\\d+)|))"+I+"*\\)|)","i"),bool:new RegExp("^(?:"+H+")$","i"),needsContext:new RegExp("^"+I+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+I+"*((?:-\\d)?\\d*)"+I+"*\\)|)(?=[^-]|$)","i")},Q=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,G=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,J=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+I+"?|("+I+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){d()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{q.apply(S=O.call(w.childNodes),w.childNodes),S[w.childNodes.length].nodeType}catch(e){q={apply:S.length?function(e,t){j.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,u,l,c,f,h,y,m=t&&t.ownerDocument,C=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==C&&9!==C&&11!==C)return r;if(!i&&((t?t.ownerDocument||t:w)!==p&&d(t),t=t||p,g)){if(11!==C&&(f=K.exec(e)))if(o=f[1]){if(9===C){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&b(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return q.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return q.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!k[e+" "]&&(!v||!v.test(e))){if(1!==C)m=t,y=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=x),u=(h=a(e)).length;while(u--)h[u]="#"+c+" "+ye(h[u]);y=h.join(","),m=J.test(e)&&ge(t.parentNode)||t}if(y)try{return q.apply(r,m.querySelectorAll(y)),r}catch(e){}finally{c===x&&t.removeAttribute("id")}}}return s(e.replace($,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function ue(e){return e[x]=!0,e}function se(e){var t=p.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function de(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pe(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return ue(function(t){return t=+t,ue(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},d=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==p&&9===a.nodeType&&a.documentElement?(p=a,h=p.documentElement,g=!o(p),w!==p&&(i=p.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=se(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=se(function(e){return e.appendChild(p.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=G.test(p.getElementsByClassName),n.getById=se(function(e){return h.appendChild(e).id=x,!p.getElementsByName||!p.getElementsByName(x).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},y=[],v=[],(n.qsa=G.test(p.querySelectorAll))&&(se(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+I+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+I+"*(?:value|"+H+")"),e.querySelectorAll("[id~="+x+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+x+"+*").length||v.push(".#.+[+~]")}),se(function(e){e.innerHTML="";var t=p.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+I+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(n.matchesSelector=G.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&se(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),y.push("!=",M)}),v=v.length&&new RegExp(v.join("|")),y=y.length&&new RegExp(y.join("|")),t=G.test(h.compareDocumentPosition),b=t||G.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===p||e.ownerDocument===w&&b(w,e)?-1:t===p||t.ownerDocument===w&&b(w,t)?1:c?P(c,e)-P(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],u=[t];if(!i||!o)return e===p?-1:t===p?1:i?-1:o?1:c?P(c,e)-P(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)u.unshift(n);while(a[r]===u[r])r++;return r?ce(a[r],u[r]):a[r]===w?-1:u[r]===w?1:0},p):p},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&d(e),t=t.replace(_,"='$1']"),n.matchesSelector&&g&&!k[t+" "]&&(!y||!y.test(t))&&(!v||!v.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,p,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==p&&d(e),b(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==p&&d(e);var i=r.attrHandle[t.toLowerCase()],o=i&&D.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(A),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:ue,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return X.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&U.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+I+")"+e+"("+I+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(W," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),u="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,s){var l,c,f,d,p,h,g=o!==a?"nextSibling":"previousSibling",v=t.parentNode,y=u&&t.nodeName.toLowerCase(),m=!s&&!u,b=!1;if(v){if(o){while(g){d=t;while(d=d[g])if(u?d.nodeName.toLowerCase()===y:1===d.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?v.firstChild:v.lastChild],a&&m){b=(p=(l=(c=(f=(d=v)[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===C&&l[1])&&l[2],d=p&&v.childNodes[p];while(d=++p&&d&&d[g]||(b=p=0)||h.pop())if(1===d.nodeType&&++b&&d===t){c[e]=[C,p,b];break}}else if(m&&(b=p=(l=(c=(f=(d=t)[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]||[])[0]===C&&l[1]),!1===b)while(d=++p&&d&&d[g]||(b=p=0)||h.pop())if((u?d.nodeName.toLowerCase()===y:1===d.nodeType)&&++b&&(m&&((c=(f=d[x]||(d[x]={}))[d.uniqueID]||(f[d.uniqueID]={}))[e]=[C,b]),d===t))break;return(b-=i)===r||b%r==0&&b/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[x]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?ue(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=P(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:ue(function(e){var t=[],n=[],r=u(e.replace($,"$1"));return r[x]?ue(function(e,t,n,i){var o,a=r(e,null,i,[]),u=e.length;while(u--)(o=a[u])&&(e[u]=!(t[u]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:ue(function(e){return function(t){return oe(e,t).length>0}}),contains:ue(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:ue(function(e){return V.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:pe(!1),disabled:pe(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xe(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else y=we(y===a?y.splice(h,y.length):y),i?i(null,a,y,s):q.apply(a,y)})}function Te(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],u=a||r.relative[" "],s=a?1:0,c=me(function(e){return e===t},u,!0),f=me(function(e){return P(t,e)>-1},u,!0),d=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];s1&&be(d),s>1&&ye(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),n,s0,i=e.length>0,o=function(o,a,u,s,c){var f,h,v,y=0,m="0",b=o&&[],x=[],w=l,T=o||i&&r.find.TAG("*",c),E=C+=null==w?1:Math.random()||.1,N=T.length;for(c&&(l=a===p||a||c);m!==N&&null!=(f=T[m]);m++){if(i&&f){h=0,a||f.ownerDocument===p||(d(f),u=!g);while(v=e[h++])if(v(f,a||p,u)){s.push(f);break}c&&(C=E)}n&&((f=!v&&f)&&y--,o&&b.push(f))}if(y+=m,n&&m!==y){h=0;while(v=t[h++])v(b,x,a,u);if(o){if(y>0)while(m--)b[m]||x[m]||(x[m]=L.call(s));x=we(x)}q.apply(s,x),c&&!o&&x.length>0&&y+t.length>1&&oe.uniqueSort(s)}return c&&(C=E,l=w),b};return n?ue(o):o}return u=oe.compile=function(e,t){var n,r=[],i=[],o=k[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Te(t[n]))[x]?r.push(o):i.push(o);(o=k(e,Ee(i,r))).selector=e}return o},s=oe.select=function(e,t,n,i){var o,s,l,c,f,d="function"==typeof e&&e,p=!i&&a(e=d.selector||e);if(n=n||[],1===p.length){if((s=p[0]=p[0].slice(0)).length>2&&"ID"===(l=s[0]).type&&9===t.nodeType&&g&&r.relative[s[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;d&&(t=t.parentNode),e=e.slice(s.shift().value.length)}o=X.needsContext.test(e)?0:s.length;while(o--){if(l=s[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),J.test(s[0].type)&&ge(t.parentNode)||t))){if(s.splice(o,1),!(e=i.length&&ye(s)))return q.apply(n,i),n;break}}}return(d||u(e,p))(i,t,!g,n,!t||J.test(e)&&ge(t.parentNode)||t),n},n.sortStable=x.split("").sort(A).join("")===x,n.detectDuplicates=!!f,d(),n.sortDetached=se(function(e){return 1&e.compareDocumentPosition(p.createElement("fieldset"))}),se(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&se(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),se(function(e){return null==e.getAttribute("disabled")})||le(H,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var N=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},k=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},A=w.expr.match.needsContext;function D(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var S=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return s.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(L(this,e||[],!1))},not:function(e){return this.pushStack(L(this,e||[],!0))},is:function(e){return!!L(this,"string"==typeof e&&A.test(e)?w(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:q.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),S.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,j=w(r);var O=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?s.call(w(e),this[0]):s.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function H(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return N(e,"parentNode")},parentsUntil:function(e,t,n){return N(e,"parentNode",n)},next:function(e){return H(e,"nextSibling")},prev:function(e){return H(e,"previousSibling")},nextAll:function(e){return N(e,"nextSibling")},prevAll:function(e){return N(e,"previousSibling")},nextUntil:function(e,t,n){return N(e,"nextSibling",n)},prevUntil:function(e,t,n){return N(e,"previousSibling",n)},siblings:function(e){return k((e.parentNode||{}).firstChild,e)},children:function(e){return k(e.firstChild)},contents:function(e){return D(e,"iframe")?e.contentDocument:(D(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(P[e]||w.uniqueSort(i),O.test(e)&&i.reverse()),this.pushStack(i)}});var I=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(I)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],u=-1,s=function(){for(i=i||e.once,r=t=!0;a.length;u=-1){n=a.shift();while(++u-1)o.splice(n,1),n<=u&&u--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||s()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function B(e){return e}function M(e){throw e}function W(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var u=this,s=arguments,l=function(){var e,l;if(!(t=o&&(r!==M&&(u=void 0,s=[e]),n.rejectWith(u,s))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:B,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:B)),n[2][3].add(a(0,e,g(r)?r:M))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],u=t[5];i[t[1]]=a.add,u&&a.add(function(){r=u},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),u=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&(W(e,a.done(u(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)W(i[n],u(n),a.reject);return a.promise()}});var $=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&$.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function z(){r.removeEventListener("DOMContentLoaded",z),e.removeEventListener("load",z),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",z),e.addEventListener("load",z));var _=function(e,t,n,r,i,o,a){var u=0,s=e.length,l=null==n;if("object"===b(n)){i=!0;for(u in n)_(e,t,u,n[u],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;u1,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:w.Callbacks("once memory").add(function(){K.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&D(e,t)?w.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var xe=r.documentElement,we=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function Ne(){return!1}function ke(){try{return r.activeElement}catch(e){}}function Ae(e,t,n,r,i,o){var a,u;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(u in t)Ae(e,u,n,r,t[u],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,u,s,l,c,f,d,p,h,g,v=K.get(e);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(xe,i),n.guid||(n.guid=w.guid++),(s=v.events)||(s=v.events={}),(a=v.handle)||(a=v.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(I)||[""]).length;while(l--)p=g=(u=Te.exec(t[l])||[])[1],h=(u[2]||"").split(".").sort(),p&&(f=w.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=w.event.special[p]||{},c=w.extend({type:p,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=s[p])||((d=s[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(p,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),w.event.global[p]=!0)}},remove:function(e,t,n,r,i){var o,a,u,s,l,c,f,d,p,h,g,v=K.hasData(e)&&K.get(e);if(v&&(s=v.events)){l=(t=(t||"").match(I)||[""]).length;while(l--)if(u=Te.exec(t[l])||[],p=g=u[1],h=(u[2]||"").split(".").sort(),p){f=w.event.special[p]||{},d=s[p=(r?f.delegateType:f.bindType)||p]||[],u=u[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;while(o--)c=d[o],!i&&g!==c.origType||n&&n.guid!==c.guid||u&&!u.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||w.removeEvent(e,p,v.handle),delete s[p])}else for(p in s)w.event.remove(e,p+t[l],n,r,!0);w.isEmptyObject(s)&&K.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,u,s=new Array(arguments.length),l=(K.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(s[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&u.push({elem:l,handlers:o})}return l=this,s\x20\t\r\n\f]*)[^>]*)\/>/gi,Se=/\s*$/g;function qe(e,t){return D(e,"table")&&D(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function Oe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Pe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function He(e,t){var n,r,i,o,a,u,s,l;if(1===t.nodeType){if(K.hasData(e)&&(o=K.access(e),a=K.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof v&&!h.checkClone&&Le.test(v))return e.each(function(i){var o=e.eq(i);y&&(t[0]=v.call(this,i,o.html())),Re(o,t,n,r)});if(d&&(i=be(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(s=(u=w.map(ve(i,"script"),Oe)).length;f")},clone:function(e,t,n){var r,i,o,a,u=e.cloneNode(!0),s=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ve(u),r=0,i=(o=ve(e)).length;r0&&ye(a,!s&&ve(e,"script")),u},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return _(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return _(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Se.test(e)&&!ge[(pe.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(s+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-s-u-.5))),s}function et(e,t,n){var r=We(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(Me.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,u=Q(t),s=Ue.test(t),l=e.style;if(s||(t=Ke(u)),a=w.cssHooks[t]||w.cssHooks[u],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[u]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(s?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,u=Q(t);return Ue.test(t)||(t=Ke(u)),(a=w.cssHooks[t]||w.cssHooks[u])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Xe&&(i=Xe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!_e.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):ue(e,Ve,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=We(e),a="border-box"===w.css(e,"boxSizing",!1,o),u=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(u-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),u&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Je(e,n,u)}}}),w.cssHooks.marginLeft=ze(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Je)}),w.fn.extend({css:function(e,t){return _(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=We(e),i=t.length;a1)}}),w.fn.delay=function(t,n){return t=w.fx?w.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=r.createElement("input"),t=r.createElement("select").appendChild(r.createElement("option"));e.type="checkbox",h.checkOn=""!==e.value,h.optSelected=t.selected,(e=r.createElement("input")).value="t",e.type="radio",h.radioValue="t"===e.value}();var tt,nt=w.expr.attrHandle;w.fn.extend({attr:function(e,t){return _(this,w.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?tt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&D(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(I);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),tt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=nt[t]||w.find.attr;nt[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=nt[a],nt[a]=i,i=null!=n(e,t,r)?a:null,nt[a]=o),i}});var rt=/^(?:input|select|textarea|button)$/i,it=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return _(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):rt.test(e.nodeName)||it.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function ot(e){return(e.match(I)||[]).join(" ")}function at(e){return e.getAttribute&&e.getAttribute("class")||""}function ut(e){return Array.isArray(e)?e:"string"==typeof e?e.match(I)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,u,s=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,at(this)))});if((t=ut(e)).length)while(n=this[s++])if(i=at(n),r=1===n.nodeType&&" "+ot(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(u=ot(r))&&n.setAttribute("class",u)}return this},removeClass:function(e){var t,n,r,i,o,a,u,s=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,at(this)))});if(!arguments.length)return this.attr("class","");if((t=ut(e)).length)while(n=this[s++])if(i=at(n),r=1===n.nodeType&&" "+ot(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(u=ot(r))&&n.setAttribute("class",u)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,at(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=ut(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=at(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+ot(at(n))+" ").indexOf(t)>-1)return!0;return!1}});var st=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(st,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:ot(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,u=a?null:[],s=a?o+1:i.length;for(r=o<0?s:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var lt=/^(?:focusinfocus|focusoutblur)$/,ct=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,u,s,l,c,d,p,h,y=[i||r],m=f.call(t,"type")?t.type:t,b=f.call(t,"namespace")?t.namespace.split("."):[];if(u=h=s=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!lt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(b=m.split(".")).shift(),b.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=b.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),p=w.event.special[m]||{},o||!p.trigger||!1!==p.trigger.apply(i,n))){if(!o&&!p.noBubble&&!v(i)){for(l=p.delegateType||m,lt.test(l+m)||(u=u.parentNode);u;u=u.parentNode)y.push(u),s=u;s===(i.ownerDocument||r)&&y.push(s.defaultView||s.parentWindow||e)}a=0;while((u=y[a++])&&!t.isPropagationStopped())h=u,t.type=a>1?l:p.bindType||m,(d=(K.get(u,"events")||{})[t.type]&&K.get(u,"handle"))&&d.apply(u,n),(d=c&&u[c])&&d.apply&&Y(u)&&(t.result=d.apply(u,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||p._default&&!1!==p._default.apply(y.pop(),n)||!Y(i)||c&&g(i[m])&&!v(i)&&((s=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,ct),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,ct),w.event.triggered=void 0,s&&(i[c]=s)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=K.access(r,t);i||r.addEventListener(e,n,!0),K.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=K.access(r,t)-1;i?K.access(r,t,i):(r.removeEventListener(e,n,!0),K.remove(r,t))}}});var ft=/\[\]$/,dt=/\r?\n/g,pt=/^(?:submit|button|image|reset|file)$/i,ht=/^(?:input|select|textarea|keygen)/i;function gt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||ft.test(e)?r(e,i):gt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==b(t))r(e,t);else for(i in t)gt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)gt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&ht.test(this.nodeName)&&!pt.test(e)&&(this.checked||!de.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(dt,"\r\n")}}):{name:t.name,value:n.replace(dt,"\r\n")}}).get()}}),w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},h.createHTMLDocument=function(){var e=r.implementation.createHTMLDocument("").body;return e.innerHTML="
",2===e.childNodes.length}(),w.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var i,o,a;return t||(h.createHTMLDocument?((i=(t=r.implementation.createHTMLDocument("")).createElement("base")).href=r.location.href,t.head.appendChild(i)):t=r),o=S.exec(e),a=!n&&[],o?[t.createElement(o[1])]:(o=be([e],t,a),a&&a.length&&w(a).remove(),w.merge([],o.childNodes))},w.offset={setOffset:function(e,t,n){var r,i,o,a,u,s,l,c=w.css(e,"position"),f=w(e),d={};"static"===c&&(e.style.position="relative"),u=f.offset(),o=w.css(e,"top"),s=w.css(e,"left"),(l=("absolute"===c||"fixed"===c)&&(o+s).indexOf("auto")>-1)?(a=(r=f.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(s)||0),g(t)&&(t=t.call(e,n,w.extend({},u))),null!=t.top&&(d.top=t.top-u.top+a),null!=t.left&&(d.left=t.left-u.left+i),"using"in t?t.using.call(e,d):f.css(d)}},w.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){w.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===w.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===w.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=w(e).offset()).top+=w.css(e,"borderTopWidth",!0),i.left+=w.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-w.css(r,"marginTop",!0),left:t.left-i.left-w.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===w.css(e,"position"))e=e.offsetParent;return e||xe})}}),w.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;w.fn[e]=function(r){return _(this,function(e,r,i){var o;if(v(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),w.each(["top","left"],function(e,t){w.cssHooks[t]=ze(h.pixelPosition,function(e,n){if(n)return n=Fe(e,t),Me.test(n)?w(e).position()[t]+"px":n})}),w.each({Height:"height",Width:"width"},function(e,t){w.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){w.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),u=n||(!0===i||!0===o?"margin":"border");return _(this,function(t,n,i){var o;return v(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?w.css(t,n,u):w.style(t,n,i,u)},t,a?i:void 0,a)}})}),w.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){w.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),w.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=D,w.isFunction=g,w.isWindow=v,w.camelCase=Q,w.type=b,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return w});var vt=e.jQuery,yt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=yt),t&&e.jQuery===w&&(e.jQuery=vt),w},t||(e.jQuery=e.$=w),w}); diff --git a/js/jquery.min.js b/js/jquery.min.js new file mode 100644 index 0000000..3883779 --- /dev/null +++ b/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/listen.html b/listen.html new file mode 100644 index 0000000..f5f5671 --- /dev/null +++ b/listen.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + EarthLoop audio connection + + + + + + + + + + + + + + + + + + + + +
+

+ EarthLoop +

+

Connected People for Networked Music

+
+ + +

+

+ Multi-user Auditorium Platform +

+

+ +

+ Listen to the current session here ! + +

+
+ + +
+ + + +
+ + + +
+ + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f5484f0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1492 @@ +{ + "name": "earthloop", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==" + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", + "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==" + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browserify": { + "version": "16.5.1", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-16.5.1.tgz", + "integrity": "sha512-EQX0h59Pp+0GtSRb5rL6OTfrttlzv+uyaUVlK6GX3w11SQ0jKPKyjC/54RhPR2ib2KmfcELM06e8FxcI5XNU2A==", + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^2.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.0.0", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==" + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, + "canvas-designer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/canvas-designer/-/canvas-designer-1.3.0.tgz", + "integrity": "sha512-LCOmIGgJAiPcJyo20Iyi/4xcfP+OS11TY6Vi0xli40MlbwNLwDZ/tF2ZPGlJQRmi5rkfqViLZXnHKKlQd94IfA==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==" + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "requires": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "detectrtc": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/detectrtc/-/detectrtc-1.4.0.tgz", + "integrity": "sha512-VQWrlpttdKCog05BhdRmlRzWa4QaJDHEoyvn1EoQvqUWz1DfneKXwmBO1sizVHRUyhu54mu6LSPG6bDQk1VrsA==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "^2.0.2" + } + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "engine.io": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", + "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "0.3.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "^7.1.2" + } + }, + "engine.io-client": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", + "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", + "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "events": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", + "integrity": "sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fbr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/fbr/-/fbr-2.0.8.tgz", + "integrity": "sha512-/0wWNI4C/iaQ9ntmP2qSUTspMA6UqZ5FFSwyY8/BfVaq2e5k7AVY42czPApyfjviYMdyrotjfTtBpl+r5D92Cw==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==" + }, + "getstats": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/getstats/-/getstats-1.2.0.tgz", + "integrity": "sha512-fdvGnPIaevnfrmL1qB78SeaqW4Pnm2f8j8nhRiWUZS7N1p377fee0ie76PO4uGkIjiuig9hMv4MVhVVBSLGBjA==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=" + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", + "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", + "requires": { + "JSONStream": "^1.0.3", + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "requires": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp-classic": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", + "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==" + }, + "module-deps": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.2.tgz", + "integrity": "sha512-a9y6yDv5u5I4A+IPHTnqFxcaKr4p50/zxTjcQJaX2ws9tN/W6J6YXnEKhqRyPhl494dkcxx951onSKVezmI+3w==", + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multistreamsmixer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/multistreamsmixer/-/multistreamsmixer-1.2.2.tgz", + "integrity": "sha512-5UGMODV/vDFLs8CaYEgFgo2sg+kI6hzXy4e02C8nvqfp7hfCikzAC65p+nNPVRqvBhtMoA2HwAz6qN8iMvoaoA==" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=" + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "requires": { + "readable-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "recordrtc": { + "version": "5.5.9", + "resolved": "https://registry.npmjs.org/recordrtc/-/recordrtc-5.5.9.tgz", + "integrity": "sha512-6T1tn80nRfsXrAoYWINwz+hBKk+pB5Im4I0BS52Og1ruQvrEdV3IUcm+AKwLMHNyBAhJ2DaVa+9pukTRxMF/uA==" + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rtcmulticonnection": { + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/rtcmulticonnection/-/rtcmulticonnection-3.6.9.tgz", + "integrity": "sha512-7TEjH4AVJXkoQZnK/3XLrR9M6Hi7xhiWGLELvFtpMC9d8nOsHb1p5lol89Hgs978NueoC2f6sF55qNAMtdLbzw==", + "requires": { + "canvas-designer": "^1.3.0", + "detectrtc": "^1.4.0", + "fbr": "^2.0.8", + "getstats": "^1.2.0", + "multistreamsmixer": "^1.2.2", + "recordrtc": "^5.5.9", + "rtcmulticonnection-server": "^1.3.1", + "socket.io": "^2.3.0", + "webrtc-adapter": "^7.5.1" + } + }, + "rtcmulticonnection-server": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rtcmulticonnection-server/-/rtcmulticonnection-server-1.3.1.tgz", + "integrity": "sha512-NsAKCYJalNBjphPHNe5h/hAvt9nUeY3U9ZHqa/zHFsKFqCX0YS2a5Uwhgphnz4WVEeKDUuOz3YOKJZxYJgdRxA==", + "requires": { + "socket.io": "^2.3.0" + } + }, + "rtcpeerconnection-shim": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz", + "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==", + "requires": { + "sdp": "^2.6.0" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "sdp": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz", + "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "requires": { + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" + } + }, + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "requires": { + "fast-safe-stringify": "^2.0.7" + } + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "socket.io": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.4.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.3.0", + "socket.io-parser": "~3.4.0" + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "socket.io-client": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + } + } + }, + "socket.io-parser": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", + "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.0.tgz", + "integrity": "sha512-cuB6RgO7BqC4FBYzmnvhob5Do3wIdIsXAgGycHJnW+981gHqoYcYz9lqjJrk8WXRddbwPuqPYRl+bag6mYv4lw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "requires": { + "minimist": "^1.1.0" + } + }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "requires": { + "acorn-node": "^1.2.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "requires": { + "process": "~0.11.0" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==" + }, + "undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "requires": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "webrtc-adapter": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.5.1.tgz", + "integrity": "sha512-R5LkIR/APjODkstSXFOztOmINXQ0nqIGfUoKTtCzjyiDXHNgwhkqZ9vi8UzGyjfUBibuZ0ZzVyV10qtuLGW3CQ==", + "requires": { + "rtcpeerconnection-shim": "^1.2.15", + "sdp": "^2.12.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", + "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==" + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c9fbe6 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "earthloop", + "version": "1.0.0", + "description": "EarthLoop Web Platform for Networked Music", + "main": "index.html", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "author": "Laurent Di Biase", + "license": "ISC", + "dependencies": { + "browserify": "^16.5.1", + "rtcmulticonnection": "^3.6.9", + "socket.io": "^2.3.0", + "socket.io-client": "^2.3.0" + } +} diff --git a/play.html b/play.html new file mode 100644 index 0000000..f967a69 --- /dev/null +++ b/play.html @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + EarthLoop audio connection + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ EarthLoop +

+

Connected People for Networked Music

+
+ + + + +
+
+ +
+ +
+ + +
+ + +
+

+ + + + +
+
+
+
+ +

You:

+
+ +

Partners:

+
+ + +
+
+ + + + + + + + + diff --git a/server-original.js b/server-original.js new file mode 100644 index 0000000..5edc9f7 --- /dev/null +++ b/server-original.js @@ -0,0 +1,85 @@ +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// Documentation - github.com/muaz-khan/getScreenId + +var port = process.env.PORT || 8080; + +var server = require('http'), + url = require('url'), + path = require('path'), + fs = require('fs'); + +function serverHandler(request, response) { + try { + var uri = url.parse(request.url).pathname, + filename = path.join(process.cwd(), uri); + + if (filename && filename.search(/server.js/g) !== -1) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + var stats; + + try { + stats = fs.lstatSync(filename); + } catch (e) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + if (fs.statSync(filename).isDirectory()) { + response.writeHead(404, { + 'Content-Type': 'text/html' + }); + + filename += '/index.html'; + } + + + fs.readFile(filename, 'utf8', function(err, file) { + if (err) { + response.writeHead(500, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + response.writeHead(200); + response.write(file, 'utf8'); + response.end(); + }); + } catch (e) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('

Unexpected error:



' + e.stack || e.message || JSON.stringify(e)); + response.end(); + } +} + +var app = server.createServer(serverHandler); + +function runServer() { + app = app.listen(port, process.env.IP || '0.0.0.0', function() { + var addr = app.address(); + + if (addr.address === '0.0.0.0') { + addr.address = 'localhost'; + } + + console.log('Server listening at http://' + addr.address + ':' + addr.port); + }); +} + +runServer(); diff --git a/server.js b/server.js new file mode 100644 index 0000000..66f32a4 --- /dev/null +++ b/server.js @@ -0,0 +1,207 @@ +// http://127.0.0.1:8080 +// http://localhost:8080 + +const fs = require('fs'); +const path = require('path'); +const url = require('url'); + +var events = require('events'); +var httpServer = require('http'); +var ioServer = require('socket.io'); +var socket = require('socket.io-client'); + +const RTCMultiConnectionServer = require('rtcmulticonnection-server'); + + +var PORT = 8080; +var isUseHTTPs = false; + +const jsonPath = { + config: 'config.json', + logs: 'logs.json' +}; + +const BASH_COLORS_HELPER = RTCMultiConnectionServer.BASH_COLORS_HELPER; +const getValuesFromConfigJson = RTCMultiConnectionServer.getValuesFromConfigJson; +const getBashParameters = RTCMultiConnectionServer.getBashParameters; + +var config = getValuesFromConfigJson(jsonPath); +config = getBashParameters(config, BASH_COLORS_HELPER); + + + +// if user didn't modifed "PORT" object +// then read value from "config.json" +if(PORT === 8080) { + PORT = config.port; +} +if(isUseHTTPs === false) { + isUseHTTPs = config.isUseHTTPs; +} + +/* +var fs = require('fs'); +require('http').createServer(function(req, res) { + if (req.url === '/page.html') { + res.end(fs.readFileSync('./page.html').toString()); + } + else if (req.url === '/unautrepage.html') { + res.end(fs.readFileSync('./unautrepage.html').toString()); + } + else { + res.end('not found'); + } +}).listen(3000); +*/ + +function serverHandler(request, response) { + try { + var uri = url.parse(request.url).pathname, + filename = path.join(process.cwd(), uri); + + var routes = ['/play.html', '/listen.html', '/assets/play.js', '/assets/listen.js']; + + if (routes.indexOf(page) !== -1) { + res.writeHead(200); + fs.readFile(page, 'utf-8', function(error, content) { + res.end(content); + }); + + if (filename && filename.search(/server.js/g) !== -1) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + var stats; + + try { + stats = fs.lstatSync(filename); + } catch (e) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + if (fs.statSync(filename).isDirectory()) { + response.writeHead(404, { + 'Content-Type': 'text/html' + }); + + filename += '/index.html'; + } + + + fs.readFile(filename, 'utf8', function(err, file) { + if (err) { + response.writeHead(500, { + 'Content-Type': 'text/plain' + }); + response.write('404 Not Found: ' + path.join('/', uri) + '\n'); + response.end(); + return; + } + + response.writeHead(200); + response.write(file, 'utf8'); + response.end(); + }); + } catch (e) { + response.writeHead(404, { + 'Content-Type': 'text/plain' + }); + response.write('

Unexpected error:



' + e.stack || e.message || JSON.stringify(e)); + response.end(); + } +} + +var httpApp; + +if (isUseHTTPs) { + httpServer = require('https'); + + // See how to use a valid certificate: + // https://github.com/muaz-khan/WebRTC-Experiment/issues/62 + var options = { + key: null, + cert: null, + ca: null + }; + + var pfx = false; + + if (!fs.existsSync(config.sslKey)) { + console.log(BASH_COLORS_HELPER.getRedFG(), 'sslKey:\t ' + config.sslKey + ' does not exist.'); + } else { + pfx = config.sslKey.indexOf('.pfx') !== -1; + options.key = fs.readFileSync(config.sslKey); + } + + if (!fs.existsSync(config.sslCert)) { + console.log(BASH_COLORS_HELPER.getRedFG(), 'sslCert:\t ' + config.sslCert + ' does not exist.'); + } else { + options.cert = fs.readFileSync(config.sslCert); + } + + if (config.sslCabundle) { + if (!fs.existsSync(config.sslCabundle)) { + console.log(BASH_COLORS_HELPER.getRedFG(), 'sslCabundle:\t ' + config.sslCabundle + ' does not exist.'); + } + + options.ca = fs.readFileSync(config.sslCabundle); + } + + if (pfx === true) { + options = { + pfx: sslKey + }; + } + + httpApp = httpServer.createServer(options, serverHandler); +} else { + httpApp = httpServer.createServer(serverHandler); +} + +RTCMultiConnectionServer.beforeHttpListen(httpApp, config); +httpApp = httpApp.listen(process.env.PORT || PORT, process.env.IP || "0.0.0.0", function() { + RTCMultiConnectionServer.afterHttpListen(httpApp, config); +}); + +/* + +// -------------------------- +// socket.io codes goes below + +ioServer(httpApp).on('connection', function(socket) { + RTCMultiConnectionServer.addSocket(socket, { config: config }); + + + + + socket.on('clickedSend', function(roomId) { + console.log(roomId); + //send a message to ALL connected clients + socket.broadcast.emit('clickedReceived', roomId); + }); + + + // ---------------------- + // below code is optional + + const params = socket.handshake.query; + + if (!params.socketCustomEvent) { + params.socketCustomEvent = 'custom-message'; + } + + socket.on(params.socketCustomEvent, function(message) { + socket.broadcast.emit(params.socketCustomEvent, message); + }); +}); +*/