You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
916 lines
26 KiB
916 lines
26 KiB
/* |
|
* Spreed WebRTC. |
|
* Copyright (C) 2013-2014 struktur AG |
|
* |
|
* This file is part of Spreed WebRTC. |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Affero General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Affero General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Affero General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'webrtc.adapter'], function($, _, angular, BigScreen, moment, sjcl, Modernizr) { |
|
|
|
return ["$scope", "$rootScope", "$element", "$window", "$timeout", "safeDisplayName", "safeApply", "mediaStream", "appData", "playSound", "desktopNotify", "alertify", "toastr", "translation", "fileDownload", "localStorage", "screensharing", "userSettingsData", "localStatus", "dialogs", "rooms", function($scope, $rootScope, $element, $window, $timeout, safeDisplayName, safeApply, mediaStream, appData, playSound, desktopNotify, alertify, toastr, translation, fileDownload, localStorage, screensharing, userSettingsData, localStatus, dialogs, rooms) { |
|
|
|
/*console.log("route", $route, $routeParams, $location);*/ |
|
|
|
// Disable drag and drop. |
|
$($window).on("dragover dragenter drop", function(event) { |
|
event.preventDefault(); |
|
}); |
|
|
|
// Avoid accidential reloads or exits when in a call. |
|
var manualUnload = false; |
|
$($window).on("beforeunload", function(event) { |
|
if (manualUnload || !$scope.peer) { |
|
return; |
|
} |
|
return translation._("Close this window and disconnect?"); |
|
}); |
|
|
|
// Enable app full screen listener. |
|
$("#bar .logo").on("doubletap dblclick", _.debounce(function() { |
|
if (BigScreen.enabled) { |
|
BigScreen.toggle($("body").get(0)); |
|
} |
|
}, 100, true)); |
|
|
|
// Load default sounds. |
|
playSound.initialize({ |
|
urls: ['sounds/sprite1.ogg', 'sounds/sprite1.mp3'], |
|
sprite: { |
|
"connect1": [ |
|
0, |
|
5179], |
|
"end1": [ |
|
12892, |
|
6199], |
|
"entry1": [ |
|
8387, |
|
3000], |
|
"leaving1": [ |
|
5228, |
|
2126], |
|
"message1": [ |
|
19140, |
|
816], |
|
"question1": [ |
|
20006, |
|
3313], |
|
"ringtone1": [ |
|
7403, |
|
935], |
|
"whistle1": [ |
|
11437, |
|
1405] |
|
} |
|
}, null, { |
|
"ring": "whistle1", |
|
"joined": "entry1", |
|
"left": "leaving1", |
|
"end": "end1", |
|
"dial": "ringtone1", |
|
"connect": "connect1", |
|
"prompt": "question1" |
|
}); |
|
|
|
appData.set($scope); |
|
|
|
var videoQualityMap = { |
|
tiny: { |
|
maxWidth: 80, |
|
maxHeight: 45 |
|
}, |
|
low: { |
|
maxWidth: 320, |
|
maxHeight: 180 |
|
}, |
|
high: { |
|
maxWidth: 640, |
|
maxHeight: 360 |
|
}, |
|
hd: { |
|
minWidth: 1280, |
|
minHeight: 720 |
|
}, |
|
fullhd: { |
|
minWidth: 1920, |
|
minHeight: 1080 |
|
} |
|
} |
|
|
|
var displayName = safeDisplayName; |
|
|
|
// Init STUN and TURN servers. |
|
$scope.stun = mediaStream.config.StunURIs || []; |
|
if (!$scope.stun.length) { |
|
$scope.stun.push("stun:stun.l.google.com:19302") |
|
} |
|
$scope.turn = {}; // TURN servers are set on received.self. |
|
|
|
// Add browser details for easy access. |
|
$scope.isChrome = $window.webrtcDetectedBrowser === "chrome"; |
|
$scope.webrtcDetectedBrowser = $window.webrtcDetectedBrowser; |
|
$scope.webrtcDetectedVersion = $window.webrtcDetectedVersion; |
|
|
|
// Add support status. |
|
$scope.supported = { |
|
screensharing: screensharing.supported, |
|
renderToAssociatedSink: $window.navigator.platform.indexOf("Win") === 0 |
|
} |
|
|
|
// Default scope data. |
|
$scope.status = "initializing"; |
|
$scope.id = $scope.myid = null; |
|
$scope.userid = $scope.myuserid = null; |
|
$scope.suserid = null; |
|
$scope.peer = null; |
|
$scope.dialing = null; |
|
$scope.conference = null; |
|
$scope.conferencePeers = []; |
|
$scope.incoming = null; |
|
$scope.microphoneMute = false; |
|
$scope.cameraMute = false; |
|
$scope.layout = { |
|
main: null, |
|
}; |
|
$scope.chatMessagesUnseen = 0; |
|
$scope.autoAccept = null; |
|
$scope.isCollapsed = true; |
|
$scope.defaults = { |
|
displayName: null, |
|
buddyPicture: null, |
|
message: null, |
|
settings: { |
|
videoQuality: "high", |
|
stereo: true, |
|
maxFrameRate: 20, |
|
defaultRoom: "", |
|
language: "", |
|
audioRenderToAssociatedSkin: true, |
|
experimental: { |
|
enabled: false, |
|
audioEchoCancellation2: true, |
|
audioAutoGainControl2: true, |
|
audioNoiseSuppression2: true, |
|
audioTypingNoiseDetection: true, |
|
videoLeakyBucket: true, |
|
videoNoiseReduction: false, |
|
videoCpuOveruseDetection: true |
|
} |
|
} |
|
}; |
|
$scope.master = angular.copy($scope.defaults); |
|
|
|
// Data voids. |
|
var resurrect = null; |
|
var reconnecting = false; |
|
var connected = false; |
|
var autoreconnect = true; |
|
|
|
$scope.update = function(user) { |
|
$scope.master = angular.copy(user); |
|
if (connected) { |
|
$scope.updateStatus(); |
|
} |
|
$scope.refreshWebrtcSettings(); |
|
}; |
|
|
|
$scope.reset = function() { |
|
$scope.user = angular.copy($scope.master); |
|
}; |
|
$scope.reset(); // Call once for bootstrap. |
|
|
|
$scope.setStatus = function(status) { |
|
// This is the connection status to signaling server. |
|
$scope.$emit("status", status); |
|
}; |
|
|
|
$scope.getStatus = function() { |
|
return $scope.status; |
|
}; |
|
|
|
$scope.updateStatus = function(clear) { |
|
// This is the user status. |
|
var status = { |
|
displayName: $scope.master.displayName || null, |
|
buddyPicture: $scope.master.buddyPicture || null, |
|
message: $scope.master.message || null |
|
} |
|
if (clear) { |
|
localStatus.clear(); |
|
} |
|
localStatus.update(status); |
|
}; |
|
|
|
$scope.refreshWebrtcSettings = function() { |
|
|
|
if (!$window.webrtcDetectedBrowser) { |
|
console.warn("This is not a WebRTC capable browser."); |
|
return; |
|
} |
|
var settings = $scope.master.settings; |
|
|
|
// Create iceServers from scope settings. |
|
var iceServers = []; |
|
var iceServer; |
|
if ($scope.stun.length) { |
|
iceServer = $window.createIceServers($scope.stun); |
|
if (iceServer.length) { |
|
iceServers.push.apply(iceServers, iceServer); |
|
} |
|
} |
|
if ($scope.turn.urls && $scope.turn.urls.length) { |
|
iceServer = $window.createIceServers($scope.turn.urls, $scope.turn.username, $scope.turn.password); |
|
if (iceServer.length) { |
|
iceServers.push.apply(iceServers, iceServer); |
|
} |
|
} |
|
|
|
var audioConstraints = []; |
|
var videoConstraints = []; |
|
var videoConstraintsMandatory = {}; |
|
var screensharingConstraints = []; |
|
|
|
var pushmulti = function(arrays, data) { |
|
_.each(arrays, function(a) { |
|
a.push(data); |
|
}); |
|
}; |
|
|
|
// Chrome only constraints. |
|
if ($scope.isChrome) { |
|
// Audio settings. |
|
// For defaults in Chromium see https://code.google.com/p/webrtc/source/browse/trunk/talk/media/webrtc/webrtcvoiceengine.cc#225 |
|
|
|
// Experimental audio settings. |
|
if (settings.experimental.enabled) { |
|
|
|
audioConstraints.push({ |
|
googEchoCancellation: true // defaults to true |
|
}); |
|
audioConstraints.push({ |
|
googEchoCancellation2: settings.experimental.audioEchoCancellation2 && true // defaults to false in Chrome |
|
}); |
|
audioConstraints.push({ |
|
googAutoGainControl: true // defaults to true |
|
}); |
|
audioConstraints.push({ |
|
googAutoGainControl2: settings.experimental.audioAutoGainControl2 && true // defaults to false in Chrome |
|
}); |
|
audioConstraints.push({ |
|
googNoiseSuppression: true // defaults to true |
|
}); |
|
audioConstraints.push({ |
|
googgNoiseSuppression2: settings.experimental.audioNoiseSuppression2 && true // defaults to false in Chrome |
|
}); |
|
audioConstraints.push({ |
|
googHighpassFilter: true // defaults to true |
|
}); |
|
audioConstraints.push({ |
|
googTypingNoiseDetection: settings.experimental.audioTypingNoiseDetection && true // defaults to true in Chrome |
|
}); |
|
|
|
} |
|
|
|
if ($scope.supported.renderToAssociatedSink) { |
|
audioConstraints.push({ |
|
// When true uses the default communications device on Windows. |
|
// https://codereview.chromium.org/155863003 |
|
googDucking: true // defaults to true on Windows. |
|
}); |
|
audioConstraints.push({ |
|
// Chrome will start rendering mediastream output to an output device that's associated with |
|
// the input stream that was opened via getUserMedia. |
|
// https://chromiumcodereview.appspot.com/23558010 |
|
chromeRenderToAssociatedSink: settings.audioRenderToAssociatedSkin && true // defaults to false in Chrome |
|
}); |
|
} |
|
|
|
// Select microphone device by id. |
|
if (settings.microphoneId) { |
|
audioConstraints.push({ |
|
sourceId: settings.microphoneId |
|
}); |
|
} |
|
// Select camera by device id. |
|
if (settings.cameraId) { |
|
videoConstraints.push({ |
|
sourceId: settings.cameraId |
|
}); |
|
} |
|
|
|
// Video settings. |
|
if (settings.experimental.enabled) { |
|
|
|
// Experimental video settings. |
|
pushmulti([videoConstraints, screensharingConstraints], { |
|
// Changes the way the video encoding adapts to the available bandwidth. |
|
// https://code.google.com/p/webrtc/issues/detail?id=3351 |
|
googLeakyBucket: settings.experimental.videoLeakyBucket && true // defaults to false in Chrome |
|
}); |
|
pushmulti([videoConstraints, screensharingConstraints], { |
|
// Removes the noise in the captured video stream at the expense of CPU. |
|
googNoiseReduction: settings.experimental.videoNoiseReduction && true // defaults to false in Chrome |
|
}); |
|
pushmulti([videoConstraints, screensharingConstraints], { |
|
googCpuOveruseDetection: settings.experimental.videoCpuOveruseDetection && true // defaults to true in Chrome |
|
}); |
|
|
|
} |
|
|
|
// Video. |
|
videoConstraintsMandatory = $.extend(videoConstraintsMandatory, videoQualityMap[settings.videoQuality]); |
|
// Not supported as of Firefox 27. |
|
if (settings.maxFrameRate && settings.maxFrameRate != "auto") { |
|
videoConstraintsMandatory.maxFrameRate = parseInt(settings.maxFrameRate, 10); |
|
} |
|
} |
|
|
|
// Apply the shit. |
|
mediaStream.webrtc.settings.stereo = settings.stereo; |
|
mediaStream.webrtc.settings.mediaConstraints.video.mandatory = videoConstraintsMandatory; |
|
mediaStream.webrtc.settings.mediaConstraints.video.optional = videoConstraints; |
|
mediaStream.webrtc.settings.mediaConstraints.audio = { |
|
optional: audioConstraints |
|
}; |
|
mediaStream.webrtc.settings.pcConfig.iceServers = iceServers; |
|
mediaStream.webrtc.settings.screensharing.mediaConstraints.video.optional = screensharingConstraints; |
|
|
|
// Inject optional stuff. |
|
var optionalPcConstraints = mediaStream.webrtc.settings.pcConstraints.optional = []; |
|
if ($window.webrtcDetectedBrowser === "chrome") { |
|
// NOTE(longsleep): We can always enable SCTP data channels, as we have a workaround |
|
// using the "active" event for Firefox < 27. |
|
// SCTP does not work correctly with Chrome 31. Require M32. |
|
if ($window.webrtcDetectedVersion >= 32) { |
|
// SCTP is supported from Chrome M31. |
|
// No need to pass DTLS constraint as it is on by default in Chrome M31. |
|
// For SCTP, reliable and ordered is true by default. |
|
} else { |
|
// Chrome < M32 does not yet do DTLS-SRTP by default whereas Firefox only |
|
// does DTLS-SRTP. In order to get interop, you must supply Chrome |
|
// with a PC constructor constraint to enable DTLS. |
|
console.warn("Turning on SCTP combatibility - please update your Chrome."); |
|
optionalPcConstraints.push({ |
|
DtlsSrtpKeyAgreement: true |
|
}); |
|
} |
|
} |
|
|
|
//console.log("WebRTC settings", mediaStream.webrtc.settings); |
|
|
|
}; |
|
$scope.refreshWebrtcSettings(); // Call once for bootstrap. |
|
|
|
var pickupTimeout = null; |
|
var autoAcceptTimeout = null; |
|
$scope.updateAutoAccept = function(id, from) { |
|
|
|
if (id) { |
|
console.log("Auto accept requested", id); |
|
$scope.autoAccept = id; |
|
$timeout.cancel(autoAcceptTimeout); |
|
autoAcceptTimeout = $timeout(function() { |
|
$scope.autoAccept = null; |
|
console.warn("Auto accept expired!") |
|
safeApply($scope); |
|
}, 2000); |
|
} else { |
|
if ($scope.autoAccept && $scope.autoAccept === from) { |
|
$scope.autoAccept = null; |
|
$timeout.cancel(autoAcceptTimeout); |
|
console.log("Auto accept success", from) |
|
return from; |
|
} |
|
return null; |
|
} |
|
|
|
}; |
|
|
|
$scope.manualReloadApp = function(url) { |
|
manualUnload = true; |
|
if (url) { |
|
$window.location.href = url; |
|
$timeout(function() { |
|
manualUnload = false; |
|
}, 0); |
|
} else { |
|
$window.location.reload(true); |
|
} |
|
}; |
|
|
|
$scope.loadUserSettings = function() { |
|
$scope.master = angular.copy($scope.defaults); |
|
var storedUser = userSettingsData.load(); |
|
if (storedUser) { |
|
$scope.user = $.extend(true, {}, $scope.master, storedUser); |
|
$scope.user.settings = $.extend(true, {}, $scope.user.settings, $scope.master.settings, $scope.user.settings); |
|
$scope.update($scope.user); |
|
$scope.loadedUser = storedUser.displayName && true; |
|
} else { |
|
$scope.loadedUser = false; |
|
} |
|
$scope.reset(); |
|
}; |
|
|
|
$scope.toggleBuddylist = (function() { |
|
var oldState = null; |
|
return function(status, force) { |
|
if (status || force) { |
|
oldState = $scope.layout.buddylist; |
|
$scope.layout.buddylist = !! status; |
|
} else { |
|
$scope.layout.buddylist = oldState; |
|
} |
|
} |
|
}()); |
|
|
|
$scope.openContactsManager = (function() { |
|
var oldDialog = null; |
|
return function() { |
|
if (oldDialog) { |
|
oldDialog.dismiss("open"); |
|
} |
|
oldDialog = dialogs.create( |
|
"/contactsmanager/main.html", |
|
"ContactsmanagerController", |
|
{ |
|
header: translation._("Contacts Manager") |
|
}, { |
|
wc: "contactsmanager" |
|
} |
|
); |
|
oldDialog.result.finally(function() { |
|
oldDialog = null; |
|
}); |
|
return oldDialog |
|
} |
|
}()); |
|
|
|
$scope.$watch("cameraMute", function(cameraMute) { |
|
mediaStream.webrtc.setVideoMute(cameraMute); |
|
}); |
|
|
|
$scope.$watch("microphoneMute", function(cameraMute) { |
|
mediaStream.webrtc.setAudioMute(cameraMute); |
|
}); |
|
|
|
var ringer = playSound.interval("ring", null, 4000); |
|
var dialer = playSound.interval("dial", null, 4000); |
|
var dialerEnabled = false; |
|
var notification; |
|
var ttlTimeout; |
|
var reloadDialog = false; |
|
|
|
mediaStream.api.e.on("received.self", function(event, data) { |
|
|
|
$timeout.cancel(ttlTimeout); |
|
safeApply($scope, function(scope) { |
|
scope.id = scope.myid = data.Id; |
|
scope.userid = scope.myuserid = data.Userid ? data.Userid : null; |
|
scope.suserid = data.Suserid ? data.Suserid : null; |
|
scope.turn = data.Turn; |
|
scope.stun = data.Stun; |
|
scope.refreshWebrtcSettings(); |
|
}); |
|
if (data.Version !== mediaStream.version) { |
|
console.info("Server was upgraded. Reload required."); |
|
if (!reloadDialog) { |
|
reloadDialog = true; |
|
_.delay(function() { |
|
alertify.dialog.confirm(translation._("Restart required to apply updates. Click ok to restart now."), function() { |
|
$scope.manualReloadApp(); |
|
}, function() { |
|
reloadDialog = false; |
|
}); |
|
}, 300); |
|
} |
|
} |
|
|
|
// Support authentication from localStorage. |
|
if (!data.Userid && mediaStream.config.UsersEnabled) { |
|
// Check if we can load a user. |
|
var login = mediaStream.users.load(); |
|
if (login !== null) { |
|
$scope.loadedUserlogin = true; |
|
console.log("Trying to authorize with stored credentials ..."); |
|
mediaStream.users.authorize(login, function(data) { |
|
console.info("Retrieved nonce - authenticating as user:", data.userid); |
|
mediaStream.api.requestAuthentication(data.userid, data.nonce); |
|
delete data.nonce; |
|
}, function(data, status) { |
|
console.error("Failed to authorize session", status, data); |
|
mediaStream.users.forget(); |
|
}); |
|
} else { |
|
$scope.loadedUserlogin = false; |
|
} |
|
} |
|
|
|
// Support to upgrade stuff when ttl was reached. |
|
if (data.Turn.ttl) { |
|
ttlTimeout = $timeout(function() { |
|
console.log("Ttl reached - sending refresh request."); |
|
mediaStream.api.sendSelf(); |
|
}, data.Turn.ttl / 100 * 90 * 1000); |
|
} |
|
|
|
// Support resurrection shrine. |
|
if (resurrect) { |
|
var resurrection = resurrect; |
|
resurrect = null; |
|
$timeout(function() { |
|
if (resurrection.id === $scope.id) { |
|
console.log("Using resurrection shrine", resurrection); |
|
// Valid resurrection. |
|
$scope.setStatus(resurrection.status); |
|
} |
|
}, 0); |
|
} |
|
|
|
// Propagate authentication event. |
|
appData.e.triggerHandler("selfReceived", [data]); |
|
|
|
// Unmark authorization process. |
|
if (data.Userid) { |
|
$rootScope.authorizing(false); |
|
$rootScope.$broadcast("authorization.succeeded"); |
|
} else if (!$rootScope.authorizing()) { |
|
// Trigger user data load when not in authorizing phase. |
|
$scope.loadUserSettings(); |
|
} |
|
|
|
if (rooms.inDefaultRoom() && $scope.master.settings.defaultRoom) { |
|
console.log("Selecting default room from settings:", [$scope.master.settings.defaultRoom]); |
|
rooms.joinByName($scope.master.settings.defaultRoom, true); |
|
} |
|
}); |
|
|
|
mediaStream.webrtc.e.on("peercall", function(event, peercall) { |
|
|
|
// Kill timeout. |
|
$timeout.cancel(pickupTimeout); |
|
pickupTimeout = null; |
|
// Kill ringer. |
|
if (peercall && peercall.from === null) { |
|
dialerEnabled = true; |
|
} else { |
|
dialerEnabled = false; |
|
} |
|
ringer.stop(); |
|
// Close notifications. |
|
if (notification) { |
|
notification.close(); |
|
} |
|
// Apply peer call to scope. |
|
safeApply($scope, function(scope) { |
|
scope.peer = peercall ? peercall.id : null; |
|
}); |
|
}); |
|
|
|
mediaStream.webrtc.e.on("peerconference", function(event, peerconference) { |
|
safeApply($scope, function(scope) { |
|
scope.conference = peerconference ? peerconference.id : null; |
|
scope.conferencePeers = peerconference ? peerconference.peerIds() : []; |
|
}); |
|
}); |
|
|
|
mediaStream.webrtc.e.on("offer", function(event, from, to2, to) { |
|
safeApply($scope, function(scope) { |
|
scope.incoming = from; |
|
}); |
|
if ($scope.updateAutoAccept(null, from)) { |
|
// Auto accept support. |
|
mediaStream.webrtc.doAccept(); |
|
return; |
|
} |
|
// Start to ring. |
|
ringer.start(); |
|
// Show incoming call notification. |
|
notification = desktopNotify.notify(translation._("Incoming call"), translation._("from") + " " + displayName(from), { |
|
timeout: null |
|
}); |
|
$scope.$emit("status", "ringing"); |
|
// Start accept timeout. |
|
pickupTimeout = $timeout(function() { |
|
console.log("Pickup timeout reached."); |
|
mediaStream.webrtc.doHangup("pickuptimeout"); |
|
$scope.$emit("notification", "incomingpickuptimeout", { |
|
reason: 'pickuptimeout', |
|
from: from |
|
}); |
|
}, 30000); |
|
appData.e.triggerHandler("uiNotification", ["incoming", {from: from}]); |
|
}); |
|
|
|
mediaStream.webrtc.e.on("error", function(event, message, msgid) { |
|
switch (msgid) { |
|
case "failed_getusermedia": |
|
message = translation._("Failed to access camera/microphone."); |
|
break; |
|
case "failed_peerconnection_setup": |
|
case "failed_peerconnection": |
|
message = translation._("Failed to establish peer connection.") |
|
break; |
|
} |
|
if (!message) { |
|
message = msgid; |
|
} |
|
if (!message) { |
|
message = translation._("We are sorry but something went wrong. Boo boo."); |
|
} |
|
alertify.dialog.alert(translation._("Oops") + "<br/>" + message); |
|
}); |
|
|
|
var reconnect = function() { |
|
if (connected && autoreconnect) { |
|
if (resurrect === null) { |
|
// Storage data at the resurrection shrine. |
|
resurrect = { |
|
status: $scope.getStatus(), |
|
id: $scope.id |
|
} |
|
console.log("Stored data at the resurrection shrine", resurrect); |
|
} |
|
reconnecting = false; |
|
_.delay(function() { |
|
if (autoreconnect && !reconnecting) { |
|
reconnecting = true; |
|
console.log("Requesting to reconnect ..."); |
|
mediaStream.reconnect(); |
|
} |
|
}, 500); |
|
$scope.setStatus("reconnecting"); |
|
} else { |
|
$scope.setStatus("closed"); |
|
} |
|
}; |
|
|
|
$scope.$on("room.joined", function(ev) { |
|
// TODO(lcooper): Is it really needful to do this stuff? |
|
$timeout.cancel(ttlTimeout); |
|
connected = true; |
|
reconnecting = false; |
|
$scope.updateStatus(true); |
|
}); |
|
|
|
mediaStream.connector.e.on("open error close", function(event) { |
|
$timeout.cancel(ttlTimeout); |
|
$scope.userid = $scope.suserid = null; |
|
switch (event.type) { |
|
case "open": |
|
connected = true; |
|
reconnecting = false; |
|
$scope.updateStatus(true); |
|
$scope.setStatus("waiting"); |
|
break; |
|
case "error": |
|
if (reconnecting || connected) { |
|
reconnecting = false; |
|
reconnect(); |
|
} else { |
|
$scope.setStatus(event.type); |
|
} |
|
break; |
|
case "close": |
|
reconnect(); |
|
break; |
|
} |
|
}); |
|
|
|
mediaStream.webrtc.e.on("waitforusermedia connecting", function(event, currentcall) { |
|
var t = event.type; |
|
safeApply($scope, function(scope) { |
|
scope.dialing = currentcall ? currentcall.id : null; |
|
scope.setStatus(t); |
|
}); |
|
}); |
|
|
|
mediaStream.webrtc.e.on("statechange", function(event, state, currentcall) { |
|
console.info("P2P state changed", state, currentcall.id); |
|
switch (state) { |
|
case "completed": |
|
case "connected": |
|
if ($scope.conference) { |
|
$scope.setStatus('conference'); |
|
} else { |
|
$scope.setStatus('connected'); |
|
} |
|
break; |
|
case "failed": |
|
mediaStream.webrtc.doHangup("failed", currentcall.id); |
|
alertify.dialog.alert(translation._("Peer connection failed. Check your settings.")); |
|
break; |
|
} |
|
}); |
|
|
|
$scope.$on("active", function(event, currentcall) { |
|
|
|
console.info("Video state active (assuming connected)", currentcall.id); |
|
if ($scope.conference) { |
|
$scope.setStatus('conference'); |
|
} else { |
|
$scope.setStatus('connected'); |
|
} |
|
$timeout(function() { |
|
if ($scope.peer) { |
|
$scope.layout.buddylist = false; |
|
$scope.layout.buddylistAutoHide = true; |
|
} |
|
}, 1000); |
|
|
|
}); |
|
|
|
$scope.$on("mainview", function(event, mainview, state) { |
|
console.info("Main view update", mainview, state); |
|
var changed = false; |
|
var layout = $scope.layout; |
|
if (layout.main === mainview && !state) { |
|
layout.main = null; |
|
changed = true; |
|
} else if (state) { |
|
layout.main = mainview; |
|
changed = true; |
|
} |
|
if (changed) { |
|
$scope.$broadcast("mainresize", layout.main); |
|
} |
|
}); |
|
|
|
$scope.$watch("userid", function(userid, olduserid) { |
|
var suserid; |
|
if (userid) { |
|
suserid = $scope.suserid; |
|
console.info("Session is now authenticated:", userid, suserid); |
|
} |
|
if (userid !== olduserid) { |
|
appData.e.triggerHandler("authenticationChanged", [userid, suserid]); |
|
// Load user settings after authentication changed. |
|
$scope.loadUserSettings(); |
|
} |
|
}); |
|
|
|
// Apply all layout stuff as classes to our element. |
|
$scope.$watch("layout", (function() { |
|
var makeName = function(prefix, n) { |
|
return prefix + n.charAt(0).toUpperCase() + n.slice(1); |
|
}; |
|
return function(layout, old) { |
|
_.each(layout, function(v, k) { |
|
if (k === "main") { |
|
return; |
|
} |
|
var n = makeName("with", k); |
|
if (v) { |
|
$element.addClass(n); |
|
} else { |
|
$element.removeClass(n); |
|
} |
|
}); |
|
if (old.main !== layout.main) { |
|
if (old.main) { |
|
$element.removeClass(makeName("main", old.main)); |
|
} |
|
if (layout.main) { |
|
$element.addClass(makeName("main", layout.main)); |
|
} |
|
} |
|
$scope.$broadcast("mainresize", layout.main); |
|
} |
|
}()), true); |
|
|
|
mediaStream.webrtc.e.on("done", function() { |
|
if (mediaStream.connector.connected) { |
|
$scope.setStatus("waiting"); |
|
} |
|
}); |
|
|
|
mediaStream.webrtc.e.on("busy", function(event, from) { |
|
console.log("Incoming call - sent busy.", from); |
|
$scope.$emit("notification", "incomingbusy", { |
|
reason: 'busy', |
|
from: from |
|
}); |
|
}); |
|
|
|
mediaStream.webrtc.e.on("bye", function(event, reason, from) { |
|
switch (reason) { |
|
case "busy": |
|
console.log("User is busy", reason, from); |
|
$scope.$emit("notification", "busy", { |
|
reason: reason, |
|
from: from |
|
}); |
|
break; |
|
case "reject": |
|
console.log("User rejected", reason, from); |
|
$scope.$emit("notification", "reject", { |
|
reason: reason, |
|
from: from |
|
}); |
|
break; |
|
case "pickuptimeout": |
|
console.log("User did not pick up", reason, from); |
|
$scope.$emit("notification", "pickuptimeout", { |
|
reason: reason, |
|
from: from |
|
}); |
|
break; |
|
case "error": |
|
console.log("User cannot accept call because of error"); |
|
alertify.dialog.alert(translation._("Oops") + "<br/>" + translation._("User hung up because of error.")); |
|
break; |
|
} |
|
}); |
|
|
|
$scope.$on("status", function(event, status) { |
|
if (status === "connecting" && dialerEnabled) { |
|
dialer.start(); |
|
} else { |
|
dialer.stop(); |
|
} |
|
safeApply($scope, function(scope) { |
|
var old = $scope.status; |
|
$scope.status = status; |
|
if (old === "connected" && status === "waiting") { |
|
_.delay(playSound.play, 100, "end"); |
|
} else if (old === "connecting" && status === "connected") { |
|
playSound.play("connect"); |
|
} |
|
}); |
|
appData.e.triggerHandler("mainStatus", [status]); |
|
}); |
|
|
|
$scope.$on("notification", function(event, type, details) { |
|
var message = null; |
|
switch (type) { |
|
case "busy": |
|
message = displayName(details.from) + translation._(" is busy. Try again later."); |
|
break; |
|
case "reject": |
|
message = displayName(details.from) + translation._(" rejected your call."); |
|
break; |
|
case "pickuptimeout": |
|
message = displayName(details.from) + translation._(" does not pick up."); |
|
break; |
|
case "incomingbusy": |
|
toastr.info(moment().format("llll"), displayName(details.from) + translation._(" tried to call you.")); |
|
break; |
|
case "incomingpickuptimeout": |
|
toastr.info(moment().format("llll"), displayName(details.from) + translation._(" called you.")); |
|
break; |
|
} |
|
if (message) { |
|
playSound.play("question"); |
|
alertify.dialog.alert(message); |
|
} |
|
appData.e.triggerHandler("uiNotification", [type, details]); |
|
}); |
|
|
|
$scope.$on("download", function(event, from, token) { |
|
|
|
var scope = event.targetScope; |
|
fileDownload.startDownload(scope, from, token); |
|
|
|
}); |
|
|
|
var chatMessagesUnseen = {}; |
|
$scope.$on("chatincoming", function(event, id) { |
|
var count = chatMessagesUnseen[id] || 0; |
|
count++; |
|
chatMessagesUnseen[id] = count; |
|
$scope.chatMessagesUnseen++; |
|
}); |
|
|
|
$scope.$on("chatseen", function(event, id) { |
|
var count = chatMessagesUnseen[id] || 0; |
|
delete chatMessagesUnseen[id]; |
|
$scope.chatMessagesUnseen = $scope.chatMessagesUnseen - count; |
|
}); |
|
|
|
_.defer(function() { |
|
if (!Modernizr.websockets) { |
|
alertify.dialog.alert(translation._("Your browser is not supported. Please upgrade to a current version.")); |
|
$scope.setStatus("unsupported"); |
|
return; |
|
} |
|
if (!$window.webrtcDetectedVersion) { |
|
alertify.dialog.alert(translation._("Your browser does not support WebRTC. No calls possible.")); |
|
return; |
|
} |
|
}); |
|
|
|
}]; |
|
|
|
});
|
|
|