Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0d2a79873 | ||
|
|
d94f001f25 | ||
|
|
afb85e2fd9 | ||
|
|
fffb5801c5 | ||
|
|
d81cd20ee6 | ||
|
|
3e7a9228bc | ||
|
|
835e199135 | ||
|
|
1d660e1883 | ||
|
|
5746261961 | ||
|
|
cbeae8eb30 | ||
|
|
95b2752d2a | ||
|
|
e3da472e7a | ||
|
|
43f60ca336 | ||
|
|
118a61c416 | ||
|
|
bf99a129bd | ||
|
|
fb6ad8cffd | ||
|
|
21fef57bc4 | ||
|
|
777422c87d | ||
|
|
ee6fd63c25 | ||
|
|
b9f00b71b2 | ||
|
|
099e3340bc | ||
|
|
172c2d3d71 | ||
|
|
854c8e5f2f | ||
|
|
b2cff193a9 | ||
|
|
ad1772178d | ||
|
|
0959b3d5b8 | ||
|
|
36f91f7f1e | ||
|
|
2c9d0606c3 | ||
|
|
1ce22fb8c9 | ||
|
|
e0cba855a6 | ||
|
|
8af3a65d37 | ||
|
|
667f67376e | ||
|
|
ce7b6be024 | ||
|
|
57cd2647f3 | ||
|
|
efcfe99707 | ||
|
|
cc1ad1bc13 | ||
|
|
29f06829e7 | ||
|
|
0fdf5e0102 | ||
|
|
5b7083f5f7 | ||
|
|
adb1c572ed | ||
|
|
5d17cd0bcc | ||
|
|
134d89a3d6 | ||
|
|
0efcbdcd37 | ||
|
|
878713a15d | ||
|
|
e01713f6f8 | ||
|
|
b6155c04ad | ||
|
|
8075d0a0fd | ||
|
|
029851fe3f | ||
|
|
886fb2ac43 | ||
|
|
99b1a51df0 | ||
|
|
795ec24246 | ||
|
|
ecf9c6fc6b | ||
|
|
68bc819b89 | ||
|
|
80c5779de9 | ||
|
|
d175dfdef7 | ||
|
|
93c13f5a11 | ||
|
|
ff8b880948 | ||
|
|
5b550c8a5b | ||
|
|
ce7d3c5c81 | ||
|
|
c99350308c | ||
|
|
e98c8ada6a | ||
|
|
ce8aa961ea | ||
|
|
fbd08ba3a6 | ||
|
|
61594cb877 | ||
|
|
520e655100 | ||
|
|
58d1697b00 | ||
|
|
f902b99287 | ||
|
|
d25a9b0e41 | ||
|
|
0e0f7d7ccb | ||
|
|
58cc21d417 | ||
|
|
8ac44491d0 | ||
|
|
a093b455b3 | ||
|
|
58494d45db | ||
|
|
f98621173f | ||
|
|
dbcfc92dc4 | ||
|
|
b9bd1d599b | ||
|
|
99b0be91ed | ||
|
|
f2ae29d8e4 | ||
|
|
4c3d415a07 | ||
|
|
7b65798758 | ||
|
|
c1c5a305c6 | ||
|
|
291211c029 | ||
|
|
a3a9e8d951 | ||
|
|
3a0ee11ccd | ||
|
|
2568b07075 | ||
|
|
e5fa02a1d4 | ||
|
|
fb5550bc38 | ||
|
|
bc5565251c | ||
|
|
4f548ce748 | ||
|
|
0faeb450c0 | ||
|
|
e12ffd7a24 | ||
|
|
d4c78617a7 | ||
|
|
db2296953d | ||
|
|
cd19c0e9e3 | ||
|
|
44e558e5a0 | ||
|
|
0da2547360 | ||
|
|
f3274ea42e | ||
|
|
0848283f6d | ||
|
|
6e99286bd0 | ||
|
|
588c2d9e4b | ||
|
|
7c201573fb | ||
|
|
581ffec5ed | ||
|
|
c296940fd4 | ||
|
|
f48633fa1d | ||
|
|
66a53c7dc0 | ||
|
|
09f53c6b21 | ||
|
|
b3a17040e5 | ||
|
|
774a7b41e4 | ||
|
|
062e671290 | ||
|
|
90d979b69d | ||
|
|
9d50084b98 | ||
|
|
e1d71a41f7 | ||
|
|
e781eb6423 | ||
|
|
15f4f03ba3 | ||
|
|
4f9b6f7180 | ||
|
|
b36ec5fd01 | ||
|
|
ac95ea03fe | ||
|
|
ae535fcb7d | ||
|
|
957cc6afc1 | ||
|
|
16fdd59617 | ||
|
|
fabf8f42c6 | ||
|
|
c98a56dc37 | ||
|
|
deb68dd420 | ||
|
|
0fd1a7fa08 | ||
|
|
c6ff8aa5dd | ||
|
|
06f025e92a | ||
|
|
f14329f2cd | ||
|
|
53e525597a | ||
|
|
54b3cbcf94 | ||
|
|
2852740e71 | ||
|
|
5322ba086b | ||
|
|
d2f95f3c81 | ||
|
|
3747251821 | ||
|
|
159ba82167 | ||
|
|
e34a8e6b60 | ||
|
|
17a6e360a2 | ||
|
|
b690f5d4a1 | ||
|
|
30f3168bf7 | ||
|
|
115f2e4663 | ||
|
|
fa15a75928 | ||
|
|
4db75446f3 | ||
|
|
d9f7b8b6cc | ||
|
|
05bbfda5bb | ||
|
|
e465b3ed90 | ||
|
|
1825f47ef2 | ||
|
|
169d613ac4 | ||
|
|
3dac5eeff5 | ||
|
|
f79651f806 | ||
|
|
6048d0a325 | ||
|
|
6f12446c99 | ||
|
|
af682f8727 | ||
|
|
9123923818 | ||
|
|
aee7a8e1bd | ||
|
|
5b44edb3cc | ||
|
|
806d4ea443 | ||
|
|
1e35ca5e4d | ||
|
|
d4f00d76ab | ||
|
|
37282e63b3 | ||
|
|
4b218499ae | ||
|
|
f16a1cdf44 | ||
|
|
702f02568d | ||
|
|
b6808d87bc | ||
|
|
8042bd2aa6 | ||
|
|
053b2d5af2 | ||
|
|
222164333b | ||
|
|
db50810e4b | ||
|
|
720851dcb9 | ||
|
|
d7203b8b1a | ||
|
|
204ca29ed7 | ||
|
|
fdada53a4a | ||
|
|
9e6e23ce97 |
4
.gitignore
vendored
@@ -1 +1,5 @@
|
||||
node_modules
|
||||
*.swp
|
||||
.idea/
|
||||
*.iml
|
||||
.*.tmp
|
||||
|
||||
6
Makefile
@@ -1,3 +1,4 @@
|
||||
NPM = npm
|
||||
BROWSERIFY = browserify
|
||||
GLOBAL_FLAGS = -x jquery -e
|
||||
OUTPUT_DIR = .
|
||||
@@ -14,11 +15,10 @@ compile-debug:FLAGS = -d $(GLOBAL_FLAGS)
|
||||
compile-debug: app
|
||||
|
||||
app:
|
||||
$(BROWSERIFY) $(FLAGS) app.js -s APP -o $(OUTPUT_DIR)/app.bundle.js
|
||||
$(NPM) update && $(BROWSERIFY) $(FLAGS) app.js -s APP -o $(OUTPUT_DIR)/app.bundle.js
|
||||
|
||||
clean:
|
||||
@rm -f $(OUTPUT_DIR)/*.bundle.js
|
||||
|
||||
deploy:
|
||||
@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR)
|
||||
|
||||
@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR) && ./bump-js-versions.sh
|
||||
|
||||
8
app.js
@@ -14,13 +14,16 @@ var APP =
|
||||
this.xmpp = require("./modules/xmpp/xmpp");
|
||||
this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut");
|
||||
this.translation = require("./modules/translation/translation");
|
||||
this.settings = require("./modules/settings/Settings");
|
||||
this.DTMF = require("./modules/DTMF/DTMF");
|
||||
this.members = require("./modules/members/MemberList");
|
||||
}
|
||||
};
|
||||
|
||||
function init() {
|
||||
|
||||
APP.RTC.start();
|
||||
APP.xmpp.start(APP.UI.getCredentials());
|
||||
APP.xmpp.start();
|
||||
APP.statistics.start();
|
||||
APP.connectionquality.init();
|
||||
|
||||
@@ -28,11 +31,14 @@ function init() {
|
||||
APP.desktopsharing.init();
|
||||
|
||||
APP.keyboardshortcut.init();
|
||||
APP.members.start();
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
var URLPRocessor = require("./modules/URLProcessor/URLProcessor");
|
||||
URLPRocessor.setConfigParametersFromUrl();
|
||||
APP.init();
|
||||
|
||||
APP.translation.init();
|
||||
|
||||
25
bump-js-versions.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script finds all js files included from index.html which have been
|
||||
# modified and bumps their version (the value of the "v" parameter used
|
||||
# in index.html)
|
||||
|
||||
# contents of index.html at HEAD (excluding not-committed changes)
|
||||
index=`git show HEAD:index.html`
|
||||
|
||||
# js files included from index.html. The sort needed for comm
|
||||
jsfiles=.bump-js-versions-jsfiles.tmp
|
||||
echo "$index" | grep '<script src=".*"' -o | sed -e 's/<script src="//' | sed -e 's/\?.*//' | tr -d \" | sort > $jsfiles
|
||||
|
||||
# modified files since the last commit
|
||||
gitmodified=.bump-js-versions-gitmodified.tmp
|
||||
git ls-files -m | sort > $gitmodified
|
||||
|
||||
for file in `comm -12 $jsfiles $gitmodified` ;do
|
||||
old_version=`echo "$index" | grep "<script src=\"${file}?v=[0-9]*" -o | sed -e 's/.*v=//'| tr -d \"`
|
||||
new_version=$((1+$old_version))
|
||||
echo Bumping version of $file from $old_version to $new_version
|
||||
sed -i.tmp -e "s%script src=\"${file}\?v=.*\"%script src=\"$file?v=$new_version\"%" index.html
|
||||
done
|
||||
|
||||
rm -f $jsfiles $gitmodified index.html.tmp
|
||||
@@ -1,20 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Jitsi Meet: Unsupported Browser</title>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/chromeonly.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- wrap starts here -->
|
||||
<div id="wrap">
|
||||
<a href="http://google.com/chrome"><div id="left"></div></a>
|
||||
<div id="middle"></div>
|
||||
<div id="text">
|
||||
<p>This application is currently only supported by <a href="http://google.com/chrome">Chrome</a>, <a href="http://www.chromium.org/">Chromium</a> and <a href="http://www.opera.com">Opera</a></p>
|
||||
<p><a href="http://google.com/chrome">Download Chrome</a></p>
|
||||
<p class="firefox">We are hoping that <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=977864">multistream support</a> for Firefox would not be long so that we could all use this application with our favorite browser.</p>
|
||||
</div>
|
||||
<!-- wrap ends here -->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
13
config.js
@@ -26,12 +26,13 @@ var config = {
|
||||
channelLastN: -1, // The default value of the channel attribute last-n.
|
||||
adaptiveLastN: false,
|
||||
adaptiveSimulcast: false,
|
||||
useRtcpMux: true,
|
||||
useBundle: true,
|
||||
useRtcpMux: true, // required for FF support
|
||||
useBundle: true, // required for FF support
|
||||
enableRecording: false,
|
||||
enableWelcomePage: true,
|
||||
enableSimulcast: false,
|
||||
enableFirefoxSupport: false, //firefox support is still experimental, only one-to-one conferences with chrome focus
|
||||
// will work when simulcast, bundle, mux, lastN and SCTP are disabled.
|
||||
logStats: false // Enable logging of PeerConnection stats via the focus
|
||||
enableSimulcast: false, // blocks FF support
|
||||
logStats: false, // Enable logging of PeerConnection stats via the focus
|
||||
/*noticeMessage: 'Service update is scheduled for 16th March 2015. ' +
|
||||
'During that time service will not be available. ' +
|
||||
'Apologise for inconvenience.'*/
|
||||
};
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
body {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color: white;
|
||||
color: #424242;
|
||||
font-family:'YanoneKaffeesatzLight',Verdana,Tahoma,Arial;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
#wrap{
|
||||
display: block;
|
||||
position: absolute;
|
||||
width:900px;
|
||||
height: 262px;
|
||||
overflow:hidden;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
}
|
||||
#left{
|
||||
display:inline-block;
|
||||
background-image:url(../images/chromelogo.png);
|
||||
background-repeat:no-repeat;
|
||||
width:246px;
|
||||
height:262px;
|
||||
float: left;
|
||||
}
|
||||
.firefox{
|
||||
font-size: 11pt;
|
||||
color: #c8c8c8;
|
||||
}
|
||||
#middle{
|
||||
display:inline-block;
|
||||
background-image:url(../images/chromepointer.png);
|
||||
background-repeat:no-repeat;
|
||||
width:53px;
|
||||
height:262px;
|
||||
float: left;
|
||||
}
|
||||
#text{
|
||||
display:inline-block;
|
||||
font-size: 18pt;
|
||||
width: 560px;
|
||||
vertical-align:middle;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #087dba;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,4 +119,8 @@
|
||||
|
||||
.icon-settings:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
}
|
||||
|
||||
.icon-dialPad:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
@@ -99,5 +99,7 @@
|
||||
width: 90px;
|
||||
height: 16px;
|
||||
padding-top: 4px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin: 15px auto 0px auto;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,6 @@ html, body{
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
#settings {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#nowebrtc {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@@ -26,4 +26,9 @@
|
||||
|
||||
button.jqidefaultbutton #inviteLinkRef {
|
||||
color: #2c8ad2;
|
||||
}
|
||||
|
||||
#inviteLinkRef {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
11
css/notice.css
Normal file
@@ -0,0 +1,11 @@
|
||||
#notice {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
margin-top: 6px;
|
||||
}
|
||||
#noticeText {
|
||||
background-color: #000000;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -27,8 +27,8 @@
|
||||
}
|
||||
|
||||
#settingsmenu button {
|
||||
width: 36%;
|
||||
left: 32%;
|
||||
width: 45%;
|
||||
left: 26%;
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -47,3 +47,21 @@
|
||||
#languages_selectbox{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
|
||||
#startMutedOptions {
|
||||
padding-left: 10%;
|
||||
text-indent: -10%;
|
||||
}
|
||||
|
||||
#startAudioMuted {
|
||||
width: 13px !important;
|
||||
}
|
||||
|
||||
#startVideoMuted {
|
||||
width: 13px !important;
|
||||
}
|
||||
|
||||
.startMutedLabel {
|
||||
float: left;
|
||||
}
|
||||
|
||||
118
css/unsupported_browser.css
Normal file
@@ -0,0 +1,118 @@
|
||||
body {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color: white;
|
||||
color: #424242;
|
||||
font-family:Helvetica,'YanoneKaffeesatzLight',Verdana,Tahoma,Arial;
|
||||
font-size: 28px;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
#wrap{
|
||||
display: block;
|
||||
position: absolute;
|
||||
width:900px;
|
||||
height: 365px;
|
||||
overflow:hidden;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
}
|
||||
.firefox{
|
||||
font-size: 11pt;
|
||||
color: #c8c8c8;
|
||||
width: 468px;
|
||||
text-align: center;
|
||||
margin: 30px auto 0px auto;
|
||||
padding-left: 15px;
|
||||
}
|
||||
#text{
|
||||
display:inline-block;
|
||||
font-size: 28px;
|
||||
width: 568px;
|
||||
vertical-align:middle;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #087dba;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.browser {
|
||||
width: 138px;
|
||||
height: 163px;
|
||||
margin-top: 5px;
|
||||
background-color: #e8e8e8;
|
||||
border: 1px solid #cfcfcf;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.browser_wrapper
|
||||
{
|
||||
width: 138px;
|
||||
height: 188px;
|
||||
vertical-align: middle;
|
||||
color: #929391;
|
||||
font-size: 20px;
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.supported_browsers
|
||||
{
|
||||
margin: 0px auto 0px auto;
|
||||
width: 660px;
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.button
|
||||
{
|
||||
background-color: #62c82a;
|
||||
border: 1px solid #3c8117;
|
||||
border-radius: 10px;
|
||||
color: #FFFFFF;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
width: 115px;
|
||||
height: 26px;
|
||||
padding-top: 13px;
|
||||
margin: 15px auto 0px auto;
|
||||
}
|
||||
|
||||
.logo
|
||||
{
|
||||
margin: 20px auto 0px auto;
|
||||
}
|
||||
|
||||
#chrome_logo
|
||||
{
|
||||
width: 78px;
|
||||
height: 78px;
|
||||
background-image: url('/images/chrome.png');
|
||||
}
|
||||
#chromium_logo
|
||||
{
|
||||
width: 77px;
|
||||
height: 79px;
|
||||
background-image: url('/images/chromium.png');
|
||||
}
|
||||
#firefox-nightly_logo
|
||||
{
|
||||
width: 73px;
|
||||
height: 79px;
|
||||
background-image: url('/images/firefox-nightly.png');
|
||||
}
|
||||
|
||||
#opera_logo
|
||||
{
|
||||
width: 73px;
|
||||
height: 78px;
|
||||
background-image: url('/images/opera.png');
|
||||
}
|
||||
|
||||
|
||||
@@ -415,3 +415,28 @@
|
||||
left: 35px;
|
||||
border-radius: 200px;
|
||||
}
|
||||
|
||||
.noMic {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noMic.png");
|
||||
background-color: #000;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.noVideo {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("../images/noVideo.png");
|
||||
background-color: #000;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
|
||||
6
debian/control
vendored
@@ -10,8 +10,7 @@ Homepage: https://jitsi.org/meet
|
||||
|
||||
Package: jitsi-meet
|
||||
Architecture: all
|
||||
Pre-Depends: jitsi-videobridge
|
||||
Depends: ${misc:Depends}, nginx, jitsi-meet-prosody, libjs-strophe (>= 1.1.3),
|
||||
Depends: ${misc:Depends}, jitsi-videobridge, nginx, jitsi-meet-prosody, libjs-strophe (>= 1.1.3),
|
||||
libjs-jquery, libjs-jquery-ui
|
||||
Description: WebRTC JavaScript video conferences
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
@@ -22,8 +21,7 @@ Description: WebRTC JavaScript video conferences
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
Pre-Depends: openssl, prosody | prosody-trunk, jitsi-videobridge
|
||||
Depends: ${misc:Depends}, jicofo
|
||||
Depends: ${misc:Depends}, openssl, prosody | prosody-trunk, jitsi-videobridge, jicofo
|
||||
Description: Prosody configuration for Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
|
||||
9
debian/jitsi-meet-prosody.postinst
vendored
@@ -28,6 +28,15 @@ case "$1" in
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME_OLD.cfg.lua
|
||||
rm -f /etc/prosody/certs/$JVB_HOSTNAME_OLD.key
|
||||
rm -f /etc/prosody/certs/$JVB_HOSTNAME_OLD.crt
|
||||
fi
|
||||
|
||||
# stores the hostname so we will reuse it later, like in purge
|
||||
db_set jitsi-meet-prosody/jvb-hostname $JVB_HOSTNAME
|
||||
|
||||
|
||||
12
debian/jitsi-meet.postinst
vendored
@@ -25,6 +25,14 @@ case "$1" in
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
|
||||
fi
|
||||
|
||||
# stores the hostname so we will reuse it later, like in purge
|
||||
db_set jitsi-meet/jvb-hostname $JVB_HOSTNAME
|
||||
|
||||
@@ -37,10 +45,6 @@ case "$1" in
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
if grep "# server_names_hash_bucket_size 64" /etc/nginx/nginx.conf > /dev/null; then
|
||||
sed -i "s/#\ server_names_hash_bucket_size\ 64/\ server_names_hash_bucket_size\ 64/" /etc/nginx/nginx.conf
|
||||
fi
|
||||
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
|
||||
16
debian/patches/jquery-package
vendored
@@ -1,22 +1,20 @@
|
||||
Description: Update the used js files for jquery to generic ones, to be able to use local system installed version (through symlinks).
|
||||
Index: jitsi-meet/index.html
|
||||
===================================================================
|
||||
--- jitsi-meet.orig/index.html
|
||||
+++ jitsi-meet/index.html
|
||||
@@ -9,7 +9,7 @@
|
||||
@@ -9,12 +9,12 @@
|
||||
<meta itemprop="name" content="Jitsi Meet"/>
|
||||
<meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
|
||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||
- <script src="libs/jquery-2.1.1.min.js"></script>
|
||||
+ <script src="libs/jquery.min.js"></script>
|
||||
<script src="config.js?v=5"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="simulcast.js?v=5"></script><!-- simulcast handling -->
|
||||
<script src="libs/strophe/strophe.jingle.adapter.js?v=2"></script><!-- strophe.jingle bundles -->
|
||||
@@ -24,7 +24,7 @@
|
||||
<script src="libs/strophe/strophe.util.js"></script>
|
||||
<script src="libs/colibri/colibri.focus.js?v=10"></script><!-- colibri focus implementation -->
|
||||
<script src="libs/colibri/colibri.session.js?v=1"></script>
|
||||
<script src="config.js?v=6"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||
- <script src="libs/jquery-ui.js"></script>
|
||||
+ <script src="libs/jquery-ui.min.js"></script>
|
||||
<script src="libs/rayo.js?v=1"></script>
|
||||
<script src="libs/tooltip.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
||||
|
||||
12
debian/rules
vendored
@@ -1,10 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# This file was originally written by Joey Hess and Craig Small.
|
||||
# As a special exception, when this file is copied by dh-make into a
|
||||
# dh-make output file, you may use that output file without restriction.
|
||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
@@ -19,7 +13,7 @@ override_dh_install:
|
||||
dh_installdirs
|
||||
dh_install -X/config.js
|
||||
yui-compressor -o debian/jitsi-meet/usr/share/jitsi-meet/libs/strophe/strophe.caps.jsonly.min.js \
|
||||
debian/patches/missing-source/libs/strophe/strophe.caps.jsonly.js
|
||||
debian/missing-source/libs/strophe/strophe.caps.jsonly.js
|
||||
yui-compressor -o debian/jitsi-meet/usr/share/jitsi-meet/libs/strophe/strophe.disco.min.js \
|
||||
debian/patches/missing-source/libs/strophe/sha1.js \
|
||||
debian/patches/missing-source/libs/strophe/strophe.disco.js
|
||||
debian/missing-source/libs/strophe/sha1.js \
|
||||
debian/missing-source/libs/strophe/strophe.disco.js
|
||||
|
||||
2
debian/watch
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
version=3
|
||||
opts="uversionmangle=s/^/1.0./" https://github.com/jitsi/jitsi-meet/releases/ /jitsi/jitsi-meet/archive/(\S+)\.tar\.gz
|
||||
12
doc/adding-an-icon.md
Normal file
@@ -0,0 +1,12 @@
|
||||
### Adding an icon to the font file (e.g. for the floating menu)
|
||||
1. Go to https://icomoon.io/app/
|
||||
2. Go to "Manage Projects" from the menu on the top left.
|
||||
3. Use "Import project" and select <code>fonts/selection.json</code> from Jitsi Meet.
|
||||
4. Import icons (e.g. svg files) using the "import items" button.
|
||||
5. Go to "generate font" and make sure the identifiers for the new icons are correct.
|
||||
6. Download the result in a zip file using the "download" button.
|
||||
7. Copy <code>selection.json</code> and <code>fonts/jitsi.*</code> from the zip file to <code>fonts/</code> in Jitsi Meet
|
||||
8. Copy the class for the new icon from <code>style.css</code> in the zip file to <code>css/font.css</code> in Jitsi Meet (do *not* copy the whole file)
|
||||
|
||||
Sample commit: https://github.com/jitsi/jitsi-meet/commit/68bc819b89aec12364fcf07b81efa83a1900eed6
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
server_names_hash_bucket_size 64;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name jitsi-meet.example.com;
|
||||
|
||||
29
doc/influxdb.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Overview
|
||||
Jitsi Meet supports logging to an [InfluxDB](http://influxdb.com/) database.
|
||||
|
||||
# Configuration
|
||||
The following needs to be done to enable this functionality.
|
||||
|
||||
## Install InfluxDB
|
||||
The details are outside the scope of the document, see http://influxdb.com/download/ .
|
||||
|
||||
## Create an InfluxDB database
|
||||
Use the InfluxDB admin interface (running on port 8083) and create a database. In this example we name it <code>jitsi_database</code>
|
||||
|
||||
## Enable logging for Jitsi Videobridge
|
||||
Add the following properties to <code>/usr/share/jitsi-videobridge/.sip-communicator/sip-communicator.properties</code>.
|
||||
|
||||
- org.jitsi.videobridge.log.INFLUX_DB_ENABLED=true
|
||||
- org.jitsi.videobridge.log.INFLUX_URL_BASE=http://influxdb.example.com:8086
|
||||
- org.jitsi.videobridge.log.INFLUX_DATABASE=jitsi_database
|
||||
- org.jitsi.videobridge.log.INFLUX_USER=user
|
||||
- org.jitsi.videobridge.log.INFLUX_PASS=pass
|
||||
|
||||
## Enable logging for Jicofo
|
||||
Add the same properties as above to <code>/usr/share/jitsi-videobridge/.sip-communicator/sip-communicator.properties</code>.
|
||||
|
||||
## Enable logging for Jitsi Meet itself
|
||||
Change "logStats" to "true" in <code>/etc/jitsi/meet/you-domain.config.js</code> or the <code>config.js</code> file used in your installation.
|
||||
|
||||
# User interface
|
||||
You can explore the database using the [Jiloin](https://github.com/jitsi/jiloin) web interface.
|
||||
@@ -1,12 +1,12 @@
|
||||
# Server Installation for Jitsi Meet
|
||||
|
||||
This describes configuring a server `jitsi.example.com`. You will need to
|
||||
This describes configuring a server `jitsi.example.com` running Debian or a Debian Derivative. You will need to
|
||||
change references to that to match your host, and generate some passwords for
|
||||
`YOURSECRET1`, `YOURSECRET2`, `YOURSECRET3` and `YOURSECRET4`.
|
||||
|
||||
There are also some complete [example config files](https://github.com/jitsi/jitsi-meet/tree/master/doc/example-config-files/) available, mentioned in each section.
|
||||
|
||||
## Install prosody and otalk modules
|
||||
## Install prosody
|
||||
```sh
|
||||
apt-get install lsb-release
|
||||
echo deb http://packages.prosody.im/debian $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list
|
||||
@@ -14,9 +14,6 @@ wget --no-check-certificate https://prosody.im/files/prosody-debian-packages.key
|
||||
apt-get update
|
||||
apt-get install prosody-trunk
|
||||
apt-get install git lua-zlib lua-sec-prosody lua-dbi-sqlite3 liblua5.1-bitop-dev liblua5.1-bitop0
|
||||
git clone https://github.com/andyet/otalk-server.git
|
||||
cd otalk-server
|
||||
cp -r mod* /usr/lib/prosody/modules
|
||||
```
|
||||
|
||||
## Configure prosody
|
||||
@@ -84,7 +81,7 @@ prosodyctl restart
|
||||
apt-get install nginx
|
||||
```
|
||||
|
||||
Add nginx config for domain in `/etc/nginx/nginx.conf`:
|
||||
Optionally, add nginx config for domain in `/etc/nginx/nginx.conf`:
|
||||
```
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
@@ -143,6 +140,8 @@ Install JRE if missing:
|
||||
apt-get install default-jre
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7._
|
||||
|
||||
In the user home that will be starting Jitsi Videobridge create `.sip-communicator` folder and add the file `sip-communicator.properties` with one line in it:
|
||||
```
|
||||
org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
|
||||
@@ -157,7 +156,15 @@ Or autostart it by adding the line in `/etc/rc.local`:
|
||||
/bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --port=5347 --secret=YOURSECRET1 </dev/null >> /var/log/jvb.log 2>&1
|
||||
```
|
||||
|
||||
## Install Jitsi Conference Focus(jicofo)
|
||||
## Install Jitsi Conference Focus (jicofo)
|
||||
|
||||
Install JDK and Ant if missing:
|
||||
```
|
||||
apt-get install default-jdk ant
|
||||
```
|
||||
|
||||
_NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7._
|
||||
|
||||
Clone source from Github repo:
|
||||
```sh
|
||||
git clone https://github.com/jitsi/jicofo.git
|
||||
@@ -202,54 +209,6 @@ Restart nginx to get the new configuration:
|
||||
invoke-rc.d nginx restart
|
||||
```
|
||||
|
||||
|
||||
## Install [Turn server](https://github.com/andyet/otalk-server/tree/master/restund)
|
||||
```sh
|
||||
apt-get install make gcc
|
||||
wget http://creytiv.com/pub/re-0.4.7.tar.gz
|
||||
tar zxvf re-0.4.7.tar.gz
|
||||
ln -s re-0.4.7 re
|
||||
cd re-0.4.7
|
||||
sudo make install PREFIX=/usr
|
||||
cd ..
|
||||
wget http://creytiv.com/pub/restund-0.4.2.tar.gz
|
||||
wget https://raw.github.com/andyet/otalk-server/master/restund/restund-auth.patch
|
||||
tar zxvf restund-0.4.2.tar.gz
|
||||
cd restund-0.4.2/
|
||||
patch -p1 < ../restund-auth.patch
|
||||
sudo make install PREFIX=/usr
|
||||
cp debian/restund.init /etc/init.d/restund
|
||||
chmod +x /etc/init.d/restund
|
||||
cd /etc
|
||||
wget https://raw.github.com/andyet/otalk-server/master/restund/restund.conf
|
||||
```
|
||||
|
||||
Configure addresses and ports as desired, and the password to be configured in prosody:
|
||||
```
|
||||
realm jitsi.example.com
|
||||
# share this with your prosody server
|
||||
auth_shared YOURSECRET4
|
||||
|
||||
# modules
|
||||
module_path /usr/lib/restund/modules
|
||||
turn_relay_addr [turn ip address]
|
||||
```
|
||||
|
||||
Configure prosody to use it in `/etc/prosody/prosody.cfg.lua`. Add to your virtual host:
|
||||
```
|
||||
turncredentials_secret = "YOURSECRET4";
|
||||
turncredentials = {
|
||||
{ type = "turn", host = "turn.address.ip.configured", port = 3478, transport = "tcp" }
|
||||
}
|
||||
```
|
||||
|
||||
Add turncredentials module in the "modules_enabled" section
|
||||
|
||||
Reload prosody if needed
|
||||
```
|
||||
prosodyctl restart
|
||||
```
|
||||
|
||||
## Running behind NAT
|
||||
In case of videobridge being installed on a machine behind NAT, add the following extra lines to the file `~/.sip-communicator/sip-communicator.properties` (in the home of user running the videobridge):
|
||||
```
|
||||
|
||||
BIN
fonts/jitsi.eot
@@ -35,4 +35,5 @@
|
||||
<glyph unicode="" d="M46.993 961.7c461.234 0 553.793 0 1015.024 0 35.919 0 53.356-25.959 53.356-57.959-0.581-303.259-0.325-606.488-0.449-909.809 0-43.984-13.203-57.058-57.703-57.058-443.072-0.126-556.453-0.126-999.553 0-44.534 0-57.799 13.009-57.799 57.058-0.098 303.257 0.485 608.072-0.093 911.329-0.034 26.21 11.301 53.761 47.217 56.439zM311.405 450.298c0-119.045-0.072-172.168 0.057-291.249 0.036-50.043 11.208-61.050 62.12-61.050 233.352 0 137.075 0 370.522 0 47.075 0 59.249 11.982 59.249 58.095 0.126 239.111 0.126 346.338 0 585.389 0 48.138-10.687 58.991-57.768 58.991-235.323 0.101-140.844 0.101-376.157 0-47.044 0-57.93-11.043-57.966-58.89-0.129-119.109-0.057-172.209-0.057-291.287zM153.944 838.566c-74.929-0.062-66.687 5.958-66.845-66.685-0.201-63.95-7.054-63.534 62.528-63.372 72.999 0.194 67.201-3.764 67.302 67.554 0 67.722 4.087 62.595-62.985 62.502zM963.644 838.566c-71.159-0.034-65.56 6.185-65.751-65.364-0.129-67.302-4.508-64.693 64.528-64.693 73.089 0 65.299-2.031 65.299 66.238-0.003 68.646 6.956 63.911-64.076 63.818zM216.828 122.408c0.359 73.094 4.639 66.914-67.358 67.17-68.104 0.191-62.569 2.763-62.407-63.31 0.129-73.476-6.954-66.52 67.074-66.649 66.042-0.065 63.142-6.056 62.691 62.789zM1027.718 124.4c0.134 68.334 6.443 65.304-63.297 65.178-70.132-0.132-66.656 5.793-66.527-65.304 0.129-70.645-4.384-64.721 63.756-64.657 71.995 0.132 66.202-6.698 66.068 64.783zM1027.718 342.077c0 70.55 7.219 66.842-67.485 66.522-0.898 0-1.873 0-2.838 0-59.375 0-59.375 0-59.375-58.023 0-77.922-6.443-69.936 69.293-70.196 66.076-0.387 60.539-3.091 60.405 61.697zM151.307 489.873c68.295-0.163 65.815-5.568 65.624 62.982-0.194 71.128 4.895 64.917-66.014 65.010-69.905 0.101-63.813 4.704-63.885-63.978-0.062-67.431-5.7-64.463 64.275-64.014zM961.263 489.873c72.511-0.258 66.589-4.603 66.455 64.494 0 68.558 6.185 63.537-64.267 63.498-70.196-0.028-65.686 6.053-65.498-65.493 0.132-62.5 0.067-62.5 63.31-62.5zM150.399 280.38c71.004 0 66.659-6.567 66.466 64.528-0.163 63.694-0.036 63.501-65.013 63.756-70.805 0.258-64.822 2.673-64.822-63.756 0.036-69.167-5.919-64.788 63.369-64.528z" horiz-adv-x="1115" />
|
||||
<glyph unicode="" d="M3.881 146.835h220.26v-210.835h-220.26v210.835zM308.817 350.143h220.27v-414.143h-220.27v414.143zM613.764 553.412h220.268v-617.412h-220.268v617.412zM918.685 756.715h220.265v-820.715h-220.265v820.715zM1223.629 960h220.263v-1024h-220.263v1024z" horiz-adv-x="1444" />
|
||||
<glyph unicode="" d="M526.071 234.749c-28.637-30.869-56.465-60.861-84.282-90.859-51.578-55.636-103.047-111.376-154.842-166.832-7.606-8.135-15.958-16.1-25.317-22.012-28.075-17.708-58.31-18.090-88.472-6.492-59.84 23.028-80.004 90.727-59.734 139.234 5.413 12.95 13.721 23.601 23.709 33.173 70.256 67.351 140.506 134.717 210.76 202.077 15.638 14.993 31.264 29.995 47.364 45.45-9.302 9.529-18.386 18.833-27.451 28.137-12.122 12.442-13.234 20.28-5.067 35.498 4.735 8.816 4.789 8.878-2.627 16.198-20.012 19.72-40.168 39.198-63.498 55.188-27.167 18.624-57.161 24.233-89.083 19.849-53.402-7.328-91.609-38.372-121.413-81.046-12.774-18.299-15.365-40.313-17.517-61.875-3.23-32.245-2.415-64.479 2.209-96.597 1.654-11.515-3.863-16.539-13.835-11.175-8.306 4.448-16.095 11.048-22.115 18.353-15.574 18.89-22.223 42.042-27.474 65.395-12.955 57.652-8.86 114.49 12.191 169.495 32.345 84.537 79.743 159.571 145.953 221.932 13.659 12.857 176.841 180.564 202.944 207.021 7.493 7.599 14.895 7.635 22.393 0.028 43.009-43.641 85.985-87.316 128.927-131.029 8.117-8.267 8.019-15.097-0.222-23.49-26.339-26.834-52.726-53.627-79.106-80.419-6.244-6.334-97.34-82.437-73.027-128.816 22.693-25.090 46.196-49.449 69.575-73.904 1.189-1.238 4.686-1.386 6.523-0.632 3.63 1.499 6.848 3.997 10.248 6.066 9.745 5.94 19.545 4.918 27.812-3.083 11.755-11.381 23.405-22.858 35.392-34.59 4.807 4.575 9.939 9.41 15.027 14.294 27.128 26.039 54.272 52.071 81.351 78.146 16.413 15.778 18.652 28.418 11.038 49.658-10.473 29.221-14.356 59.677-13.85 90.624 1.017 61.045 20.438 115.334 61.003 161.416 32.825 37.286 72.054 64.311 121.643 74.325 35.227 7.101 69.139 4.513 100.663-14.026 6.365-3.752 11.908-9.007 17.455-14.005 3.491-3.125 3.153-6.236-0.565-9.98-42.503-42.885-84.772-86.013-127.154-129.035-12.442-12.638-12.356-23.167 0.196-35.914 40.344-40.978 80.597-82.050 120.936-123.052 10.076-10.233 19.537-10.021 29.504 0.134 43.195 44.077 86.449 88.090 129.706 132.118 1.21 1.233 2.572 2.322 5.135 4.624 5.491-5.893 11.895-10.924 15.961-17.406 19.452-30.944 22.608-64.83 17.073-100.25-14.253-91.080-97.188-175.638-197.712-190.123-39.977-5.764-79.372-2.562-118.067 9.031-5.898 1.775-11.541 4.629-17.538 5.829-12.47 2.474-23.872-0.366-32.74-9.877-30.921-33.168-61.674-66.484-92.474-99.758-0.73-0.805-1.349-1.718-0.181-1.099 8.992-10.006 17.354-20.662 27.061-29.94 81.064-77.54 164.91-151.986 250.882-224.063 9.936-8.347 10.274-15.695 1.040-25.1-42.338-43.068-84.689-86.111-127.059-129.154-9.413-9.575-16.846-9.152-25.291 1.295-76.686 94.78-156.8 186.609-239.707 276.002-1.334 1.453-2.562 3.029-4.257 5.042z" horiz-adv-x="1105" />
|
||||
<glyph unicode="" d="M74.418 881.299h239.304v-228.491h-239.304v228.491zM393.455 881.299h239.304v-228.491h-239.304v228.491zM712.494 881.299h239.263v-228.491h-239.263v228.491zM74.418 562.265h239.304v-228.555h-239.304v228.555zM393.455 562.265h239.304v-228.555h-239.304v228.555zM712.494 562.265h239.263v-228.555h-239.263v228.555zM74.418 243.166h239.304v-228.465h-239.304v228.465zM393.455 243.166h239.304v-228.465h-239.304v228.465zM712.494 243.166h239.263v-228.465h-239.263v228.465z" horiz-adv-x="1026" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.woff
@@ -1,6 +1,59 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M74.418 78.701h239.304v228.491h-239.304v-228.491z",
|
||||
"M393.455 78.701h239.304v228.491h-239.304v-228.491z",
|
||||
"M712.494 78.701h239.263v228.491h-239.263v-228.491z",
|
||||
"M74.418 397.735h239.304v228.555h-239.304v-228.555z",
|
||||
"M393.455 397.735h239.304v228.555h-239.304v-228.555z",
|
||||
"M712.494 397.735h239.263v228.555h-239.263v-228.555z",
|
||||
"M74.418 716.834h239.304v228.465h-239.304v-228.465z",
|
||||
"M393.455 716.834h239.304v228.465h-239.304v-228.465z",
|
||||
"M712.494 716.834h239.263v228.465h-239.263v-228.465z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"width": 1026,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"dailPad"
|
||||
]
|
||||
},
|
||||
"attrs": [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
],
|
||||
"properties": {
|
||||
"order": 29,
|
||||
"id": 0,
|
||||
"prevSize": 32,
|
||||
"code": 58908,
|
||||
"name": "dialPad"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -31,7 +84,8 @@
|
||||
"code": 58907,
|
||||
"name": "settings"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
@@ -74,7 +128,8 @@
|
||||
"name": "webCam",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 1
|
||||
},
|
||||
{
|
||||
@@ -144,7 +199,8 @@
|
||||
"name": "connection",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
@@ -168,7 +224,8 @@
|
||||
"name": "filmstrip",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 3
|
||||
},
|
||||
{
|
||||
@@ -194,7 +251,8 @@
|
||||
"name": "reload",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 4
|
||||
},
|
||||
{
|
||||
@@ -218,7 +276,8 @@
|
||||
"name": "hangup",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 5
|
||||
},
|
||||
{
|
||||
@@ -242,7 +301,8 @@
|
||||
"name": "contactList",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
@@ -267,7 +327,8 @@
|
||||
"name": "avatar",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 7
|
||||
},
|
||||
{
|
||||
@@ -292,7 +353,8 @@
|
||||
"name": "callRetro",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 8
|
||||
},
|
||||
{
|
||||
@@ -317,7 +379,8 @@
|
||||
"name": "callModern",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 9
|
||||
},
|
||||
{
|
||||
@@ -343,7 +406,8 @@
|
||||
"name": "recDisable",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 10
|
||||
},
|
||||
{
|
||||
@@ -370,7 +434,8 @@
|
||||
"name": "recEnable",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 11
|
||||
},
|
||||
{
|
||||
@@ -395,7 +460,8 @@
|
||||
"name": "kick1",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 12
|
||||
},
|
||||
{
|
||||
@@ -421,7 +487,8 @@
|
||||
"name": "kick",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 13
|
||||
},
|
||||
{
|
||||
@@ -447,7 +514,8 @@
|
||||
"name": "share-desktop",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 14
|
||||
},
|
||||
{
|
||||
@@ -471,7 +539,8 @@
|
||||
"name": "chat-simple",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 15
|
||||
},
|
||||
{
|
||||
@@ -497,7 +566,8 @@
|
||||
"name": "full-screen",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 16
|
||||
},
|
||||
{
|
||||
@@ -523,7 +593,8 @@
|
||||
"name": "exit-full-screen",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 17
|
||||
},
|
||||
{
|
||||
@@ -552,7 +623,8 @@
|
||||
"name": "prezi",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 18
|
||||
},
|
||||
{
|
||||
@@ -595,7 +667,8 @@
|
||||
"name": "addNew-V5",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 19
|
||||
},
|
||||
{
|
||||
@@ -621,7 +694,8 @@
|
||||
"name": "chat",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 20
|
||||
},
|
||||
{
|
||||
@@ -648,7 +722,8 @@
|
||||
"name": "presentation",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 21
|
||||
},
|
||||
{
|
||||
@@ -672,7 +747,8 @@
|
||||
"name": "security",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 22
|
||||
},
|
||||
{
|
||||
@@ -697,7 +773,8 @@
|
||||
"name": "share-doc",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 23
|
||||
},
|
||||
{
|
||||
@@ -721,7 +798,8 @@
|
||||
"name": "security-locked",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 24
|
||||
},
|
||||
{
|
||||
@@ -746,7 +824,8 @@
|
||||
"name": "camera-disabled",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 25
|
||||
},
|
||||
{
|
||||
@@ -772,7 +851,8 @@
|
||||
"name": "mic-disabled",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 26
|
||||
},
|
||||
{
|
||||
@@ -797,7 +877,8 @@
|
||||
"name": "microphone",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setIdx": 1,
|
||||
"setId": 1,
|
||||
"iconIdx": 27
|
||||
}
|
||||
],
|
||||
|
||||
BIN
images/chrome.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
BIN
images/chromium.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
images/firefox-nightly.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
images/firefox.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/noMic.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
images/noVideo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
images/opera.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
46
index.html
@@ -10,7 +10,7 @@
|
||||
<meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
|
||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||
<script src="libs/jquery-2.1.1.min.js"></script>
|
||||
<script src="config.js?v=6"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="config.js?v=8"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||
@@ -19,15 +19,16 @@
|
||||
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
||||
<script src="interface_config.js?v=5"></script>
|
||||
<script src="libs/app.bundle.js?v=15"></script>
|
||||
<script src="libs/app.bundle.js?v=75"></script>
|
||||
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
|
||||
<link rel="stylesheet" href="css/font.css?v=6"/>
|
||||
<link rel="stylesheet" href="css/font.css?v=7"/>
|
||||
<link rel="stylesheet" href="css/toastr.css?v=1">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=30"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=16" id="videolayout_default"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=17" id="videolayout_default"/>
|
||||
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
|
||||
<link rel="stylesheet" href="css/modaldialog.css?v=3">
|
||||
<link rel="stylesheet" href="css/notice.css?v=1">
|
||||
<link rel="stylesheet" href="css/popup_menu.css?v=4">
|
||||
<link rel="stylesheet" href="css/login_menu.css?v=1">
|
||||
<link rel="stylesheet" href="css/popover.css?v=2">
|
||||
@@ -40,8 +41,9 @@
|
||||
Link used for inline installation of chrome desktop streaming extension,
|
||||
is updated automatically from the code with the value defined in config.js -->
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/diibjkoicjeejcmhdnailmkgecihlobk">
|
||||
<script src="libs/jquery-impromptu.js"></script>
|
||||
<script src="libs/jquery-impromptu.js?v=2"></script>
|
||||
<script src="libs/jquery.autosize.js"></script>
|
||||
<!--#include virtual="plugin.head.html" -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="welcome_page">
|
||||
@@ -68,7 +70,9 @@
|
||||
<div id="brand_header"></div>
|
||||
<input type='checkbox' name='checkbox' id="disable_welcome"/>
|
||||
<label for="disable_welcome" class="disable_welcome_position" data-i18n="welcomepage.disable"></label>
|
||||
<div id="header_text"></div>
|
||||
<div id="header_text">
|
||||
<!--#include virtual="plugin.header.text.html" -->
|
||||
</div>
|
||||
</div>
|
||||
<div id="welcome_page_main">
|
||||
<div id="features">
|
||||
@@ -116,10 +120,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--#include virtual="plugin.welcomepage.footer.html" -->
|
||||
</div>
|
||||
<div id="videoconference_page">
|
||||
<div style="position: relative;" id="header_container">
|
||||
<div id="header">
|
||||
<div id="notice" class="notice" style="display: none">
|
||||
<span id="noticeText" class="noticeText"></span>
|
||||
</div>
|
||||
<span id="toolbar">
|
||||
<span id="authentication" class="authentication" style="display: none">
|
||||
<a class="button" id="toolbar_button_authentication" >
|
||||
@@ -193,6 +201,11 @@
|
||||
<a class="button" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip">
|
||||
<i class="icon-telephone"></i></a>
|
||||
</span>
|
||||
<span id="dialPadButton" style="display: none">
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_dialpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Open dialpad" data-i18n="[content]toolbar.dialpad">
|
||||
<i class="icon-dialpad"></i></a>
|
||||
</span>
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" data-i18n="[content]toolbar.Settings">
|
||||
<i id="settingsButton" class="icon-settings"></i>
|
||||
@@ -207,15 +220,6 @@
|
||||
</div>
|
||||
<div id="subject"></div>
|
||||
</div>
|
||||
<div id="settings">
|
||||
<h1 data-i18n="connectionsettings"></h1>
|
||||
<form id="loginInfo">
|
||||
<label>JID: <input id="jid" type="text" name="jid" placeholder="me@example.com"/></label>
|
||||
<label>Password: <input id="password" type="password" name="password" placeholder="secret"/></label>
|
||||
<label>BOSH URL: <input id="boshURL" type="text" name="boshURL" placeholder="/http-bind"/></label>
|
||||
<input id="connect" type="submit" value="Connect" />
|
||||
</form>
|
||||
</div>
|
||||
<div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
|
||||
<div id="videospace">
|
||||
<div id="largeVideoContainer" class="videocontainer">
|
||||
@@ -295,9 +299,19 @@
|
||||
<div class="arrow-up"></div>
|
||||
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
|
||||
<input type="text" id="setEmail" placeholder="E-Mail">
|
||||
<div id = "startMutedOptions">
|
||||
<label class = "startMutedLabel">
|
||||
<input type="checkbox" id="startAudioMuted">
|
||||
<span data-i18n="settings.startAudioMuted"></span>
|
||||
</label>
|
||||
<label class = "startMutedLabel">
|
||||
<input type="checkbox" id="startVideoMuted">
|
||||
<span data-i18n="settings.startVideoMuted"></span>
|
||||
</label>
|
||||
</div>
|
||||
<button id="updateSettings" data-i18n="settings.update"></button>
|
||||
</div>
|
||||
<a id="downloadlog" onclick='dump(event.target);' data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
|
||||
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,10 +5,10 @@ var interfaceConfig = {
|
||||
INITIAL_TOOLBAR_TIMEOUT: 20000,
|
||||
TOOLBAR_TIMEOUT: 4000,
|
||||
DEFAULT_REMOTE_DISPLAY_NAME: "Fellow Jitster",
|
||||
DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME: "Speaker",
|
||||
DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME: "speaker",
|
||||
DEFAULT_LOCAL_DISPLAY_NAME: "me",
|
||||
SHOW_JITSI_WATERMARK: true,
|
||||
JITSI_WATERMARK_LINK: "http://jitsi.org",
|
||||
JITSI_WATERMARK_LINK: "https://jitsi.org",
|
||||
SHOW_BRAND_WATERMARK: false,
|
||||
BRAND_WATERMARK_LINK: "",
|
||||
SHOW_POWERED_BY: false,
|
||||
|
||||
57
lang/Translation.md
Normal file
@@ -0,0 +1,57 @@
|
||||
Jitsi Meet Translation
|
||||
==========================
|
||||
Jitsi Meet uses [i18next](http://i18next.com) library for translation.
|
||||
i18next uses separate json files for each language.
|
||||
|
||||
|
||||
Translating Jitsi Meet
|
||||
======================
|
||||
The translation of Jitsi Meet is integrated with Pootle. You can translate Jitsi Meet via our Pootle user interface on
|
||||
[http://translate.jitsi.org](http://translate.jitsi.org).
|
||||
|
||||
**WARNING: Please don't create or edit manually the language files! Please use our Pootle user interface!**
|
||||
|
||||
Development
|
||||
===========
|
||||
If you want to add new functionality for Jitsi Meet and you have texts that need to be translated please use our translation module.
|
||||
It is located in modules/translation. You must add key and value in main.json file in English for each translatable text.
|
||||
Than you can use the key to get the translated text for the current language.
|
||||
|
||||
**WARNING: Please don't change the other language files except main.json! They must be updated and translated via our Pootle user interface!**
|
||||
|
||||
You can add translatable text in the HTML:
|
||||
|
||||
|
||||
* **via attribute on HTML element** - add **data-i18n** attribute with value the key of the translatable text.
|
||||
|
||||
|
||||
```
|
||||
<span data-i18n="dialog.OK">OK</span>
|
||||
```
|
||||
|
||||
|
||||
You can also use APP.translation.generateTranslatonHTML(key, options) to get this HTML code as Javascript string.
|
||||
|
||||
|
||||
```
|
||||
APP.translation.generateTranslatonHTML("dialog.OK") // returns <span data-i18n="dialog.OK">OK</span>
|
||||
```
|
||||
|
||||
The value in the options parameter will be added in data-i18n-options attribute of the element.
|
||||
|
||||
**Note:** If you dynamically add HTML elements don't forget to call APP.translation.translateElement(jquery_selector) to translate the text initially.
|
||||
|
||||
* **via Javascript string** - call APP.translation.translateString(key, options). You can use that method to get the translated string in Javascript and then attach it in the HTML.
|
||||
|
||||
```
|
||||
APP.translation.translateString("dialog.OK") // returns the value for the key of the current language file. "OK" for example.
|
||||
```
|
||||
|
||||
For the available values of ``options`` parameter for the above methods of translation module see [i18next documentation](http://i18next.com/pages/doc_features).
|
||||
|
||||
**Note:** It is useful to add attributes in the HTML for persistent HTML elements because when the language is changed the text will be automatically translated.
|
||||
Otherwise you should call ``APP.translation.translateString`` and manually change the text every time the language is changed.
|
||||
|
||||
|
||||
|
||||
|
||||
7
lang/languages-bg.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en": "Английски",
|
||||
"bg": "Български",
|
||||
"de": "Немски",
|
||||
"tr": "Турски",
|
||||
"it": "Италиански"
|
||||
}
|
||||
7
lang/languages-de.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en": "Englisch",
|
||||
"bg": "Bulgarisch",
|
||||
"de": "Deutsch",
|
||||
"tr": "Türkisch",
|
||||
"it": "Italienisch"
|
||||
}
|
||||
7
lang/languages-fr.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en": "Anglais",
|
||||
"bg": "Bulgare",
|
||||
"de": "Allemand",
|
||||
"tr": "Turc",
|
||||
"it": "Italien"
|
||||
}
|
||||
7
lang/languages-it.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en": "Inglese",
|
||||
"bg": "Bulgaro",
|
||||
"de": "Tedesco",
|
||||
"tr": "Turco",
|
||||
"it": "Italiano"
|
||||
}
|
||||
5
lang/languages-tr.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"en": "İngilizce",
|
||||
"bg": "Bulgarca",
|
||||
"de": "Almanca"
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"en": "English",
|
||||
"bg": "Bulgarian"
|
||||
}
|
||||
"bg": "Bulgarian",
|
||||
"de": "German",
|
||||
"tr": "Turkish",
|
||||
"it": "Italian"
|
||||
}
|
||||
|
||||
224
lang/main-bg.json
Normal file
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"contactlist": "СПИСЪК С КОНТАКТИ",
|
||||
"connectionsettings": "Настройки на връзката",
|
||||
"poweredby": "powered by",
|
||||
"downloadlogs": "Изтегли логовете",
|
||||
"roomUrlDefaultMsg": "Конференцията се създава...",
|
||||
"participant": "Участник",
|
||||
"me": "аз",
|
||||
"speaker": "Говорител",
|
||||
"defaultNickname": "например __name__",
|
||||
"defaultPreziLink": "например __url__",
|
||||
"welcomepage": {
|
||||
"go": "Влез",
|
||||
"roomname": "Въведете име на стаята",
|
||||
"disable": "Не показвай страницата следващия път",
|
||||
"feature1": {
|
||||
"title": "Лесен за употреба",
|
||||
"content": "Не е необходимо да сваляте нищо. _app_ работи директно във вашия браузър. Просто споделете адреса на вашата конференция с другите за да започнете."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "",
|
||||
"content": "Видео конференциите могат да работят с по-малко от 128Kbps, а аудио конференциите и конференциите с споделен екран дори с по-малко."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Отворен код",
|
||||
"content": "__app__ е лицензиран под MIT лиценз. Можете свободно да го изтеглите, използвате, променяте и споделяте според тези лицензи."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Неограничен брой потребители",
|
||||
"content": "Няма изкуствени ограничения за броя на потребителите или участниците в конференция. Единствените ограничения са мощността на вашия сървър и качеството на интернет връзката му."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Споделяне на екрана",
|
||||
"content": "Лесно е да споделите екрана си с другите. __app__ е идеален за онлайн презентации, лекции и техническа подръжка."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Сигурни стаи",
|
||||
"content": "Нуждаете се от уединение? _app__ конферентните стай могат да бъдат защитени от парола за да се препазите от нежелани гости или прекъсвания."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Споделени бележки",
|
||||
"content": "__app__ използва Etherpad, с който можете да редактирате текст в реално време заедно."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Статистики за използване",
|
||||
"content": "Научете повече за своите потребители като интегрирате лесно Piwik, Google Analytics и други статистики за изполването."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Включи / Изключи микрофона",
|
||||
"videomute": "Спри / пусни камерата",
|
||||
"authenticate": "",
|
||||
"record": "Запис",
|
||||
"lock": "Заключи / отключи стаята",
|
||||
"invite": "Поканване на други",
|
||||
"chat": "",
|
||||
"prezi": "Сподели Prezi",
|
||||
"etherpad": "Споделяне на документ",
|
||||
"sharescreen": "Споделяне на екрана",
|
||||
"fullscreen": "Влез / Излез от Пълен екран",
|
||||
"sip": "Обади се на SIP номер",
|
||||
"Settings": "Настройки",
|
||||
"hangup": "Затвори",
|
||||
"login": "Влез",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Отвори / затвори чат",
|
||||
"filmstrip": "Покажи / скрий лентата с видеа",
|
||||
"contactlist": "Отвори / затвори контакт листа"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Въведете име в полето",
|
||||
"popover": "Избор на име"
|
||||
},
|
||||
"messagebox": "Въведете текст..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "НАСТРОЙКИ",
|
||||
"update": "Актуализиране",
|
||||
"name": "Име"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Натиснете за да<br/>промените името",
|
||||
"moderator": "Създателя на<br/>конференцията",
|
||||
"videomute": "Учасника е спрял<br/>камерата си.",
|
||||
"mute": "Учасника е с изключен микрофон",
|
||||
"kick": "Изгони",
|
||||
"muted": "Изключен микрофон",
|
||||
"domute": "Изключи микрофона"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "",
|
||||
"packetloss": "Загуба на пакети:",
|
||||
"resolution": "Резолюция:",
|
||||
"less": "Скрий",
|
||||
"more": "Покажи",
|
||||
"address": "Адрес:",
|
||||
"remoteport": "Отдалечен порт:",
|
||||
"remoteport_plural": "Отдалечени портове:",
|
||||
"localport": "Локален порт:",
|
||||
"localport_plural": "Локални портове:",
|
||||
"localaddress": "Локален адрес:",
|
||||
"localaddress_plural": "Локални адреси:",
|
||||
"remoteaddress": "Отдалечен адрес:",
|
||||
"remoteaddress_plural": "Отдалечени адреси:",
|
||||
"transport": "Транспорт:",
|
||||
"bandwidth": "",
|
||||
"na": "Върнете се тук за информацията относно вашата връзка, когато започне конференцията"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "връзката е прекъсната",
|
||||
"moderator": "Придобихте права на модератор!",
|
||||
"connected": "свързан",
|
||||
"somebody": "Някой",
|
||||
"me": "Аз",
|
||||
"focus": "Конферентен фокус",
|
||||
"focusFail": "__component__ не е на раположения - следващ опит след __ms__ секунди",
|
||||
"grantedTo": "Даване на роля модератор на __to__!",
|
||||
"grantedToUnknown": "Даване на роля модератор на $t(somebody)!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Бяхте изгонен от срещата!",
|
||||
"popupError": "Вашия браузър блокира попъп прозорците от този сайт. Моля позволете попъп прозорците от настройките на браузъра и опитайте пак.",
|
||||
"passwordError": "Тази конференция е защитена с парола. Само създателя и може да промени паролата.",
|
||||
"passwordError2": "Тази конференция не е защитена с парола. Само създателя и може да сложи парола.",
|
||||
"joinError": "Не можете да се присъедините към конференцията. Може би имате проблем с конфигурацията на сифурността. Моля обърнете се към администратора на услугата.",
|
||||
"connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "Възникна грешка при опит да бъде намерено разширението за споделяне на екран.",
|
||||
"failtoinstall": "Неуспешна инсталация на разширението за споделяне на екрана.",
|
||||
"failedpermissions": "Неуспешен опит за получаване на права за използване на микрофон и/или камера.",
|
||||
"bridgeUnavailable": "Jitsi Videobridge не е наличен. Моля опитайте пак!",
|
||||
"lockTitle": "Неуспешно заключване",
|
||||
"lockMessage": "Неуспешно заключване на конференцията.",
|
||||
"warning": "Внимание",
|
||||
"passwordNotSupported": "В момента не поддържаме стаи с пароли.",
|
||||
"sorry": "Съжаляваме",
|
||||
"internalError": "Вътрешна грешка [setRemoteDescription]",
|
||||
"unableToSwitch": "Неуспешен опит за смяна на видеото.",
|
||||
"SLDFailure": "Опа! Нещо се обърка и не успяхме да спрем микрофона! (SLD Failure)",
|
||||
"SRDFailure": "Опа! Нещо се обърка и не успяхме да спрем камерата! (SRD Failure)",
|
||||
"oops": "Опа!",
|
||||
"defaultError": "Възникна грешка",
|
||||
"passwordRequired": "Изисква се парола",
|
||||
"Ok": "ОК",
|
||||
"removePreziTitle": "Премахни Prezi",
|
||||
"removePreziMsg": "Сигурни ли сте, че искате да премахнете Prezi?",
|
||||
"sharePreziTitle": "Сподели Prezi",
|
||||
"sharePreziMsg": "Друг участник вече е споделил Prezi. Тази конференция позволява само да се споделя само едно Prezi.",
|
||||
"Remove": "Премахване",
|
||||
"Stop": "Спиране",
|
||||
"AuthMsg": "Нужна е идентификация, за да създадете стая:<br/><b>__room__</b></br> Може да се идентифицирате, за да създадете стая или да изчакате някой друг да го направи.",
|
||||
"Authenticate": "Идентификация",
|
||||
"Cancel": "Отказ",
|
||||
"retry": "Повторен опит",
|
||||
"logoutTitle": "Изход",
|
||||
"logoutQuestion": "Сигурни ли сте, че искате да излезете и да прекъснете конференцията?",
|
||||
"sessTerminated": "Сесията е прекъсната.",
|
||||
"hungUp": "Вие затворихте обаждането.",
|
||||
"joinAgain": "Песъединете се отново",
|
||||
"Share": "Споделяне",
|
||||
"preziLinkError": "Моля въведете правилен Prezi линк.",
|
||||
"Save": "Запазване",
|
||||
"recordingToken": "Въведете код за достъп за запис на конференцията",
|
||||
"Dial": "Набиране",
|
||||
"sipMsg": "Въведете SIP номер",
|
||||
"passwordCheck": "Сигурни ли сте, че искате да махнете паролата?",
|
||||
"passwordMsg": "Въведете парола, за да заключите стаята",
|
||||
"Invite": "Покани",
|
||||
"shareLink": "Сподели този линк с всеки, който искаш да поканиш",
|
||||
"settings1": "Конфигурирай конференцията",
|
||||
"settings2": "Участниците се присъединиха с изключен микрофон.",
|
||||
"settings3": "Изисквай имена<br/><br/>Въведете парола за да заключите стаята:",
|
||||
"yourPassword": "вашата парола",
|
||||
"Back": "Назад",
|
||||
"serviceUnavailable": "Услугата не е налична",
|
||||
"gracefulShutdown": "Услугата временно не е достъпна поради профилактика. Моля опитайте по-късно.",
|
||||
"Yes": "Да",
|
||||
"reservationError": "Грешка в системата за резервации",
|
||||
"reservationErrorMsg": "Грешка номер: __code__, съобщение: __msg__",
|
||||
"password": "парола",
|
||||
"userPassword": "потребителска парола",
|
||||
"token": "код за достъп"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Тази конференция е защитена с парола. Моля използвайте следния код за да се присъедините:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Покана за __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Здравей, Бих искал да те поканя в една __appName__ конференция, която създадох.",
|
||||
"",
|
||||
"",
|
||||
"Кликни на следния линк за да се присъединиш в конференцията.",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
"__appName__ поддържа __supportedBrowsers__, така че трябва да използваш един от тези браузъри.",
|
||||
"",
|
||||
"",
|
||||
"Ще се видим след секунда!"
|
||||
],
|
||||
"and": "и"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Грешка",
|
||||
"CONNECTING": "Свързване",
|
||||
"CONNFAIL": "Връзката е неуспешна",
|
||||
"AUTHENTICATING": "Идентификация",
|
||||
"AUTHFAIL": "Неуспешна идентификация",
|
||||
"CONNECTED": "Свързан",
|
||||
"DISCONNECTED": "Изключен",
|
||||
"DISCONNECTING": "Прекъсване на връзката",
|
||||
"ATTACHED": "Прикрепен"
|
||||
}
|
||||
}
|
||||
230
lang/main-de.json
Normal file
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"contactlist": "Kontaktliste",
|
||||
"connectionsettings": "Verbindungseinstellungen",
|
||||
"poweredby": "Betrieben von",
|
||||
"downloadlogs": "Log herunterladen",
|
||||
"roomUrlDefaultMsg": "Die Konferenz wird erstellt...",
|
||||
"participant": "Teilnehmer",
|
||||
"me": "ich",
|
||||
"speaker": "Sprecher",
|
||||
"defaultNickname": "Bsp.: __name__",
|
||||
"defaultPreziLink": "Bsp.: __url__",
|
||||
"welcomepage": {
|
||||
"go": "Los",
|
||||
"roomname": "Raumnamen eingeben",
|
||||
"disable": "Diese Seite beim nächsten Betreten nicht mehr anzeigen",
|
||||
"feature1": {
|
||||
"title": "Einfach zu benutzen",
|
||||
"content": "Kein Download nötig. __app__ läuft direkt im Browser. Einfach die Konferenzadresse teilen und los geht's."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "Niedrige Bandbreite",
|
||||
"content": "Videokonferenzen mit mehreren Teilnehmen mit weniger als 128Kpbs. Bildschirmfreigaben und Telefonkonferenzen kommen sogar mit noch weniger Bandbreite aus."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Open Source",
|
||||
"content": "__app__ steht unter der MIT Lizenz. __app__ kann gemäss der Lizenz heruntergeladen, verwendet, verändert und weitergegeben werden."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Unbegrenzte Anzahl Benutzer",
|
||||
"content": "Es gibt keine künstliche Beschränkung der Anzahl der Benutzer oder Konferenzteilnehmer. Die Leistung des Servers und die Bandbreite sind die einzigen limitierenden Faktoren."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Bildschirmfreigabe",
|
||||
"content": "Es ist ganz einfach den Bildschirm zu teilen. __app__ ist ideal für Online-Präsentationen, Vorlesungen und Fernwartungsanfragen."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Sichere Konferenzen",
|
||||
"content": "Privatsphäre gewünscht? __app__ Konferenzen können mit einem Passwort geschützt werden um ungebetene Gäste fernzuhalten und Unterbrechungen zu vermeiden."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Freigegebene Notizen",
|
||||
"content": "__app__ verwendent Etherpad, ein Editor zur kollaborativen Bearbeitung von Texten."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Benutzungsstatistiken",
|
||||
"content": "Die Verwendung kann durch die Integration mit Piwik, Google Analytics und anderen Überwachungs- und Statistikprogrammen protokolliert werden."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Stummschaltung aktivieren / deaktivieren",
|
||||
"videomute": "Kamera starten / stoppen",
|
||||
"authenticate": "Anmelden",
|
||||
"record": "Aufnehmen",
|
||||
"lock": "Raum schützen / Schutz aufheben",
|
||||
"invite": "Andere einladen",
|
||||
"chat": "Chat öffnen / schliessen",
|
||||
"prezi": "Prezi freigeben",
|
||||
"etherpad": "Geteiltes Dokument",
|
||||
"sharescreen": "Bildschirm freigeben",
|
||||
"fullscreen": "Vollbildmodus aktivieren / deaktivieren",
|
||||
"sip": "SIP Nummer anrufen",
|
||||
"Settings": "Einstellungen",
|
||||
"hangup": "Auflegen",
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Chat öffnen / schliessen",
|
||||
"filmstrip": "Videovorschauen anzeigen / verstecken",
|
||||
"contactlist": "Kontaktliste öffnen / schliessen"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Nickname im Eingabefeld eingeben",
|
||||
"popover": "Einen Namen auswählen"
|
||||
},
|
||||
"messagebox": "Text eingeben..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Einstellungen",
|
||||
"update": "Aktualisieren",
|
||||
"name": "Name"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Klicken um den Anzeigenamen zu bearbeiten",
|
||||
"moderator": "Besitzer dieser Konferenz",
|
||||
"videomute": "Teilnehmer hat die Kamera pausiert.",
|
||||
"mute": "Teilnehmer ist stumm geschaltet",
|
||||
"kick": "Hinauswerfen",
|
||||
"muted": "Stummgeschaltet",
|
||||
"domute": "Stummschalten"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "Bitrate:",
|
||||
"packetloss": "Paketverlust:",
|
||||
"resolution": "Auflösung:",
|
||||
"less": "Weniger anzeigen",
|
||||
"more": "Mehr anzeigen",
|
||||
"address": "Adresse:",
|
||||
"remoteport": "Entfernter Port:",
|
||||
"remoteport_plural": "Entfernte Ports:",
|
||||
"localport": "Lokaler Port:",
|
||||
"localport_plural": "Lokale Ports:",
|
||||
"localaddress": "Lokale Adresse:",
|
||||
"localaddress_plural": "Lokale Adressen:",
|
||||
"remoteaddress": "Entfernte Adresse:",
|
||||
"remoteaddress_plural": "Entfernte Adressen:",
|
||||
"transport": "Protokoll:",
|
||||
"bandwidth": "Geschätzte Bandbreite:",
|
||||
"na": "Verbindungsdaten erneut anzeigen wenn die Konferenz begonnen hat"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "getrennt",
|
||||
"moderator": "Moderatorenrechte vergeben",
|
||||
"connected": "verbunden",
|
||||
"somebody": "Jemand",
|
||||
"me": "Ich",
|
||||
"focus": "Konferenz-Organisator",
|
||||
"focusFail": "__component__ ist im Moment nicht verfügbar - wiederholen in __ms__ Sekunden",
|
||||
"grantedTo": "Moderatorenrechte an __to__ vergeben.",
|
||||
"grantedToUnknown": "Moderatorenrechte an $t(somebody) vergeben."
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.",
|
||||
"popupError": "Ihr Browser blockiert Popups von dieser Webseite. Bitte erlauben Sie Popups in den Sicherheitseinstellungen und versuchen Sie es erneut.",
|
||||
"passwordError": "Diese Konferenz ist mit einem Paswort geschützt. Nur der Besitzer der Konferenz kann ein Passwort vergeben.",
|
||||
"passwordError2": "Diese Konferenzt ist nicht mit einem Passwort geschützt. Nur der Besitzer der Konferenz kann ein Passwort vergeben.",
|
||||
"joinError": "Oh! Der Konferenz konnte nicht beigetreten werden. Diese könnte ein Problem mit den Sicherheitseinstellungen sein. Bitte kontaktieren Sie den Administrator des Dienstes.",
|
||||
"connectError": "Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden.",
|
||||
"connecting": "",
|
||||
"error": "Fehler",
|
||||
"detectext": "Fehler bei der Erkennung der Bildschirmfreigabeerweiterung.",
|
||||
"failtoinstall": "Die Bildschirmfreigabeerweiterung konnte nicht installiert werden.",
|
||||
"failedpermissions": "Die Zugriffsberechtigungen auf das Mikrofon und/oder die Kamera konnte nicht eingeholt werden.",
|
||||
"bridgeUnavailable": "Die Jitsi Videobridge ist momentan nicht erreichbar. Bitte versuchen Sie es später noch einmal.",
|
||||
"lockTitle": "Sperren fehlgeschlagen",
|
||||
"lockMessage": "Die Konferenz konnte nicht gesperrt werden.",
|
||||
"warning": "Warnung",
|
||||
"passwordNotSupported": "Passwörter für Räume werden nicht unterstützt.",
|
||||
"sorry": "Entschuldigung",
|
||||
"internalError": "Interner Anwendungsfehler [setRemoteDescription]",
|
||||
"unableToSwitch": "Der Videodatenstrom kann nicht gewechselt werden.",
|
||||
"SLDFailure": "Oh! Die Stummschaltung konnte nicht aktiviert werden. (SLD Fehler)",
|
||||
"SRDFailure": "Oh! Das Video konnte nicht gestoppt werden. (SRD Fehler)",
|
||||
"oops": "Oh!",
|
||||
"defaultError": "Es ist ein Fehler aufgetreten",
|
||||
"passwordRequired": "Passwort erforderlich",
|
||||
"Ok": "OK",
|
||||
"removePreziTitle": "Prezi entfernen",
|
||||
"removePreziMsg": "Sind Sie sich sicher dass sie Prezi entfernen möchten?",
|
||||
"sharePreziTitle": "Ein Prezi teilen",
|
||||
"sharePreziMsg": "Ein anderer Teilnehmer teilt bereits ein Prezi. Diese Konferenz kann nur eine Prezi auf einmal anzeigen.",
|
||||
"Remove": "Entfernen",
|
||||
"Stop": "Stopp",
|
||||
"AuthMsg": "Für die Erstellung des Raums ist eine Authentifizierung erforderlich. <br/><b>__room__</b><br/>Sie können sich entweder anmelden oder warten bis jemand anderes die Authentifizierung vornimmt.",
|
||||
"Authenticate": "Anmelden",
|
||||
"Cancel": "Abbrechen",
|
||||
"retry": "Wiederholen",
|
||||
"logoutTitle": "Abmelden",
|
||||
"logoutQuestion": "Sind Sie sicher dass Sie sich abmelden und die Konferenz verlassen möchten?",
|
||||
"sessTerminated": "Sitzung beendet",
|
||||
"hungUp": "Anruf beendet",
|
||||
"joinAgain": "Erneut beitreten",
|
||||
"Share": "Teilen",
|
||||
"preziLinkError": "Bitte einen gültigen Prezi-Link angeben.",
|
||||
"Save": "Speichern",
|
||||
"recordingToken": "Aufnahme-Token eingeben",
|
||||
"Dial": "Wählen",
|
||||
"sipMsg": "Geben Sie eine SIP Nummer ein",
|
||||
"passwordCheck": "Sind Sie sicher dass Sie das Passwort entfernen möchten?",
|
||||
"passwordMsg": "Passwort setzen um den Raum zu schützen",
|
||||
"Invite": "Einladen",
|
||||
"shareLink": "Teilen Sie diesen Link mit jedem den Sie einladen möchten",
|
||||
"settings1": "Konferenz einrichten",
|
||||
"settings2": "Teilnehmer treten stummgeschaltet bei",
|
||||
"settings3": "Nickname erforderlich<br/><br/>Setzen Sie ein Passwort um den Raum zu schützen:",
|
||||
"yourPassword": "Ihr Passwort",
|
||||
"Back": "Zurück",
|
||||
"serviceUnavailable": "Dienst nicht verfügbar",
|
||||
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"Yes": "Ja",
|
||||
"reservationError": "Fehler im Reservationssystem",
|
||||
"reservationErrorMsg": "Fehler, Nummer: __code__, Nachricht: __msg__",
|
||||
"password": "Passwort",
|
||||
"userPassword": "Benutzerpasswort",
|
||||
"token": "Token"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Diese Konferenz ist Passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Einladung zu einer __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Hallo!",
|
||||
"",
|
||||
"",
|
||||
"Ich möchte dich zu einer eben erstellten __appName__-Konferenz einladen.",
|
||||
"",
|
||||
"",
|
||||
"Bitte klicke auf den folgenden Link um der Konferenz ebenfalls beizutreten:",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" Bitte beachte, dass __appName__ momentan nur mit einem der Browser __supportedBrowsers__ verwendet werden kann.",
|
||||
"",
|
||||
"",
|
||||
"Bis gleich!"
|
||||
],
|
||||
"and": "und"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Fehler",
|
||||
"CONNECTING": "Verbindung wird hergestellt",
|
||||
"CONNFAIL": "Verbindungsaufbau gescheitert",
|
||||
"AUTHENTICATING": "Anmeldung läuft",
|
||||
"AUTHFAIL": "Authentifizierung fehlgeschlagen",
|
||||
"CONNECTED": "Verbunden",
|
||||
"DISCONNECTED": "Getrennt",
|
||||
"DISCONNECTING": "Verbindung wird getrennt",
|
||||
"ATTACHED": "Angehängt"
|
||||
}
|
||||
}
|
||||
227
lang/main-fr.json
Normal file
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"contactlist": "Liste de contacts",
|
||||
"connectionsettings": "Paramètres de connexion",
|
||||
"poweredby": "propulsé par",
|
||||
"downloadlogs": "Téléchargement des logs",
|
||||
"roomUrlDefaultMsg": "Votre conférence est en cours de création...",
|
||||
"participant": "Participant",
|
||||
"me": "moi",
|
||||
"speaker": "Haut-parleur",
|
||||
"defaultNickname": "ex: __name__",
|
||||
"defaultPreziLink": "e.g. __url__",
|
||||
"welcomepage": {
|
||||
"go": "Créer",
|
||||
"roomname": "Saisissez un nom de salle",
|
||||
"disable": "Ne pas afficher cette page lors de ma prochaine visite",
|
||||
"feature1": {
|
||||
"title": "Simple à utiliser",
|
||||
"content": "Aucun téléchargement requis. __app__ s'utilise directement depuis votre navigateur. Partager simplement l'URL de votre conférence avec les autres pour commencer."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "Faible bande passante",
|
||||
"content": "Les vidéo conférences à plusieurs participants nécessitent moins de 128 kbps. Le partage d'écran et les conférences avec seulement de l'audio sont possibles avec beaucoup moins de débit."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Open source",
|
||||
"content": "__app__ est sous licence MIT. Vous êtes libre de le télécharger, l'utiliser, le modifier et le partager sous cette licence."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Nombre d'utilisateurs illimité",
|
||||
"content": "Il n'y a pas de restrictions artificielles concernant le nombre d'utilisateurs ou de participants à une conférence. La puissance du serveur et la bande passante sont les seuls facteurs limitants."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Partage d'écan",
|
||||
"content": "C'est facile de partager votre écran avec d'autres personnes. __app__ est idéal pour les présentations en ligne, les cours, et les sessions de support technique."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Salles sécurisées",
|
||||
"content": "Besoin de confidentialité? Les salles de conférence __app__ peuvent être sécurisées par un mot de passe pour exclure des invités non désirées, et prévenir des interruptions. "
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Notes partagées",
|
||||
"content": "__app__ propose Etherpad, un éditeur de texte collaboratif en temps réel qui est parfait pour les procès-verbaux, l'édition d'articles et plus encore."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Statistiques d'utilisation",
|
||||
"content": "Connaissez mieux vos utilisateurs avec une intégration facile de Piwik, Google Analytics et d'autres systèmes de statistiques et supervision d'utilisation."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Muet / Actif",
|
||||
"videomute": "Démarrer / Arrêter la caméra",
|
||||
"authenticate": "",
|
||||
"record": "Enregistrer",
|
||||
"lock": "Verrouiller / déverrouiller la salle",
|
||||
"invite": "Inviter des participants",
|
||||
"chat": "",
|
||||
"prezi": "Partager une présentation Prezi",
|
||||
"etherpad": "Partager un document",
|
||||
"sharescreen": "Partager mon écran",
|
||||
"fullscreen": "Activer / Désactiver le plein écran",
|
||||
"sip": "Appeler un numéro SIP",
|
||||
"Settings": "Paramètres",
|
||||
"hangup": "Raccrocher",
|
||||
"login": "Connexion",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Ouvrir / fermer le chat",
|
||||
"filmstrip": "Montrer / cacher ma vidéo miniature",
|
||||
"contactlist": "Ouvrir / fermer ma liste de contacts"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Saisissez un pseudonyme dans le champ ci-dessous",
|
||||
"popover": "Choisissez un pseudonyme"
|
||||
},
|
||||
"messagebox": "Saisissez votre texte..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "PARAMÈTRES",
|
||||
"update": "Mise à jour",
|
||||
"name": "Nom"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Cliquez pour modifier<br/>votre nom",
|
||||
"moderator": "Le propriétaire de<br/>cette conférence",
|
||||
"videomute": "Un participant a<br/>arrêté sa caméra.",
|
||||
"mute": "Un participant a coupé son micro",
|
||||
"kick": "Exclure",
|
||||
"muted": "Coupé",
|
||||
"domute": "Couper le son"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "Débit",
|
||||
"packetloss": "Perte de paquets:",
|
||||
"resolution": "Résolution:",
|
||||
"less": "Cacher le détail",
|
||||
"more": "Montrer le détail",
|
||||
"address": "Adresse:",
|
||||
"remoteport": "Port distant:",
|
||||
"remoteport_plural": "Ports distants:",
|
||||
"localport": "Port local:",
|
||||
"localport_plural": "Ports locaux:",
|
||||
"localaddress": "Adresse locale:",
|
||||
"localaddress_plural": "Adresses locales:",
|
||||
"remoteaddress": "Adresse distante:",
|
||||
"remoteaddress_plural": "Adresses distantes:",
|
||||
"transport": "Transport:",
|
||||
"bandwidth": "Bande passante estimée:",
|
||||
"na": "Revenez ici pour afficher les informations de connexion une fois la conférence démarrée"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "Déconnecté",
|
||||
"moderator": "Droits modérateur accordés!",
|
||||
"connected": "Connecté",
|
||||
"somebody": "Quelqu'un",
|
||||
"me": "Moi",
|
||||
"focus": "",
|
||||
"focusFail": "__component__ n'est pas disponible - réessayez dans __ms__ sec",
|
||||
"grantedTo": "Droits modérateur accordés à __to__!",
|
||||
"grantedToUnknown": "Droits modérateur accordés à $t(somebody)!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Oups! Vous avez été renvoyé de la réunion!",
|
||||
"popupError": "Votre navigateur bloque les popups pour ce site. Activez les popups pour ce site dans vos paramètres de sécurité et réessayez.",
|
||||
"passwordError": "Cette conversation est protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.",
|
||||
"passwordError2": "Cette conversation n'est pas protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.",
|
||||
"joinError": "Oups! La conférence ne peut être rejointe. Il y a peut-être un souci avec les paramètres de sécurité. Contactez l'administrateur.",
|
||||
"connectError": "Oups! Un problème est survenu et la connexion à la conférence est impossible.",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "Une erreur est survenue pendant la détection de l'extension de partage d'écran.",
|
||||
"failtoinstall": "Échec de l'installation de l'extension de partage d'écran",
|
||||
"failedpermissions": "Échec d'obtention des permissions pour utiliser le micro et/ou la caméra local(e)",
|
||||
"bridgeUnavailable": "Le pont de visioconférence Jitsi est indisponible pour le moment. Réessayez plus tard!",
|
||||
"lockTitle": "Échec du verrouillage",
|
||||
"lockMessage": "Impossible de verrouiller la conférence.",
|
||||
"warning": "Avertissement",
|
||||
"passwordNotSupported": "Les mots de passe de conférence ne sont pas supportés.",
|
||||
"sorry": "Désolé",
|
||||
"internalError": "Une erreur interne de l'application est survenue [setRemoteDescription]",
|
||||
"unableToSwitch": "Impossible de passer le flux vidéo.",
|
||||
"SLDFailure": "Oups! Un problème est survenu et le micro n'a pas été coupé! (Échec SLD)",
|
||||
"SRDFailure": "Oups! Un problème est survenu et la caméra n'a pas été coupée! (Échec SRD)",
|
||||
"oops": "Oups!",
|
||||
"defaultError": "Une erreur est survenue",
|
||||
"passwordRequired": "Mot de passe requis",
|
||||
"Ok": "Ok",
|
||||
"removePreziTitle": "Supprimer la présentation Prezi",
|
||||
"removePreziMsg": "Voulez-vous vraiment supprimer votre présentation Prezi?",
|
||||
"sharePreziTitle": "Partager une présentation Prezi",
|
||||
"sharePreziMsg": "Un autre participant partage déjà une présentation Prezi. Cette conférence autorise une seule présentation Prezi à la fois.",
|
||||
"Remove": "Supprimer",
|
||||
"Stop": "Arrêter",
|
||||
"AuthMsg": "L'authentification est requise pour créer la conférence:<br/><b>__room__ </b></br> Vous pouvez vous authentifier pour créer la conférence ou attendre que quelqu'un le fasse pour vous.",
|
||||
"Authenticate": "Authentifiez-vous",
|
||||
"Cancel": "Annuler",
|
||||
"retry": "Réessayer",
|
||||
"logoutTitle": "Déconnexion",
|
||||
"logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence?",
|
||||
"sessTerminated": "Session terminée",
|
||||
"hungUp": "Vous avez raccroché et quitté la conférence",
|
||||
"joinAgain": "Rejoignez à nouveau la conférence",
|
||||
"Share": "Partager",
|
||||
"preziLinkError": "Fournissez s'il vous plaît un lien prezi fonctionnel.",
|
||||
"Save": "Sauvegarder",
|
||||
"recordingToken": "Saisissez un jeton d'enregistrement",
|
||||
"Dial": "Composer",
|
||||
"sipMsg": "Saisissez un numéro SIP",
|
||||
"passwordCheck": "Voulez-vous vraiment supprimer votre mot de passe?",
|
||||
"passwordMsg": "Saisissez un mot de passe pour verrouiller la conférence",
|
||||
"Invite": "Inviter",
|
||||
"shareLink": "Partagez ce lien avec toutes les personnes que vous voulez inviter",
|
||||
"settings1": "Configurez votre conférence",
|
||||
"settings2": "Les participants rejoignent la conférence en étant muets.",
|
||||
"settings3": "Pseudonymes requis<br/><br/>Saisissez un mot de passe pour verrouiller la conférence:",
|
||||
"yourPassword": "Votre mot de passe",
|
||||
"Back": "Retour",
|
||||
"serviceUnavailable": "Service indisponible",
|
||||
"gracefulShutdown": "Le service est actuellement en maintenance. Réessayez plus tard.",
|
||||
"Yes": "Oui",
|
||||
"reservationError": "Erreur du système de réservation",
|
||||
"reservationErrorMsg": "Code d'erreur: __code__, message: __msg__",
|
||||
"password": "mot de passe",
|
||||
"userPassword": "mot de passe utilisateur",
|
||||
"token": "jeton"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Cette conférence est protégée par un mot de passe. Utilisez le code suivant pour la rejoindre:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Invitation à la conférence __appName__ : __conferenceName__",
|
||||
"body": [
|
||||
"Bonjour, je vous invite à la conférence __appName__ que je viens de créer.",
|
||||
"",
|
||||
"",
|
||||
"Cliquez sur le lien suivant pour rejoindre la conférence.",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" Notez que __appName__ est actuellement seulement supporté par __supportedBrowsers__, vous devez donc utiliser un de ces navigateurs.",
|
||||
"",
|
||||
"",
|
||||
"À tout de suite dans la conférence!"
|
||||
],
|
||||
"and": "et"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Erreur",
|
||||
"CONNECTING": "Connexion en cours...",
|
||||
"CONNFAIL": "Échec de la Connexion",
|
||||
"AUTHENTICATING": "Authentification en cours...",
|
||||
"AUTHFAIL": "Échec de l'authentification",
|
||||
"CONNECTED": "Connecté",
|
||||
"DISCONNECTED": "Déconnecté",
|
||||
"DISCONNECTING": "Déconnexion en cours...",
|
||||
"ATTACHED": "Attachée"
|
||||
}
|
||||
}
|
||||
227
lang/main-it.json
Normal file
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"contactlist": "LISTA CONTATTI",
|
||||
"connectionsettings": "Impostazioni Connessione",
|
||||
"poweredby": "powered by",
|
||||
"downloadlogs": "Scarica logs",
|
||||
"roomUrlDefaultMsg": "La tua conferenza sta per essere creata...",
|
||||
"participant": "Partecipante",
|
||||
"me": "io",
|
||||
"speaker": "Relatore",
|
||||
"defaultNickname": "es. __nome__",
|
||||
"defaultPreziLink": "es. __url__",
|
||||
"welcomepage": {
|
||||
"go": "VAI",
|
||||
"roomname": "Inserisci Nome Stanza",
|
||||
"disable": "Non visualizzare questa pagina la prossima volta",
|
||||
"feature1": {
|
||||
"title": "Semplice da usare",
|
||||
"content": "Nessun download richiesto. __app__ funziona direttamente nel tuo browser. Condividi semplicemente l'URL della tua conferenza con altri per iniziare."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "Poca banda",
|
||||
"content": "Conferenze video multi utente funzionano con appena 128Kbps. La condivisione dello schermo ed conferenze solo audio sono possibili con molto meno."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Open source",
|
||||
"content": "__app__ è sotto licenza MIT. Sei libero di scaricarla, usarla, modificarla e condividerla con la medesima licenza."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Utenti illimitati",
|
||||
"content": "Non ci sono restrizioni sul numero di utenti per una conferenza. La potenza del server e la banda a disposizione sono gli unici fattori limitanti."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Condivisione Schermo",
|
||||
"content": "é facile condividere il tuo schermo con altri. __app__ è l'ideale per presentazioni online, letture, e sessioni di supporto tecnico."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Stanze sicure",
|
||||
"content": "Hai bisogno di più privacy? Le conferenze di __app__ possono essere rese sicure con una password per escludere ospiti non desiderati e prevenire interruzioni."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Note condivise",
|
||||
"content": "__app__ utilizza Etherpad, un editor di testo real-time e collaborativo che è ottimo per meeting, scrivere articoli e tanto altro."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Statistiche di utilizzo",
|
||||
"content": "Impara come i tuoi utenti lo utilizzano con la facile integrazione con PiWik, Google Analytics, e altri sistemi di statistica e monitor dell'utilizzo."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Microfono Attiva / Disattiva",
|
||||
"videomute": "Abilita / Disabilita video",
|
||||
"authenticate": "",
|
||||
"record": "Registra",
|
||||
"lock": "Blocca / Sblocca Stanza",
|
||||
"invite": "Invita altri",
|
||||
"chat": "",
|
||||
"prezi": "Condividi con Prezi",
|
||||
"etherpad": "Documento condiviso",
|
||||
"sharescreen": "Condividi schermo",
|
||||
"fullscreen": "Entra / Esci da schermo intero",
|
||||
"sip": "Chiama numero SIP",
|
||||
"Settings": "Impostazioni",
|
||||
"hangup": "Termina",
|
||||
"login": "Login",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Apri / Chiudi chat",
|
||||
"filmstrip": "Mostra / Nascondi miniature",
|
||||
"contactlist": "Apri / Chiudi la lista contatti"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Scegli un nickname nel box qui sotto",
|
||||
"popover": "Scegli un nickname"
|
||||
},
|
||||
"messagebox": "Inserisci testo..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "IMPOSTAZIONI",
|
||||
"update": "Aggiorna",
|
||||
"name": "Nome"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Clicca per modificare il tuo<br/>nome visualizzato",
|
||||
"moderator": "Il proprietario<br/>della conferenza",
|
||||
"videomute": "Il partecipante ha<br/>fermato il video.",
|
||||
"mute": "Il partecipante è in muto",
|
||||
"kick": "Espelli",
|
||||
"muted": "Audio disattivato",
|
||||
"domute": "Disattiva audio"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "Bitrate:",
|
||||
"packetloss": "Perdita pacchetti:",
|
||||
"resolution": "Risoluzione:",
|
||||
"less": "Mostra meno",
|
||||
"more": "Mostra di più",
|
||||
"address": "Indirizzo:",
|
||||
"remoteport": "Porta remota:",
|
||||
"remoteport_plural": "Porte remote:",
|
||||
"localport": "Porta locale:",
|
||||
"localport_plural": "Porte locali:",
|
||||
"localaddress": "Indirizzo locale:",
|
||||
"localaddress_plural": "Indirizzi locali:",
|
||||
"remoteaddress": "Indirizzo remoto:",
|
||||
"remoteaddress_plural": "Indirizzi remoti:",
|
||||
"transport": "Trasporto:",
|
||||
"bandwidth": "Banda stimata:",
|
||||
"na": "Ritorna qui per informazioni sulla connessione una volta che la conferenza inizia"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "disconnesso",
|
||||
"moderator": "Impostati i permessi di moderatore!",
|
||||
"connected": "connesso",
|
||||
"somebody": "Qualcuno",
|
||||
"me": "io",
|
||||
"focus": "Focus su conferenza",
|
||||
"focusFail": "__component__ non disponibile - riprova in __ms__ sec",
|
||||
"grantedTo": "Permessi di moderatore garantiti a __to__!",
|
||||
"grantedToUnknown": "Permessi di moderatore garantiti a $t(somebody)!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Accidenti! Sei stato espulso dalla conferenza !",
|
||||
"popupError": "Il tuo browser sta bloccando le finestre popup da questo sito. Abilita i popup tra le impostazioni di sicurezza del tuo browser e riprova.",
|
||||
"passwordError": "Questa conversazione è protetta da una password. Solo il proprietario della conferenza può impostare una password.",
|
||||
"passwordError2": "Questa conversazione non è al momento protetta da una password. Solo il proprietario può impostare la password.",
|
||||
"joinError": "Oops! Non puoi entrare nella conferenza. Ci deve essere qualche problema con la configurazione di sicurezza. Contattare l'amministratore di sistema.",
|
||||
"connectError": "Oops! Qualcosa è andato storto e non ti puoi collegare alla conferenza.",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "Errore durante il rilevamento dell'estensione per il desktopsharing.",
|
||||
"failtoinstall": "Impossibile installare l'estensione per il desktop sharing",
|
||||
"failedpermissions": "Impossibile ottenere i permessi per usare il microfono e/o il video locale.",
|
||||
"bridgeUnavailable": "Il Videobridge non è al momento disponibile. Si prega di riprovare più tardi!",
|
||||
"lockTitle": "Blocco fallito",
|
||||
"lockMessage": "Impossibile bloccare la conferenza.",
|
||||
"warning": "Attenzione",
|
||||
"passwordNotSupported": "Le password sulla stanza non sono al momento supportate.",
|
||||
"sorry": "Spiacente",
|
||||
"internalError": "Errore interno dell'applicazione [setRemoteDescription]",
|
||||
"unableToSwitch": "Impossibile cambiare lo stream video.",
|
||||
"SLDFailure": "Oops! Qualcosa è andato storto e non è possibile silenziare il microfono! (Errore SLD)",
|
||||
"SRDFailure": "Oops! Qualcosa è andato storto e non è possibile fermare il video! (Errore SRD)",
|
||||
"oops": "Oops!",
|
||||
"defaultError": "C'è stato qualche tipo di errore",
|
||||
"passwordRequired": "Richiesta password ",
|
||||
"Ok": "Ok",
|
||||
"removePreziTitle": "Rimuovi Prezi",
|
||||
"removePreziMsg": "Sei sicuro di voler rimuovere il tuo Prezi?",
|
||||
"sharePreziTitle": "Condividi un Prezi",
|
||||
"sharePreziMsg": "Un altro partecipante sta già condividendo un Prezi. Questa conferenza permette un solo Prezi alla volta.",
|
||||
"Remove": "Rimuovi",
|
||||
"Stop": "Ferma",
|
||||
"AuthMsg": "Autenticazione richiesta per creare la stanza:<br/><b>__room__ </b></br> Puoi autenticarti per creare la stanza o aspettare che qualcun altro lo faccia per te.",
|
||||
"Authenticate": "Autenticazione",
|
||||
"Cancel": "Annulla",
|
||||
"retry": "Riprova",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutQuestion": "Vuoi disconnetterti e interrompere la conferenza ?",
|
||||
"sessTerminated": "Sessione Terminata",
|
||||
"hungUp": "Hai terminato la conversazione",
|
||||
"joinAgain": "Entra ancora",
|
||||
"Share": "Condividi",
|
||||
"preziLinkError": "Fornire un link Prezi esatto.",
|
||||
"Save": "Salva",
|
||||
"recordingToken": "Inserire token di registrazione",
|
||||
"Dial": "Componi",
|
||||
"sipMsg": "Inserire numero SIP",
|
||||
"passwordCheck": "Confermi la rimozione della password?",
|
||||
"passwordMsg": "Imposta una password per bloccare la stanza",
|
||||
"Invite": "Invita",
|
||||
"shareLink": "Condividi questo link con tutte le persone che vuoi invitare",
|
||||
"settings1": "Configura la tua conferenza",
|
||||
"settings2": "Partecipanti connessi in muto",
|
||||
"settings3": "Richiedi nicknames<br/><br/>Imposta una password per bloccare la tua stanza:",
|
||||
"yourPassword": "la tua password",
|
||||
"Back": "Indietro",
|
||||
"serviceUnavailable": "Servizio non disponibile",
|
||||
"gracefulShutdown": "Il nostro servizio è al momento spento per manutenzione. Si prega di riprovare più tardi.",
|
||||
"Yes": "Sì",
|
||||
"reservationError": "Errore di sistema in prenotazione",
|
||||
"reservationErrorMsg": "Codice di errore: __code__, messaggio: __msg__",
|
||||
"password": "password",
|
||||
"userPassword": "password utente",
|
||||
"token": "token"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Questa conferenza è protetta da password. Utilizzare il seguente PIN alla connessione:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Invito su __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Ciao, Vorrei invitarti alla conferenza che ho appena creato su __appName__ .",
|
||||
"",
|
||||
"",
|
||||
"Cliccare sul seguente link per entrare nella conferenza.",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
"NOTA: __appName__ è al momento supportato da questi browsers: __supportedBrowsers__, è necessario utilizzare uno di questi programmi per poter entrare.",
|
||||
"",
|
||||
"",
|
||||
"Ci sentiamo tra un secondo!"
|
||||
],
|
||||
"and": "e"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Errore",
|
||||
"CONNECTING": "Connessione",
|
||||
"CONNFAIL": "Connessione non riuscita",
|
||||
"AUTHENTICATING": "Autenticazione",
|
||||
"AUTHFAIL": "Autenticazione fallita",
|
||||
"CONNECTED": "Connesso",
|
||||
"DISCONNECTED": "Disconnesso",
|
||||
"DISCONNECTING": "Disconnessione in corso",
|
||||
"ATTACHED": "Collegato"
|
||||
}
|
||||
}
|
||||
173
lang/main-tr.json
Normal file
@@ -0,0 +1,173 @@
|
||||
{
|
||||
"contactlist": "KİŞİ LİSTESİ",
|
||||
"connectionsettings": "Bağlantı Ayarları",
|
||||
"poweredby": "Gücünün kaynağı",
|
||||
"downloadlogs": "Günlükleri indir",
|
||||
"welcomepage": {
|
||||
"go": "GİT",
|
||||
"roomname": "Oda adı girin",
|
||||
"disable": "Sonraki girişimde bu sayfayı gösterme",
|
||||
"feature1": {
|
||||
"title": "Kullanımı kolay",
|
||||
"content": "İndirmeye gerek yok. __app__ tarayıcınızda doğrudan çalışır. Başlamak için görüşme bağlantısını URL diğerleri ile paylaşın."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "Düşük bant genişliği ihtiyacı",
|
||||
"content": "Ekran paylaşımı ve sadece ses ile çok katılımcılı video görüşmeleri, 128Kbps bağlantı ile mümkündür."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Açık kaynak kodlu",
|
||||
"content": "__app__ MIT ile lisanslanmıştır. Bu lisansa uygun olarak indirmek, kullanmak, değiştirmek ve paylaşmakta özgürsün."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Sınırsız sayıda kullanıcı",
|
||||
"content": "Kullanıcılar veya konferans katılımcılarının sayısında hiçbir yapay kısıtlama yoktur. Sadece sunucun güç ve bant genişliği, sınırlayıcı unsurdur."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Ekran paylaşımı",
|
||||
"content": "Diğerlerinle ekranınızı kolayca paylaşın. __app__ çevrimiçi sunumlar, dersler ve teknik destek oturumları için idealdir."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Güvenli odalar",
|
||||
"content": "Biraz gizliliğe ihtiyacınız var? __app__ görüşme odaları, istemeyen misafirleri uzak tutmak ve kesinleri önlemek için bir parola ile güvence altına alınabilir."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Paylaşımlı notlar",
|
||||
"content": "__app__ Etherpad içerir, gerçek zamanlı bir ortak çalışma metin düzenleyicisidir. Görüşme tutanakları, makale yazımı ve daha fazlası için biçilmiş kaftandır."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Kullanım istatistikleri",
|
||||
"content": "Piwik, Google Analytics ve diğer kullanım izleme ve istatistik sistemleri ile kolay tümleştirmeyle kullanıcılar hakkında bilgi edinin."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Sessiz / Sesli",
|
||||
"videomute": "Kamera başlat / durdur",
|
||||
"authenticate": "",
|
||||
"record": "Kaydet",
|
||||
"lock": "Odayı kilitle / kilit aç",
|
||||
"invite": "Arkadaşlarını davet et",
|
||||
"chat": "",
|
||||
"prezi": "Prezi paylaş",
|
||||
"etherpad": "Paylaşımlı belge",
|
||||
"sharescreen": "Ekran paylaş",
|
||||
"fullscreen": "Tam Ekrana Gir / Çık",
|
||||
"sip": "SIP numara ara",
|
||||
"Settings": "Ayarlar",
|
||||
"hangup": "Kapat",
|
||||
"login": "Oturum aç",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Sohbeti aç / kapa",
|
||||
"filmstrip": "Kişi listesi aç / kapa",
|
||||
"contactlist": "Film şeridini göster / gizle"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Aşağıdaki kutuya bir takma ad girin",
|
||||
"popover": "Bir takma ad seçin"
|
||||
},
|
||||
"messagebox": "Metin girin..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "AYARLAR",
|
||||
"update": "Güncelle",
|
||||
"name": "Ad"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Görünür adınızı değiştirmek<br/>için tıkla",
|
||||
"moderator": "Bu görüşmenin<br/>sahibi",
|
||||
"videomute": "Katılımcı<br/>kamera durdurdu.",
|
||||
"mute": "Katılımcı sessiz",
|
||||
"kick": "Kovuldu",
|
||||
"muted": "Sessiz",
|
||||
"domute": "Sustur"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "Bit hızı:",
|
||||
"packetloss": "Paket kaybı:",
|
||||
"resolution": "Çözünürlük:",
|
||||
"less": "Daha az göster",
|
||||
"more": "Daha fazla göster",
|
||||
"address": "Adres:",
|
||||
"remoteport": "Uzak port:Uzak portlar:",
|
||||
"localport": "Yerel port:Yerel portlar:",
|
||||
"localaddress": "Yerel adres:Yerel adresler:",
|
||||
"remoteaddress": "Uzak adres:Uzak adresler:",
|
||||
"transport": "Transport:",
|
||||
"bandwidth": "Tahmini bant genişliği:",
|
||||
"na": "Görüşme başladıktan sonra bağlantı bilgileri için buraya gel"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "bağlantı kesildi",
|
||||
"moderator": "Görüşme yöneticisi hakları verildi!",
|
||||
"connected": "bağlandı",
|
||||
"somebody": "Birisi",
|
||||
"me": "Bana",
|
||||
"focus": "Görüşme odağı",
|
||||
"focusFail": "__component__ uygun değil - __ms__ saniye içinde tekrar deneyin",
|
||||
"grantedTo": "__to__, görüşme yöneticisi hakları verildi!",
|
||||
"grantedToUnknown": "$t(somebody), görüşme yöneticisi hakları verildi!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Ahhh! Görüşmeden, kavuldun!",
|
||||
"popupError": "Tarayıcınız bu siteden açılır pencereleri engelliyor. Lütfen, tarayıcınızın güvenlik ayarlarında pop-up etkinleştirin ve tekrar deneyin.",
|
||||
"passwordError": "Bu görüşme şu anda bir parola ile korunmaktadır. Sadece görüşmenin sahibi bir parola ayarlayabilir.",
|
||||
"passwordError2": "Bu görüşme şu anda bir parola ile korunmamaktadır. Sadece görüşmenin sahibi bir parola ayarlayabilir.",
|
||||
"joinError": "Amanin boo! Görüşmeye katılamadık. Güvenlik yapılandırması ile ilgili bir sorun olabilir. Hizmet yöneticisi ile bağlantı kurun.",
|
||||
"connectError": "Amanin boo! Birşeyler ters gitti ve görüşmeye bağlanamadık.",
|
||||
"error": "Hata",
|
||||
"detectext": "Ekran paylaşımı eklentisi tespit edilirken hata.",
|
||||
"failtoinstall": "Masaüstü paylaşım eklentisi yüklenemedi",
|
||||
"failedpermissions": "Yerel mikrofon ve/veya kamerayı kullanmak için izinler alınamadı.",
|
||||
"bridgeUnavailable": "Jitsi Videobridge şu anda kullanılamıyor. Daha sonra tekrar deneyiniz!",
|
||||
"lockTitle": "Kilitlenemedi",
|
||||
"lockMessage": "Görüşme kilitlenemedi.",
|
||||
"warning": "Uyarı",
|
||||
"passwordNotSupported": "Oda parolaları şu anda desteklenmemekte.",
|
||||
"sorry": "Üzgünüz",
|
||||
"internalError": "İç uygulama hatası [setRemoteDescription]",
|
||||
"unableToSwitch": "Video akışı açılamıyor.",
|
||||
"SLDFailure": "Amanin boo! Birşeyler ters gitti ve sessize alamadık! (SLD Başarısız)",
|
||||
"SRDFailure": "Amanin boo! Birşeyler ters gitti ve videoyu durduramadık! (SRD Başarısız)",
|
||||
"oops": "Amanin boo!",
|
||||
"defaultError": "Bir tür hata var",
|
||||
"passwordRequired": "Parola gerekli",
|
||||
"Ok": "Tamam",
|
||||
"removePreziTitle": "Prezi kaldır",
|
||||
"removePreziMsg": "Prezi kaldırmak istediğinizden emin misiniz?",
|
||||
"sharePreziTitle": "Bir Prezi paylaşın",
|
||||
"sharePreziMsg": "Diğer katılımcı hala bir Prezi paylaşıyor.Bu görüşme aynı zamanda sadece bir Prezi izin verir.",
|
||||
"Remove": "Kaldır",
|
||||
"Stop": "Durdur",
|
||||
"AuthMsg": "Oda oluşturmak için kimlik doğrulama gerekli:<br/><b>__room__ </b></br> Oda oluşturmak için ya kimlik doğrulamalı ya da bunu yapması için bir başkasını beklemelisiniz.",
|
||||
"Authenticate": "Kimlik doğrula",
|
||||
"Cancel": "İptal",
|
||||
"logoutTitle": "Oturum kapat",
|
||||
"logoutQuestion": "Oturumu ve görüşmeyi sonlandırmak istediğinizden emin misiniz?",
|
||||
"sessTerminated": "Oturum sonlandırıldı",
|
||||
"hungUp": "Görüşmeyi bitirdiniz",
|
||||
"joinAgain": "Yeniden katıl",
|
||||
"Share": "Paylaş",
|
||||
"preziLinkError": "Lütfen doğru prezi bağlantısı verin.",
|
||||
"Save": "Kaydet",
|
||||
"recordingToken": "Kayıt jetonu girin",
|
||||
"Dial": "Ara",
|
||||
"sipMsg": "SIP numarası gir",
|
||||
"passwordCheck": "Parolanızı kaldırmak istediğinizden emin misiniz?",
|
||||
"passwordMsg": "Odanızı kilitlemek için bir parola koyun",
|
||||
"Invite": "Davet et",
|
||||
"shareLink": "Davet etmek istediğiniz herkesle bu bağlantıyı paylaşın",
|
||||
"settings1": "Görüşmenizi yapılandır",
|
||||
"settings2": "Katılımcılar sessiz katılsın",
|
||||
"settings3": "Takma adlar gerekli<br/><br/>Odanızı kitlemek için bir parola ayarlayın:",
|
||||
"yourPassword": "parolanız",
|
||||
"Back": "Geri",
|
||||
"serviceUnavailable": "Hizmet kullanım dışı",
|
||||
"gracefulShutdown": "Hizmetimiz bakıp için durduruldu. Daha sonra tekrar deneyiniz.",
|
||||
"Yes": "Evet",
|
||||
"reservationError": "Rezervasyon sistemi hatası",
|
||||
"reservationErrorMsg": "Hata kodu: __code__, mesaj: __msg__"
|
||||
}
|
||||
}
|
||||
112
lang/main.json
@@ -3,6 +3,12 @@
|
||||
"connectionsettings": "Connection Settings",
|
||||
"poweredby": "powered by",
|
||||
"downloadlogs": "Download logs",
|
||||
"roomUrlDefaultMsg": "Your conference is currently being created...",
|
||||
"participant": "Participant",
|
||||
"me": "me",
|
||||
"speaker": "Speaker",
|
||||
"defaultNickname": "ex. __name__",
|
||||
"defaultPreziLink": "e.g. __url__",
|
||||
"welcomepage":{
|
||||
"go": "GO",
|
||||
"roomname": "Enter room name",
|
||||
@@ -56,12 +62,13 @@
|
||||
"Settings": "Settings",
|
||||
"hangup": "Hang Up",
|
||||
"login": "Login",
|
||||
"logout": "Logout"
|
||||
"logout": "Logout",
|
||||
"dialpad": "Show dialpad"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Open / close chat",
|
||||
"filmstrip": "Open / close contact list",
|
||||
"contactlist": "Show / hide film strip"
|
||||
"filmstrip": "Show / hide film strip",
|
||||
"contactlist": "Open / close contact list"
|
||||
},
|
||||
"chat":{
|
||||
"nickname": {
|
||||
@@ -74,7 +81,9 @@
|
||||
{
|
||||
"title": "SETTINGS",
|
||||
"update": "Update",
|
||||
"name": "Name"
|
||||
"name": "Name",
|
||||
"startAudioMuted": "everyone join audio muted",
|
||||
"startVideoMuted": "everyone join video muted"
|
||||
},
|
||||
"videothumbnail":
|
||||
{
|
||||
@@ -90,42 +99,45 @@
|
||||
"connectionindicator":
|
||||
{
|
||||
"bitrate": "Bitrate:",
|
||||
"packetloss": "Packet loss: ",
|
||||
"packetloss": "Packet loss:",
|
||||
"resolution": "Resolution:",
|
||||
"less": "Show less",
|
||||
"more": "Show more",
|
||||
"address": "Address:",
|
||||
"remoteports": "Remote ports:",
|
||||
"localports": "Local ports:",
|
||||
"remoteport_plural": "Remote ports:",
|
||||
"localport_plural": "Local ports:",
|
||||
"remoteport": "Remote port:",
|
||||
"localport": "Local port:",
|
||||
"localaddress": "Local address: ",
|
||||
"localaddresses": "Local addresses: ",
|
||||
"remoteaddress": "Remote address: ",
|
||||
"remoteaddresses": "Remote addresses: ",
|
||||
"transport": "Transport: ",
|
||||
"localaddress": "Local address:",
|
||||
"localaddress_plural": "Local addresses:",
|
||||
"remoteaddress": "Remote address:",
|
||||
"remoteaddress_plural": "Remote addresses:",
|
||||
"transport": "Transport:",
|
||||
"bandwidth": "Estimated bandwidth:",
|
||||
"na": "Come back here for connection information once the conference starts"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "disconnected",
|
||||
"moderator": "Moderator rights granted !",
|
||||
"moderator": "Moderator rights granted!",
|
||||
"connected": "connected",
|
||||
"somebody": "Somebody",
|
||||
"me": "Me",
|
||||
"focus": "Conference focus",
|
||||
"focusFail": "__component__ not available - retry in __ms__ sec",
|
||||
"grantedTo": "Moderator rights granted to __to__!",
|
||||
"grantedToUnknown": "Moderator rights granted to $t(somebody)!"
|
||||
|
||||
"grantedToUnknown": "Moderator rights granted to $t(somebody)!",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "You're muted!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser security settings and try again.",
|
||||
"passwordError": "This conversation is currently protected by a password. Only the owner of the conference could set a password.",
|
||||
"passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.",
|
||||
"joinError": "Oops ! We couldn`t join the conference. There might be some problem with security configuration. Please contact service administrator.",
|
||||
"connectError": "Oops! Something went wrong and we couldn`t connect to the conference.",
|
||||
"joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.",
|
||||
"connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
|
||||
"connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: __msg__",
|
||||
"connecting": "Connecting",
|
||||
"error": "Error",
|
||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||
"failtoinstall": "Failed to install desktop sharing extension",
|
||||
@@ -136,10 +148,10 @@
|
||||
"warning": "Warning",
|
||||
"passwordNotSupported": "Room passwords are currently not supported.",
|
||||
"sorry": "Sorry",
|
||||
"internalError": "Internal application error[setRemoteDescription]",
|
||||
"internalError": "Internal application error [setRemoteDescription]",
|
||||
"unableToSwitch": "Unable to switch video stream.",
|
||||
"SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
|
||||
"SRDFailure": "Oops! Something went wrong and we failed to stop video!(SRD Failure)",
|
||||
"SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
|
||||
"oops": "Oops!",
|
||||
"defaultError": "There was some kind of error",
|
||||
"passwordRequired": "Password required",
|
||||
@@ -149,10 +161,13 @@
|
||||
"sharePreziTitle": "Share a Prezi",
|
||||
"sharePreziMsg": "Another participant is already sharing a Prezi.This conference allows only one Prezi at a time.",
|
||||
"Remove": "Remove",
|
||||
"Stop": "Stop",
|
||||
"AuthMsg": "Authentication is required to create room:<br/><b>__room__ </b></br> You can either authenticate to create the room or just wait for someone else to do so.",
|
||||
"Authenticate": "Authenticate",
|
||||
"WaitingForHost": "Waiting for the host ...",
|
||||
"WaitForHostMsg": "The conference <b>__room__ </b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
|
||||
"IamHost": "I am the host",
|
||||
"Cancel": "Cancel",
|
||||
"retry": "Retry",
|
||||
"logoutTitle" : "Logout",
|
||||
"logoutQuestion" : "Are you sure you want to logout and stop the conference?",
|
||||
"sessTerminated": "Session Terminated",
|
||||
"hungUp": "You hung up the call",
|
||||
"joinAgain": "Join again",
|
||||
@@ -171,6 +186,57 @@
|
||||
"settings2": "Participants join muted",
|
||||
"settings3": "Require nicknames<br/><br/>Set a password to lock your room:",
|
||||
"yourPassword": "your password",
|
||||
"Back": "Back"
|
||||
"Back": "Back",
|
||||
"serviceUnavailable": "Service unavailable",
|
||||
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
|
||||
"Yes": "Yes",
|
||||
"reservationError": "Reservation system error",
|
||||
"reservationErrorMsg": "Error code: __code__, message: __msg__",
|
||||
"password": "password",
|
||||
"userPassword": "user password",
|
||||
"token": "token"
|
||||
},
|
||||
"email":
|
||||
{
|
||||
"sharedKey": [
|
||||
"This conference is password protected. Please use the following pin when joining:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""],
|
||||
"subject": "Invitation to a __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Hey there, I%27d like to invite you to a __appName__ conference I%27ve just set up.",
|
||||
"",
|
||||
"",
|
||||
"Please click on the following link in order to join the conference.",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" Note that __appName__ is currently only supported by __supportedBrowsers__, so you need to be using one of these browsers.",
|
||||
"",
|
||||
"",
|
||||
"Talk to you in a sec!"
|
||||
],
|
||||
"and": "and"
|
||||
},
|
||||
"connection":
|
||||
{
|
||||
"ERROR": "Error",
|
||||
"CONNECTING": "Connecting",
|
||||
"CONNFAIL": "Connection failed",
|
||||
"AUTHENTICATING": "Authenticating",
|
||||
"AUTHFAIL": "Authentication failed",
|
||||
"CONNECTED": "Connected",
|
||||
"DISCONNECTED": "Disconnected",
|
||||
"DISCONNECTING": "Disconnecting",
|
||||
"ATTACHED": "Attached",
|
||||
"FETCH_SESSION_ID": "Obtaining session-id...",
|
||||
"GOT_SESSION_ID": "Obtaining session-id... Done",
|
||||
"GET_SESSION_ID_ERROR": "Get session-id error: "
|
||||
}
|
||||
}
|
||||
|
||||
5882
libs/app.bundle.js
1273
libs/jquery-impromptu.js
vendored
@@ -135,7 +135,7 @@ function processMessage(event)
|
||||
}
|
||||
|
||||
function setupListeners() {
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_ENTER, function (from) {
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, function (from) {
|
||||
API.triggerEvent("participantJoined", {jid: from});
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, function (from, nick, txt, myjid) {
|
||||
@@ -143,7 +143,7 @@ function setupListeners() {
|
||||
API.triggerEvent("incomingMessage",
|
||||
{"from": from, "nick": nick, "message": txt});
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_LEFT, function (jid) {
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, function (jid) {
|
||||
API.triggerEvent("participantLeft", {jid: jid});
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, function (jid, newDisplayName) {
|
||||
|
||||
47
modules/DTMF/DTMF.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/* global APP */
|
||||
|
||||
/**
|
||||
* A module for sending DTMF tones.
|
||||
*/
|
||||
var DTMFSender;
|
||||
var initDtmfSender = function() {
|
||||
// TODO: This needs to reset this if the peerconnection changes
|
||||
// (e.g. the call is re-made)
|
||||
if (DTMFSender)
|
||||
return;
|
||||
|
||||
var localAudio = APP.RTC.localAudio;
|
||||
if (localAudio && localAudio.getTracks().length > 0)
|
||||
{
|
||||
var peerconnection
|
||||
= APP.xmpp.getConnection().jingle.activecall.peerconnection;
|
||||
if (peerconnection) {
|
||||
DTMFSender =
|
||||
peerconnection.peerconnection
|
||||
.createDTMFSender(localAudio.getTracks()[0]);
|
||||
console.log("Initialized DTMFSender");
|
||||
}
|
||||
else {
|
||||
console.log("Failed to initialize DTMFSender: no PeerConnection.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Failed to initialize DTMFSender: no audio track.");
|
||||
}
|
||||
};
|
||||
|
||||
var DTMF = {
|
||||
sendTones: function (tones, duration, pause) {
|
||||
if (!DTMFSender)
|
||||
initDtmfSender();
|
||||
|
||||
if (DTMFSender){
|
||||
DTMFSender.insertDTMF(tones,
|
||||
(duration || 200),
|
||||
(pause || 200));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DTMF;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global Strophe, updateLargeVideo, focusedVideoSrc*/
|
||||
/* global Strophe, focusedVideoSrc*/
|
||||
|
||||
// cache datachannels to avoid garbage collection
|
||||
// https://code.google.com/p/chromium/issues/detail?id=405545
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
|
||||
|
||||
function LocalStream(stream, type, eventEmitter, videoType)
|
||||
|
||||
function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.type = type;
|
||||
this.videoType = videoType;
|
||||
this.isGUMStream = true;
|
||||
if(isGUMStream === false)
|
||||
this.isGUMStream = isGUMStream;
|
||||
var self = this;
|
||||
if(type == "audio")
|
||||
{
|
||||
@@ -36,27 +40,43 @@ LocalStream.prototype.getOriginalStream = function()
|
||||
}
|
||||
|
||||
LocalStream.prototype.isAudioStream = function () {
|
||||
return (this.stream.getAudioTracks() && this.stream.getAudioTracks().length > 0);
|
||||
};
|
||||
|
||||
LocalStream.prototype.mute = function()
|
||||
{
|
||||
var ismuted = false;
|
||||
var tracks = this.getTracks();
|
||||
|
||||
for (var idx = 0; idx < tracks.length; idx++) {
|
||||
ismuted = !tracks[idx].enabled;
|
||||
tracks[idx].enabled = ismuted;
|
||||
}
|
||||
return ismuted;
|
||||
return this.type === "audio";
|
||||
};
|
||||
|
||||
LocalStream.prototype.setMute = function(mute)
|
||||
{
|
||||
var tracks = this.getTracks();
|
||||
|
||||
for (var idx = 0; idx < tracks.length; idx++) {
|
||||
tracks[idx].enabled = mute;
|
||||
if((window.location.protocol != "https:" && this.isGUMStream) ||
|
||||
(this.isAudioStream() && this.isGUMStream) || this.videoType === "screen")
|
||||
{
|
||||
var tracks = this.getTracks();
|
||||
|
||||
for (var idx = 0; idx < tracks.length; idx++) {
|
||||
tracks[idx].enabled = mute;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mute === false) {
|
||||
APP.xmpp.removeStream(this.stream);
|
||||
this.stream.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
var self = this;
|
||||
APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(
|
||||
(this.isAudioStream() ? ["audio"] : ["video"]),
|
||||
function (stream) {
|
||||
if(self.isAudioStream())
|
||||
{
|
||||
APP.RTC.changeLocalAudio(stream, function () {});
|
||||
}
|
||||
else
|
||||
{
|
||||
APP.RTC.changeLocalVideo(stream, false, function () {});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,6 +88,8 @@ LocalStream.prototype.isMuted = function () {
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this.stream.ended)
|
||||
return true;
|
||||
tracks = this.stream.getVideoTracks();
|
||||
}
|
||||
for (var idx = 0; idx < tracks.length; idx++) {
|
||||
@@ -81,6 +103,4 @@ LocalStream.prototype.getId = function () {
|
||||
return this.stream.getTracks()[0].id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = LocalStream;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////These lines should be uncommented when require works in app.js
|
||||
var RTCBrowserType = require("../../service/RTC/RTCBrowserType.js");
|
||||
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
||||
var StreamEventType = require("../../service/RTC/StreamEventTypes");
|
||||
|
||||
/**
|
||||
* Creates a MediaStream object for the given data, session id and ssrc.
|
||||
@@ -13,7 +13,7 @@ var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function MediaStream(data, sid, ssrc, browser) {
|
||||
function MediaStream(data, sid, ssrc, browser, eventEmitter) {
|
||||
|
||||
// XXX(gp) to minimize headaches in the future, we should build our
|
||||
// abstractions around tracks and not streams. ORTC is track based API.
|
||||
@@ -33,26 +33,28 @@ function MediaStream(data, sid, ssrc, browser) {
|
||||
MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
|
||||
this.videoType = null;
|
||||
this.muted = false;
|
||||
if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX)
|
||||
{
|
||||
if (!this.getVideoTracks)
|
||||
this.getVideoTracks = function () { return []; };
|
||||
if (!this.getAudioTracks)
|
||||
this.getAudioTracks = function () { return []; };
|
||||
}
|
||||
this.eventEmitter = eventEmitter;
|
||||
}
|
||||
|
||||
|
||||
MediaStream.prototype.getOriginalStream = function()
|
||||
{
|
||||
return this.stream;
|
||||
}
|
||||
};
|
||||
|
||||
MediaStream.prototype.setMute = function (value)
|
||||
{
|
||||
this.stream.muted = value;
|
||||
this.muted = value;
|
||||
}
|
||||
};
|
||||
|
||||
MediaStream.prototype.setVideoType = function (value) {
|
||||
if(this.videoType === value)
|
||||
return;
|
||||
this.videoType = value;
|
||||
this.eventEmitter.emit(StreamEventType.EVENT_TYPE_REMOTE_CHANGED,
|
||||
this.peerjid);
|
||||
};
|
||||
|
||||
|
||||
module.exports = MediaStream;
|
||||
|
||||
@@ -7,13 +7,46 @@ var DesktopSharingEventTypes
|
||||
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
||||
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
|
||||
var RTCEvents = require("../../service/RTC/RTCEvents.js");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var UIEvents = require("../../service/UI/UIEvents");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
|
||||
|
||||
function getMediaStreamUsage()
|
||||
{
|
||||
var result = {
|
||||
audio: true,
|
||||
video: true
|
||||
};
|
||||
|
||||
/** There are some issues with the desktop sharing
|
||||
* when this property is enabled.
|
||||
* WARNING: We must change the implementation to start video/audio if we
|
||||
* receive from the focus that the peer is not muted.
|
||||
|
||||
var isSecureConnection = window.location.protocol == "https:";
|
||||
|
||||
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
||||
{
|
||||
result = {
|
||||
audio: false,
|
||||
video: false
|
||||
};
|
||||
|
||||
}
|
||||
**/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var RTC = {
|
||||
rtcUtils: null,
|
||||
devices: {
|
||||
audio: true,
|
||||
video: true
|
||||
},
|
||||
localStreams: [],
|
||||
remoteStreams: {},
|
||||
localAudio: null,
|
||||
@@ -30,13 +63,16 @@ var RTC = {
|
||||
|
||||
eventEmitter.removeListener(eventType, listener);
|
||||
},
|
||||
createLocalStream: function (stream, type, change) {
|
||||
createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
|
||||
|
||||
var localStream = new LocalStream(stream, type, eventEmitter);
|
||||
var localStream = new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
|
||||
//in firefox we have only one stream object
|
||||
if(this.localStreams.length == 0 ||
|
||||
this.localStreams[0].getOriginalStream() != stream)
|
||||
this.localStreams.push(localStream);
|
||||
if(isMuted === true)
|
||||
localStream.setMute(false);
|
||||
|
||||
if(type == "audio")
|
||||
{
|
||||
this.localAudio = localStream;
|
||||
@@ -49,7 +85,7 @@ var RTC = {
|
||||
if(change)
|
||||
eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
|
||||
|
||||
eventEmitter.emit(eventType, localStream);
|
||||
eventEmitter.emit(eventType, localStream, isMuted);
|
||||
return localStream;
|
||||
},
|
||||
removeLocalStream: function (stream) {
|
||||
@@ -63,14 +99,13 @@ var RTC = {
|
||||
},
|
||||
createRemoteStream: function (data, sid, thessrc) {
|
||||
var remoteStream = new MediaStream(data, sid, thessrc,
|
||||
this.getBrowserType());
|
||||
this.getBrowserType(), eventEmitter);
|
||||
var jid = data.peerjid || APP.xmpp.myJid();
|
||||
if(!this.remoteStreams[jid]) {
|
||||
this.remoteStreams[jid] = {};
|
||||
}
|
||||
this.remoteStreams[jid][remoteStream.type]= remoteStream;
|
||||
eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream);
|
||||
console.debug("ADD remote stream ", remoteStream.type, " ", jid, " ", thessrc);
|
||||
return remoteStream;
|
||||
},
|
||||
getBrowserType: function () {
|
||||
@@ -112,7 +147,7 @@ var RTC = {
|
||||
function (stream, isUsingScreenStream, callback) {
|
||||
self.changeLocalVideo(stream, isUsingScreenStream, callback);
|
||||
}, DesktopSharingEventTypes.NEW_STREAM_CREATED);
|
||||
APP.xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
|
||||
APP.xmpp.addListener(XMPPEvents.STREAMS_CHANGED, function (jid, changedStreams) {
|
||||
for(var i = 0; i < changedStreams.length; i++) {
|
||||
var type = changedStreams[i].type;
|
||||
if (type != "audio") {
|
||||
@@ -122,7 +157,7 @@ var RTC = {
|
||||
var videoStream = peerStreams[MediaStreamType.VIDEO_TYPE];
|
||||
if(!videoStream)
|
||||
continue;
|
||||
videoStream.videoType = changedStreams[i].type;
|
||||
videoStream.setVideoType(changedStreams[i].type);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -134,7 +169,8 @@ var RTC = {
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
||||
DataChannels.handlePinnedEndpointEvent);
|
||||
this.rtcUtils = new RTCUtils(this);
|
||||
this.rtcUtils.obtainAudioAndVideoPermissions();
|
||||
this.rtcUtils.obtainAudioAndVideoPermissions(
|
||||
null, null, getMediaStreamUsage());
|
||||
},
|
||||
muteRemoteVideoStream: function (jid, value) {
|
||||
var stream;
|
||||
@@ -146,7 +182,7 @@ var RTC = {
|
||||
}
|
||||
|
||||
if(!stream)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if (value != stream.muted) {
|
||||
stream.setMute(value);
|
||||
@@ -167,10 +203,27 @@ var RTC = {
|
||||
changeLocalVideo: function (stream, isUsingScreenStream, callback) {
|
||||
var oldStream = this.localVideo.getOriginalStream();
|
||||
var type = (isUsingScreenStream? "screen" : "video");
|
||||
this.localVideo = this.createLocalStream(stream, "video", true, type);
|
||||
var localCallback = callback;
|
||||
if(this.localVideo.isMuted() && this.localVideo.videoType !== type)
|
||||
{
|
||||
localCallback = function() {
|
||||
APP.xmpp.setVideoMute(false, APP.UI.setVideoMuteButtonsState);
|
||||
callback();
|
||||
};
|
||||
}
|
||||
var videoStream = this.rtcUtils.createStream(stream, true);
|
||||
this.localVideo = this.createLocalStream(videoStream, "video", true, type);
|
||||
// Stop the stream to trigger onended event for old stream
|
||||
oldStream.stop();
|
||||
APP.xmpp.switchStreams(stream, oldStream,callback);
|
||||
APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
|
||||
},
|
||||
changeLocalAudio: function (stream, callback) {
|
||||
var oldStream = this.localAudio.getOriginalStream();
|
||||
var newStream = this.rtcUtils.createStream(stream);
|
||||
this.localAudio = this.createLocalStream(newStream, "audio", true);
|
||||
// Stop the stream to trigger onended event for old stream
|
||||
oldStream.stop();
|
||||
APP.xmpp.switchStreams(newStream, oldStream, callback, true);
|
||||
},
|
||||
/**
|
||||
* Checks if video identified by given src is desktop stream.
|
||||
@@ -183,8 +236,7 @@ var RTC = {
|
||||
return false;
|
||||
var isDesktop = false;
|
||||
var stream = null;
|
||||
if (APP.xmpp.myJid() &&
|
||||
APP.xmpp.myResource() === jid) {
|
||||
if (APP.xmpp.myJid() === jid) {
|
||||
// local video
|
||||
stream = this.localVideo;
|
||||
} else {
|
||||
@@ -198,6 +250,34 @@ var RTC = {
|
||||
isDesktop = (stream.videoType === "screen");
|
||||
|
||||
return isDesktop;
|
||||
},
|
||||
setVideoMute: function(mute, callback, options) {
|
||||
if(!this.localVideo)
|
||||
return;
|
||||
|
||||
if (mute == APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
APP.xmpp.sendVideoInfoPresence(mute);
|
||||
if(callback)
|
||||
callback(mute);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP.RTC.localVideo.setMute(!mute);
|
||||
APP.xmpp.setVideoMute(
|
||||
mute,
|
||||
callback,
|
||||
options);
|
||||
}
|
||||
},
|
||||
setDeviceAvailability: function (devices) {
|
||||
if(!devices)
|
||||
return;
|
||||
if(devices.audio === true || devices.audio === false)
|
||||
this.devices.audio = devices.audio;
|
||||
if(devices.video === true || devices.video === false)
|
||||
this.devices.video = devices.video;
|
||||
eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -130,12 +130,22 @@ function RTCUtils(RTCService)
|
||||
if (navigator.mozGetUserMedia) {
|
||||
console.log('This appears to be Firefox');
|
||||
var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
|
||||
if (version >= 22) {
|
||||
if (version >= 40
|
||||
&& !config.enableSimulcast && config.useBundle && config.useRtcpMux) {
|
||||
this.peerconnection = mozRTCPeerConnection;
|
||||
this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
|
||||
this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
|
||||
this.pc_constraints = {};
|
||||
this.attachMediaStream = function (element, stream) {
|
||||
// srcObject is being standardized and FF will eventually
|
||||
// support that unprefixed. FF also supports the
|
||||
// "element.src = URL.createObjectURL(...)" combo, but that
|
||||
// will be deprecated in favour of srcObject.
|
||||
//
|
||||
// https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
|
||||
// https://github.com/webrtc/samples/issues/302
|
||||
if(!element[0])
|
||||
return;
|
||||
element[0].mozSrcObject = stream;
|
||||
element[0].play();
|
||||
};
|
||||
@@ -148,14 +158,21 @@ function RTCUtils(RTCService)
|
||||
return tracks[0].id.replace(/[\{,\}]/g,"");
|
||||
};
|
||||
this.getVideoSrc = function (element) {
|
||||
if(!element)
|
||||
return null;
|
||||
return element.mozSrcObject;
|
||||
};
|
||||
this.setVideoSrc = function (element, src) {
|
||||
element.mozSrcObject = src;
|
||||
if(element)
|
||||
element.mozSrcObject = src;
|
||||
};
|
||||
RTCSessionDescription = mozRTCSessionDescription;
|
||||
RTCIceCandidate = mozRTCIceCandidate;
|
||||
} else {
|
||||
window.location.href = 'unsupported_browser.html';
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (navigator.webkitGetUserMedia) {
|
||||
console.log('This appears to be Chrome');
|
||||
this.peerconnection = webkitRTCPeerConnection;
|
||||
@@ -170,10 +187,13 @@ function RTCUtils(RTCService)
|
||||
return stream.id.replace(/[\{,\}]/g,"");
|
||||
};
|
||||
this.getVideoSrc = function (element) {
|
||||
if(!element)
|
||||
return null;
|
||||
return element.getAttribute("src");
|
||||
};
|
||||
this.setVideoSrc = function (element, src) {
|
||||
element.setAttribute("src", src);
|
||||
if(element)
|
||||
element.setAttribute("src", src);
|
||||
};
|
||||
// DTLS should now be enabled by default but..
|
||||
this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
|
||||
@@ -195,16 +215,9 @@ function RTCUtils(RTCService)
|
||||
{
|
||||
try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
|
||||
|
||||
window.location.href = 'webrtcrequired.html';
|
||||
window.location.href = 'unsupported_browser.html';
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.browser !== RTCBrowserType.RTC_BROWSER_CHROME &&
|
||||
config.enableFirefoxSupport !== true) {
|
||||
window.location.href = 'chromeonly.html';
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +234,8 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
|
||||
var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
|
||||
var self = this;
|
||||
|
||||
try {
|
||||
if (config.enableSimulcast
|
||||
&& constraints.video
|
||||
@@ -232,10 +247,12 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
&& !isFF) {
|
||||
APP.simulcast.getUserMedia(constraints, function (stream) {
|
||||
console.log('onUserMediaSuccess');
|
||||
self.setAvailableDevices(um, true);
|
||||
success_callback(stream);
|
||||
},
|
||||
function (error) {
|
||||
console.warn('Failed to get access to local media. Error ', error);
|
||||
self.setAvailableDevices(um, false);
|
||||
if (failure_callback) {
|
||||
failure_callback(error);
|
||||
}
|
||||
@@ -245,9 +262,11 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
this.getUserMedia(constraints,
|
||||
function (stream) {
|
||||
console.log('onUserMediaSuccess');
|
||||
self.setAvailableDevices(um, true);
|
||||
success_callback(stream);
|
||||
},
|
||||
function (error) {
|
||||
self.setAvailableDevices(um, false);
|
||||
console.warn('Failed to get access to local media. Error ',
|
||||
error, constraints);
|
||||
if (failure_callback) {
|
||||
@@ -264,29 +283,114 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
}
|
||||
};
|
||||
|
||||
RTCUtils.prototype.setAvailableDevices = function (um, available) {
|
||||
var devices = {};
|
||||
if(um.indexOf("video") != -1)
|
||||
{
|
||||
devices.video = available;
|
||||
}
|
||||
if(um.indexOf("audio") != -1)
|
||||
{
|
||||
devices.audio = available;
|
||||
}
|
||||
this.service.setDeviceAvailability(devices);
|
||||
}
|
||||
|
||||
/**
|
||||
* We ask for audio and video combined stream in order to get permissions and
|
||||
* not to ask twice.
|
||||
*/
|
||||
RTCUtils.prototype.obtainAudioAndVideoPermissions = function() {
|
||||
RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||
function(devices, callback, usageOptions)
|
||||
{
|
||||
var self = this;
|
||||
// Get AV
|
||||
|
||||
this.getUserMediaWithConstraints(
|
||||
['audio', 'video'],
|
||||
var successCallback = function (stream) {
|
||||
if(callback)
|
||||
callback(stream, usageOptions);
|
||||
else
|
||||
self.successCallback(stream, usageOptions);
|
||||
};
|
||||
|
||||
if(!devices)
|
||||
devices = ['audio', 'video'];
|
||||
|
||||
var newDevices = [];
|
||||
|
||||
|
||||
if(usageOptions)
|
||||
for(var i = 0; i < devices.length; i++)
|
||||
{
|
||||
var device = devices[i];
|
||||
if(usageOptions[device] === true)
|
||||
newDevices.push(device);
|
||||
}
|
||||
else
|
||||
newDevices = devices;
|
||||
|
||||
if(newDevices.length === 0)
|
||||
{
|
||||
successCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigator.mozGetUserMedia) {
|
||||
|
||||
// With FF we can't split the stream into audio and video because FF
|
||||
// doesn't support media stream constructors. So, we need to get the
|
||||
// audio stream separately from the video stream using two distinct GUM
|
||||
// calls. Not very user friendly :-( but we don't have many other
|
||||
// options neither.
|
||||
//
|
||||
// Note that we pack those 2 streams in a single object and pass it to
|
||||
// the successCallback method.
|
||||
|
||||
self.getUserMediaWithConstraints(
|
||||
['audio'],
|
||||
function (audioStream) {
|
||||
self.getUserMediaWithConstraints(
|
||||
['video'],
|
||||
function (videoStream) {
|
||||
return self.successCallback({
|
||||
audioStream: audioStream,
|
||||
videoStream: videoStream
|
||||
});
|
||||
},
|
||||
function (error) {
|
||||
console.error('failed to obtain video stream - stop',
|
||||
error);
|
||||
return self.successCallback(null);
|
||||
},
|
||||
config.resolution || '360');
|
||||
},
|
||||
function (error) {
|
||||
console.error('failed to obtain audio stream - stop',
|
||||
error);
|
||||
return self.successCallback(null);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.getUserMediaWithConstraints(
|
||||
newDevices,
|
||||
function (stream) {
|
||||
self.successCallback(stream);
|
||||
successCallback(stream);
|
||||
},
|
||||
function (error) {
|
||||
self.errorCallback(error);
|
||||
},
|
||||
config.resolution || '360');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RTCUtils.prototype.successCallback = function (stream) {
|
||||
console.log('got', stream, stream.getAudioTracks().length,
|
||||
stream.getVideoTracks().length);
|
||||
this.handleLocalStream(stream);
|
||||
RTCUtils.prototype.successCallback = function (stream, usageOptions) {
|
||||
// If this is FF, the stream parameter is *not* a MediaStream object, it's
|
||||
// an object with two properties: audioStream, videoStream.
|
||||
if(stream && !navigator.mozGetUserMedia)
|
||||
console.log('got', stream, stream.getAudioTracks().length,
|
||||
stream.getVideoTracks().length);
|
||||
this.handleLocalStream(stream, usageOptions);
|
||||
};
|
||||
|
||||
RTCUtils.prototype.errorCallback = function (error) {
|
||||
@@ -317,44 +421,76 @@ RTCUtils.prototype.errorCallback = function (error) {
|
||||
function (error) {
|
||||
console.error('failed to obtain audio/video stream - stop',
|
||||
error);
|
||||
APP.UI.messageHandler.showError("dialog.error", "Error",
|
||||
"dialog.failedpermissions",
|
||||
"Failed to obtain permissions to use the local microphone " +
|
||||
"and/or camera.");
|
||||
return self.successCallback(null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RTCUtils.prototype.handleLocalStream = function(stream)
|
||||
RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
||||
{
|
||||
// If this is FF, the stream parameter is *not* a MediaStream object, it's
|
||||
// an object with two properties: audioStream, videoStream.
|
||||
var audioStream, videoStream;
|
||||
if(window.webkitMediaStream)
|
||||
{
|
||||
var audioStream = new webkitMediaStream();
|
||||
var videoStream = new webkitMediaStream();
|
||||
var audioTracks = stream.getAudioTracks();
|
||||
var videoTracks = stream.getVideoTracks();
|
||||
for (var i = 0; i < audioTracks.length; i++) {
|
||||
audioStream.addTrack(audioTracks[i]);
|
||||
audioStream = new webkitMediaStream();
|
||||
videoStream = new webkitMediaStream();
|
||||
if(stream) {
|
||||
var audioTracks = stream.getAudioTracks();
|
||||
|
||||
for (var i = 0; i < audioTracks.length; i++) {
|
||||
audioStream.addTrack(audioTracks[i]);
|
||||
}
|
||||
|
||||
var videoTracks = stream.getVideoTracks();
|
||||
|
||||
for (i = 0; i < videoTracks.length; i++) {
|
||||
videoStream.addTrack(videoTracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.service.createLocalStream(audioStream, "audio");
|
||||
|
||||
for (i = 0; i < videoTracks.length; i++) {
|
||||
videoStream.addTrack(videoTracks[i]);
|
||||
}
|
||||
|
||||
|
||||
this.service.createLocalStream(videoStream, "video");
|
||||
}
|
||||
else
|
||||
{//firefox
|
||||
this.service.createLocalStream(stream, "stream");
|
||||
audioStream = stream.audioStream;
|
||||
videoStream = stream.videoStream;
|
||||
}
|
||||
|
||||
var audioMuted = (usageOptions && usageOptions.audio === false),
|
||||
videoMuted = (usageOptions && usageOptions.video === false);
|
||||
|
||||
var audioGUM = (!usageOptions || usageOptions.audio !== false),
|
||||
videoGUM = (!usageOptions || usageOptions.video !== false);
|
||||
|
||||
|
||||
this.service.createLocalStream(audioStream, "audio", null, null,
|
||||
audioMuted, audioGUM);
|
||||
|
||||
this.service.createLocalStream(videoStream, "video", null, null,
|
||||
videoMuted, videoGUM);
|
||||
};
|
||||
|
||||
RTCUtils.prototype.createStream = function(stream, isVideo)
|
||||
{
|
||||
var newStream = null;
|
||||
if(window.webkitMediaStream)
|
||||
{
|
||||
newStream = new webkitMediaStream();
|
||||
if(newStream)
|
||||
{
|
||||
var tracks = (isVideo? stream.getVideoTracks() : stream.getAudioTracks());
|
||||
|
||||
for (i = 0; i < tracks.length; i++) {
|
||||
newStream.addTrack(tracks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RTCUtils;
|
||||
}
|
||||
else
|
||||
newStream = stream;
|
||||
|
||||
return newStream;
|
||||
};
|
||||
|
||||
module.exports = RTCUtils;
|
||||
|
||||
277
modules/UI/UI.js
@@ -26,11 +26,18 @@ var DesktopSharingEventTypes
|
||||
var RTCEvents = require("../../service/RTC/RTCEvents");
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var MemberEvents = require("../../service/members/Events");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
var roomName = null;
|
||||
|
||||
|
||||
function notifyForInitialMute()
|
||||
{
|
||||
messageHandler.notify(null, "notify.mutedTitle", "connected",
|
||||
"notify.muted", null, {timeOut: 120000});
|
||||
}
|
||||
|
||||
function setupPrezi()
|
||||
{
|
||||
$("#reloadPresentationLink").click(function()
|
||||
@@ -53,24 +60,42 @@ function setupToolbars() {
|
||||
BottomToolbar.init();
|
||||
}
|
||||
|
||||
function streamHandler(stream) {
|
||||
function streamHandler(stream, isMuted) {
|
||||
switch (stream.type)
|
||||
{
|
||||
case "audio":
|
||||
VideoLayout.changeLocalAudio(stream);
|
||||
VideoLayout.changeLocalAudio(stream, isMuted);
|
||||
break;
|
||||
case "video":
|
||||
VideoLayout.changeLocalVideo(stream);
|
||||
VideoLayout.changeLocalVideo(stream, isMuted);
|
||||
break;
|
||||
case "stream":
|
||||
VideoLayout.changeLocalStream(stream);
|
||||
VideoLayout.changeLocalStream(stream, isMuted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onXmppConnectionFailed(stropheErrorMsg) {
|
||||
|
||||
var title = APP.translation.generateTranslatonHTML(
|
||||
"dialog.error");
|
||||
|
||||
var message;
|
||||
if (stropheErrorMsg) {
|
||||
message = APP.translation.generateTranslatonHTML(
|
||||
"dialog.connectErrorWithMsg", {msg: stropheErrorMsg});
|
||||
} else {
|
||||
message = APP.translation.generateTranslatonHTML(
|
||||
"dialog.connectError");
|
||||
}
|
||||
|
||||
messageHandler.openDialog(
|
||||
title, message, true, {}, function (e, v, m, f) { return false; });
|
||||
}
|
||||
|
||||
function onDisposeConference(unload) {
|
||||
Toolbar.showAuthenticateButton(false);
|
||||
};
|
||||
}
|
||||
|
||||
function onDisplayNameChanged(jid, displayName) {
|
||||
ContactList.onDisplayNameChange(jid, displayName);
|
||||
@@ -85,6 +110,9 @@ function registerListeners() {
|
||||
APP.RTC.addStreamListener(function (stream) {
|
||||
VideoLayout.onRemoteStreamAdded(stream);
|
||||
}, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
|
||||
APP.RTC.addStreamListener(function (jid) {
|
||||
VideoLayout.onVideoTypeChanged(jid);
|
||||
}, StreamEventTypes.EVENT_TYPE_REMOTE_CHANGED);
|
||||
APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged);
|
||||
APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (resourceJid) {
|
||||
VideoLayout.onDominantSpeakerChanged(resourceJid);
|
||||
@@ -102,7 +130,10 @@ function registerListeners() {
|
||||
function (endpointSimulcastLayers) {
|
||||
VideoLayout.onSimulcastLayersChanging(endpointSimulcastLayers);
|
||||
});
|
||||
|
||||
APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED,
|
||||
function (devices) {
|
||||
VideoLayout.setDeviceAvailabilityIcons(null, devices);
|
||||
})
|
||||
APP.statistics.addAudioLevelListener(function(jid, audioLevel)
|
||||
{
|
||||
var resourceJid;
|
||||
@@ -134,20 +165,53 @@ function registerListeners() {
|
||||
VideoLayout.updateConnectionStats);
|
||||
APP.connectionquality.addListener(CQEvents.STOP,
|
||||
VideoLayout.onStatsStop);
|
||||
APP.xmpp.addListener(XMPPEvents.CONNECTION_FAILED, onXmppConnectionFailed);
|
||||
APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
|
||||
APP.xmpp.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () {
|
||||
messageHandler.openMessageDialog(
|
||||
'dialog.serviceUnavailable',
|
||||
'dialog.gracefulShutdown'
|
||||
);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) {
|
||||
var title = APP.translation.generateTranslatonHTML(
|
||||
"dialog.reservationError");
|
||||
var message = APP.translation.generateTranslatonHTML(
|
||||
"dialog.reservationErrorMsg", {code: code, msg: msg});
|
||||
messageHandler.openDialog(
|
||||
title,
|
||||
message,
|
||||
true, {},
|
||||
function (event, value, message, formVals)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.KICKED, function () {
|
||||
messageHandler.openMessageDialog("dialog.sessTerminated", "Session Terminated",
|
||||
"dialog.kickMessage", "Ouch! You have been kicked out of the meet!");
|
||||
messageHandler.openMessageDialog("dialog.sessTerminated",
|
||||
"dialog.kickMessage");
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_DESTROYED, function (reason) {
|
||||
//FIXME: use Session Terminated from translation, but
|
||||
// 'reason' text comes from XMPP packet and is not translated
|
||||
var title = APP.translation.generateTranslatonHTML("dialog.sessTerminated");
|
||||
messageHandler.openDialog(
|
||||
title, reason, true, {},
|
||||
function (event, value, message, formVals)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () {
|
||||
messageHandler.showError("dialog.error","Error",
|
||||
"dialog.bridgeUnavailable",
|
||||
"Jitsi Videobridge is currently unavailable. Please try again later!");
|
||||
messageHandler.showError("dialog.error",
|
||||
"dialog.bridgeUnavailable");
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.USER_ID_CHANGED, function (from, id) {
|
||||
Avatar.setUserAvatar(from, id);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
|
||||
APP.xmpp.addListener(XMPPEvents.STREAMS_CHANGED, function (jid, changedStreams) {
|
||||
for(stream in changedStreams)
|
||||
{
|
||||
// might need to update the direction if participant just went from sendrecv to recvonly
|
||||
@@ -169,19 +233,28 @@ function registerListeners() {
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined);
|
||||
APP.xmpp.addListener(XMPPEvents.LOCALROLE_CHANGED, onLocalRoleChange);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_ENTER, onMucEntered);
|
||||
APP.xmpp.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, onLocalRoleChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, onMucMemberJoined);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus);
|
||||
APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject);
|
||||
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_LEFT, onMucLeft);
|
||||
APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordReqiured);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft);
|
||||
APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordRequired);
|
||||
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
|
||||
APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad);
|
||||
APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, onAuthenticationRequired);
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED,
|
||||
onAuthenticationRequired);
|
||||
APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE,
|
||||
function (resource, devices) {
|
||||
VideoLayout.setDeviceAvailabilityIcons(resource, devices);
|
||||
});
|
||||
|
||||
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
||||
onDtmfSupportChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
|
||||
SettingsMenu.setStartMuted(audio, video);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -195,21 +268,8 @@ function registerListeners() {
|
||||
* contrast to an automatic decision taken by the application logic)
|
||||
*/
|
||||
function setVideoMute(mute, options) {
|
||||
APP.xmpp.setVideoMute(
|
||||
mute,
|
||||
function (mute) {
|
||||
var video = $('#video');
|
||||
var communicativeClass = "icon-camera";
|
||||
var muteClass = "icon-camera icon-camera-disabled";
|
||||
|
||||
if (mute) {
|
||||
video.removeClass(communicativeClass);
|
||||
video.addClass(muteClass);
|
||||
} else {
|
||||
video.removeClass(muteClass);
|
||||
video.addClass(communicativeClass);
|
||||
}
|
||||
},
|
||||
APP.RTC.setVideoMute(mute,
|
||||
UI.setVideoMuteButtonsState,
|
||||
options);
|
||||
}
|
||||
|
||||
@@ -305,6 +365,12 @@ UI.start = function (init) {
|
||||
|
||||
$("#welcome_page").hide();
|
||||
|
||||
// Display notice message at the top of the toolbar
|
||||
if (config.noticeMessage) {
|
||||
$('#noticeText').text(config.noticeMessage);
|
||||
$('#notice').css({display: 'block'});
|
||||
}
|
||||
|
||||
document.getElementById('largeVideo').volume = 0;
|
||||
|
||||
if (!$('#settings').is(':visible')) {
|
||||
@@ -361,9 +427,8 @@ function updateChatConversation(from, displayName, message) {
|
||||
|
||||
function onMucJoined(jid, info) {
|
||||
Toolbar.updateRoomUrl(window.location.href);
|
||||
document.getElementById('localNick').appendChild(
|
||||
document.createTextNode(Strophe.getResourceFromJid(jid) + ' (me)')
|
||||
);
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
$("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")");
|
||||
|
||||
var settings = Settings.getSettings();
|
||||
// Add myself to the contact list.
|
||||
@@ -376,20 +441,23 @@ function onMucJoined(jid, info) {
|
||||
? info.displayName : Strophe.getResourceFromJid(jid);
|
||||
|
||||
if (displayName)
|
||||
onDisplayNameChanged('localVideoContainer', displayName + ' (me)');
|
||||
onDisplayNameChanged('localVideoContainer', displayName);
|
||||
|
||||
|
||||
VideoLayout.mucJoined();
|
||||
}
|
||||
|
||||
function initEtherpad(name) {
|
||||
Etherpad.init(name);
|
||||
};
|
||||
|
||||
function onMucLeft(jid) {
|
||||
function onMucMemberLeft(jid) {
|
||||
console.log('left.muc', jid);
|
||||
var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) +
|
||||
'>.displayname').html();
|
||||
messageHandler.notify(displayName,'notify.somebody', "Somebody",
|
||||
messageHandler.notify(displayName,'notify.somebody',
|
||||
'disconnected',
|
||||
'notify.disconnected', "disconnected");
|
||||
'notify.disconnected');
|
||||
// Need to call this with a slight delay, otherwise the element couldn't be
|
||||
// found for some reason.
|
||||
// XXX(gp) it works fine without the timeout for me (with Chrome 38).
|
||||
@@ -410,18 +478,18 @@ function onMucLeft(jid) {
|
||||
};
|
||||
|
||||
|
||||
function onLocalRoleChange(jid, info, pres, isModerator)
|
||||
function onLocalRoleChanged(jid, info, pres, isModerator)
|
||||
{
|
||||
|
||||
console.info("My role changed, new role: " + info.role);
|
||||
onModeratorStatusChanged(isModerator);
|
||||
VideoLayout.showModeratorIndicator();
|
||||
SettingsMenu.onRoleChanged();
|
||||
|
||||
if (isModerator) {
|
||||
Authentication.closeAuthenticationWindow();
|
||||
messageHandler.notify(null, "notify.me",
|
||||
'Me', 'connected', "notify.moderator",
|
||||
'Moderator rights granted !');
|
||||
'connected', "notify.moderator");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,37 +508,49 @@ function onModeratorStatusChanged(isModerator) {
|
||||
}
|
||||
};
|
||||
|
||||
function onPasswordReqiured(callback) {
|
||||
function onPasswordRequired(callback) {
|
||||
// password is required
|
||||
Toolbar.lockLockButton();
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
message += APP.translation.translateString(
|
||||
"dialog.passwordRequired", null, "Password required");
|
||||
"dialog.passwordRequired");
|
||||
message += '</h2>' +
|
||||
'<input id="lockKey" type="text" placeholder="password" autofocus>';
|
||||
'<input name="lockKey" type="text" data-i18n=' +
|
||||
'"[placeholder]dialog.password" placeholder="' +
|
||||
APP.translation.translateString("dialog.password") +
|
||||
'" autofocus>';
|
||||
|
||||
messageHandler.openTwoButtonDialog(null, null, null, message,
|
||||
true,
|
||||
"dialog.Ok",
|
||||
function (e, v, m, f) {},
|
||||
function (event) {
|
||||
document.getElementById('lockKey').focus();
|
||||
},
|
||||
null,
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var lockKey = document.getElementById('lockKey');
|
||||
if (lockKey.value !== null) {
|
||||
Toolbar.setSharedKey(lockKey.value);
|
||||
callback(lockKey.value);
|
||||
var lockKey = f.lockKey;
|
||||
if (lockKey) {
|
||||
Toolbar.setSharedKey(lockKey);
|
||||
callback(lockKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
':input:first'
|
||||
);
|
||||
}
|
||||
function onMucEntered(jid, id, displayName) {
|
||||
messageHandler.notify(displayName,'notify.somebody', "Somebody",
|
||||
|
||||
/**
|
||||
* The dialpad button is shown iff there is at least one member that supports
|
||||
* DTMF (e.g. jigasi).
|
||||
*/
|
||||
function onDtmfSupportChanged(dtmfSupport) {
|
||||
//TODO: enable when the UI is ready
|
||||
//Toolbar.showDialPadButton(dtmfSupport);
|
||||
}
|
||||
|
||||
function onMucMemberJoined(jid, id, displayName) {
|
||||
messageHandler.notify(displayName,'notify.somebody',
|
||||
'connected',
|
||||
'notify.connected', "connected");
|
||||
'notify.connected');
|
||||
|
||||
// Add Peer's container
|
||||
VideoLayout.ensurePeerContainerExists(jid,id);
|
||||
@@ -486,9 +566,7 @@ function onMucRoleChanged(role, displayName) {
|
||||
|
||||
if (role === 'moderator') {
|
||||
var messageKey, messageOptions = {};
|
||||
var lDisplayName = displayName;
|
||||
if (!lDisplayName) {
|
||||
lDisplayName = 'Somebody';
|
||||
if (!displayName) {
|
||||
messageKey = "notify.grantedToUnknown";
|
||||
}
|
||||
else
|
||||
@@ -497,9 +575,8 @@ function onMucRoleChanged(role, displayName) {
|
||||
messageOptions = {to: displayName};
|
||||
}
|
||||
messageHandler.notify(
|
||||
displayName,'notify.somebody', "Somebody",
|
||||
displayName,'notify.somebody',
|
||||
'connected', messageKey,
|
||||
'Moderator rights granted to ' + lDisplayName + '!',
|
||||
messageOptions);
|
||||
}
|
||||
}
|
||||
@@ -590,49 +667,30 @@ UI.connectionIndicatorShowMore = function(id)
|
||||
return VideoLayout.connectionIndicators[id].showMore();
|
||||
};
|
||||
|
||||
UI.getCredentials = function () {
|
||||
var settings = this.getSettings();
|
||||
return {
|
||||
bosh: document.getElementById('boshURL').value,
|
||||
password: document.getElementById('password').value,
|
||||
jid: document.getElementById('jid').value,
|
||||
email: settings.email,
|
||||
displayName: settings.displayName,
|
||||
uid: settings.uid
|
||||
};
|
||||
};
|
||||
|
||||
UI.disableConnect = function () {
|
||||
document.getElementById('connect').disabled = true;
|
||||
};
|
||||
|
||||
UI.showLoginPopup = function(callback)
|
||||
{
|
||||
console.log('password is required');
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
message += APP.translation.translateString(
|
||||
"dialog.passwordRequired", null, "Password required");
|
||||
"dialog.passwordRequired");
|
||||
message += '</h2>' +
|
||||
'<input id="passwordrequired.username" type="text" ' +
|
||||
'<input name="username" type="text" ' +
|
||||
'placeholder="user@domain.net" autofocus>' +
|
||||
'<input id="passwordrequired.password" ' +
|
||||
'type="password" placeholder="user password">';
|
||||
'<input name="password" ' +
|
||||
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
|
||||
' placeholder="user password">';
|
||||
UI.messageHandler.openTwoButtonDialog(null, null, null, message,
|
||||
true,
|
||||
"dialog.Ok",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var username = document.getElementById('passwordrequired.username');
|
||||
var password = document.getElementById('passwordrequired.password');
|
||||
|
||||
if (username.value !== null && password.value != null) {
|
||||
callback(username.value, password.value);
|
||||
if (f.username !== null && f.password != null) {
|
||||
callback(f.username, f.password);
|
||||
}
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
document.getElementById('passwordrequired.username').focus();
|
||||
}
|
||||
null, null, ':input:first'
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -671,12 +729,16 @@ UI.getRoomName = function () {
|
||||
return roomName;
|
||||
};
|
||||
|
||||
UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
|
||||
if(muteAudio || muteVideo) notifyForInitialMute();
|
||||
if(muteAudio) UI.setAudioMuted(true);
|
||||
if(muteVideo) UI.setVideoMute(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutes/unmutes the local video.
|
||||
*/
|
||||
UI.toggleVideo = function () {
|
||||
UIUtil.buttonClick("#video", "icon-camera icon-camera-disabled");
|
||||
|
||||
setVideoMute(!APP.RTC.localVideo.isMuted());
|
||||
};
|
||||
|
||||
@@ -690,9 +752,17 @@ UI.toggleAudio = function() {
|
||||
/**
|
||||
* Sets muted audio state for the local participant.
|
||||
*/
|
||||
UI.setAudioMuted = function (mute) {
|
||||
|
||||
if(!APP.xmpp.setAudioMute(mute, function () {
|
||||
UI.setAudioMuted = function (mute, earlyMute) {
|
||||
var audioMute = null;
|
||||
if(earlyMute)
|
||||
audioMute = function (mute, cb) {
|
||||
return APP.xmpp.sendAudioInfoPresence(mute, cb);
|
||||
};
|
||||
else
|
||||
audioMute = function (mute, cb) {
|
||||
return APP.xmpp.setAudioMute(mute, cb);
|
||||
}
|
||||
if(!audioMute(mute, function () {
|
||||
VideoLayout.showLocalAudioIndicator(mute);
|
||||
|
||||
UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
@@ -726,5 +796,22 @@ UI.dockToolbar = function (isDock) {
|
||||
return ToolbarToggler.dockToolbar(isDock);
|
||||
}
|
||||
|
||||
UI.setVideoMuteButtonsState = function (mute) {
|
||||
var video = $('#video');
|
||||
var communicativeClass = "icon-camera";
|
||||
var muteClass = "icon-camera icon-camera-disabled";
|
||||
|
||||
if (mute) {
|
||||
video.removeClass(communicativeClass);
|
||||
video.addClass(muteClass);
|
||||
} else {
|
||||
video.removeClass(muteClass);
|
||||
video.addClass(communicativeClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UI.setVideoMute = setVideoMute;
|
||||
|
||||
module.exports = UI;
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/* global $, APP*/
|
||||
|
||||
var LoginDialog = require('./LoginDialog');
|
||||
var Moderator = require('../../xmpp/moderator');
|
||||
|
||||
/* Initial "authentication required" dialog */
|
||||
var authDialog = null;
|
||||
/* Loop retry ID that wits for other user to create the room */
|
||||
@@ -8,7 +13,7 @@ var Authentication = {
|
||||
openAuthenticationDialog: function (roomName, intervalCallback, callback) {
|
||||
// This is the loop that will wait for the room to be created by
|
||||
// someone else. 'auth_required.moderator' will bring us back here.
|
||||
authRetryId = window.setTimeout(intervalCallback , 5000);
|
||||
authRetryId = window.setTimeout(intervalCallback, 5000);
|
||||
// Show prompt only if it's not open
|
||||
if (authDialog !== null) {
|
||||
return;
|
||||
@@ -16,18 +21,16 @@ var Authentication = {
|
||||
// extract room name from 'room@muc.server.net'
|
||||
var room = roomName.substr(0, roomName.indexOf('@'));
|
||||
|
||||
var title = APP.translation.generateTranslatonHTML("dialog.Stop",
|
||||
"Stop");
|
||||
var defMsg = 'Authentication is required to create room:<br/><b>' +
|
||||
room +
|
||||
'</b></br> You can either authenticate to create the room or ' +
|
||||
'just wait for someone else to do so.';
|
||||
var msg = APP.translation.generateTranslatonHTML("dialog.AuthMsg",
|
||||
defMsg, {room: room});
|
||||
var button = APP.translation.generateTranslatonHTML(
|
||||
"dialog.Authenticate", "Authenticate");
|
||||
var buttons = {};
|
||||
buttons.authenticate = {title: button, value: "authNow"};
|
||||
var title
|
||||
= APP.translation.generateTranslatonHTML("dialog.WaitingForHost");
|
||||
var msg
|
||||
= APP.translation.generateTranslatonHTML(
|
||||
"dialog.WaitForHostMsg", {room: room});
|
||||
|
||||
var buttonTxt
|
||||
= APP.translation.generateTranslatonHTML("dialog.IamHost");
|
||||
var buttons = [];
|
||||
buttons.push({title: buttonTxt, value: "authNow"});
|
||||
|
||||
authDialog = APP.UI.messageHandler.openDialog(
|
||||
title,
|
||||
@@ -46,12 +49,43 @@ var Authentication = {
|
||||
}
|
||||
);
|
||||
},
|
||||
closeAuthenticationWindow:function () {
|
||||
closeAuthenticationWindow: function () {
|
||||
if (authenticationWindow) {
|
||||
authenticationWindow.close();
|
||||
authenticationWindow = null;
|
||||
}
|
||||
},
|
||||
xmppAuthenticate: function () {
|
||||
|
||||
var loginDialog = LoginDialog.show(
|
||||
function (connection, state) {
|
||||
if (!state) {
|
||||
// User cancelled
|
||||
loginDialog.close();
|
||||
return;
|
||||
} else if (state == APP.xmpp.Status.CONNECTED) {
|
||||
|
||||
loginDialog.close();
|
||||
|
||||
Authentication.stopInterval();
|
||||
Authentication.closeAuthenticationDialog();
|
||||
|
||||
// Close the connection as anonymous one will be used
|
||||
// to create the conference. Session-id will authorize
|
||||
// the request.
|
||||
connection.disconnect();
|
||||
|
||||
var roomName = APP.UI.generateRoomName();
|
||||
Moderator.allocateConferenceFocus(roomName, function () {
|
||||
// If it's not "on the fly" authentication now join
|
||||
// the conference room
|
||||
if (!APP.xmpp.getMUCJoined()) {
|
||||
APP.UI.checkForNicknameAndJoin();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
focusAuthenticationWindow: function () {
|
||||
// If auth window exists just bring it to the front
|
||||
if (authenticationWindow) {
|
||||
@@ -62,7 +96,7 @@ var Authentication = {
|
||||
closeAuthenticationDialog: function () {
|
||||
// Close authentication dialog if opened
|
||||
if (authDialog) {
|
||||
APP.UI.messageHandler.closeDialog();
|
||||
authDialog.close();
|
||||
authDialog = null;
|
||||
}
|
||||
},
|
||||
@@ -72,10 +106,7 @@ var Authentication = {
|
||||
// On closed
|
||||
function () {
|
||||
// Close authentication dialog if opened
|
||||
if (authDialog) {
|
||||
messageHandler.closeDialog();
|
||||
authDialog = null;
|
||||
}
|
||||
Authentication.closeAuthenticationDialog();
|
||||
callback();
|
||||
authenticationWindow = null;
|
||||
});
|
||||
|
||||
228
modules/UI/authentication/LoginDialog.js
Normal file
@@ -0,0 +1,228 @@
|
||||
/* global $, APP, config*/
|
||||
|
||||
var XMPP = require('../../xmpp/xmpp');
|
||||
var Moderator = require('../../xmpp/moderator');
|
||||
|
||||
//FIXME: use LoginDialog to add retries to XMPP.connect method used when
|
||||
// anonymous domain is not enabled
|
||||
|
||||
/**
|
||||
* Creates new <tt>Dialog</tt> instance.
|
||||
* @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt> called
|
||||
* when we either fail to connect or succeed(check Strophe.Status).
|
||||
* @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to Jicofo
|
||||
* in order to create session-id after the connection is established.
|
||||
* @constructor
|
||||
*/
|
||||
function Dialog(callback, obtainSession) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var stop = false;
|
||||
|
||||
var connection = APP.xmpp.createConnection();
|
||||
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
message += APP.translation.translateString("dialog.passwordRequired");
|
||||
message += '</h2>' +
|
||||
'<input name="username" type="text" ' +
|
||||
'placeholder="user@domain.net" autofocus>' +
|
||||
'<input name="password" ' +
|
||||
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
|
||||
' placeholder="user password">';
|
||||
|
||||
var okButton = APP.translation.generateTranslatonHTML("dialog.Ok");
|
||||
|
||||
var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel");
|
||||
|
||||
var states = {
|
||||
login: {
|
||||
html: message,
|
||||
buttons: [
|
||||
{ title: okButton, value: true},
|
||||
{ title: cancelButton, value: false}
|
||||
],
|
||||
focus: ':input:first',
|
||||
submit: function (e, v, m, f) {
|
||||
e.preventDefault();
|
||||
if (v) {
|
||||
var jid = f.username;
|
||||
var password = f.password;
|
||||
if (jid && password) {
|
||||
stop = false;
|
||||
connection.reset();
|
||||
connDialog.goToState('connecting');
|
||||
connection.connect(jid, password, stateHandler);
|
||||
}
|
||||
} else {
|
||||
// User cancelled
|
||||
stop = true;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
},
|
||||
connecting: {
|
||||
title: APP.translation.translateString('dialog.connecting'),
|
||||
html: '<div id="connectionStatus"></div>',
|
||||
buttons: [],
|
||||
defaultButton: 0
|
||||
},
|
||||
finished: {
|
||||
title: APP.translation.translateString('dialog.error'),
|
||||
html: '<div id="errorMessage"></div>',
|
||||
buttons: [
|
||||
{
|
||||
title: APP.translation.translateString('dialog.retry'),
|
||||
value: 'retry'
|
||||
},
|
||||
{
|
||||
title: APP.translation.translateString('dialog.Cancel'),
|
||||
value: 'cancel'
|
||||
},
|
||||
],
|
||||
defaultButton: 0,
|
||||
submit: function (e, v, m, f) {
|
||||
e.preventDefault();
|
||||
if (v === 'retry')
|
||||
connDialog.goToState('login');
|
||||
else
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var connDialog
|
||||
= APP.UI.messageHandler.openDialogWithStates(states,
|
||||
{ persistent: true, closeText: '' }, null);
|
||||
|
||||
var stateHandler = function (status, message) {
|
||||
if (stop) {
|
||||
return;
|
||||
}
|
||||
|
||||
var translateKey = "connection." + XMPP.getStatusString(status);
|
||||
var statusStr = APP.translation.translateString(translateKey);
|
||||
|
||||
// Display current state
|
||||
var connectionStatus =
|
||||
connDialog.getState('connecting').find('#connectionStatus');
|
||||
|
||||
connectionStatus.text(statusStr);
|
||||
|
||||
switch (status) {
|
||||
case XMPP.Status.CONNECTED:
|
||||
|
||||
stop = true;
|
||||
if (!obtainSession) {
|
||||
callback(connection, status);
|
||||
return;
|
||||
}
|
||||
// Obtaining session-id status
|
||||
connectionStatus.text(
|
||||
APP.translation.translateString(
|
||||
'connection.FETCH_SESSION_ID'));
|
||||
|
||||
// Authenticate with Jicofo and obtain session-id
|
||||
var roomName = APP.UI.generateRoomName();
|
||||
|
||||
// Jicofo will return new session-id when connected
|
||||
// from authenticated domain
|
||||
connection.sendIQ(
|
||||
Moderator.createConferenceIq(roomName),
|
||||
function (result) {
|
||||
|
||||
connectionStatus.text(
|
||||
APP.translation.translateString(
|
||||
'connection.GOT_SESSION_ID'));
|
||||
|
||||
stop = true;
|
||||
|
||||
// Parse session-id
|
||||
Moderator.parseSessionId(result);
|
||||
|
||||
callback(connection, status);
|
||||
},
|
||||
function (error) {
|
||||
console.error("Auth on the fly failed", error);
|
||||
|
||||
stop = true;
|
||||
|
||||
var errorMsg =
|
||||
APP.translation.translateString(
|
||||
'connection.GET_SESSION_ID_ERROR') +
|
||||
$(error).find('>error').attr('code');
|
||||
|
||||
self.displayError(errorMsg);
|
||||
|
||||
connection.disconnect();
|
||||
});
|
||||
|
||||
break;
|
||||
case XMPP.Status.AUTHFAIL:
|
||||
case XMPP.Status.CONNFAIL:
|
||||
case XMPP.Status.DISCONNECTED:
|
||||
|
||||
stop = true;
|
||||
|
||||
callback(connection, status);
|
||||
|
||||
var errorMessage = statusStr;
|
||||
|
||||
if (message)
|
||||
{
|
||||
errorMessage += ': ' + message;
|
||||
}
|
||||
self.displayError(errorMessage);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays error message in 'finished' state which allows either to cancel
|
||||
* or retry.
|
||||
* @param message the final message to be displayed.
|
||||
*/
|
||||
this.displayError = function (message) {
|
||||
|
||||
var finishedState = connDialog.getState('finished');
|
||||
|
||||
var errorMessageElem = finishedState.find('#errorMessage');
|
||||
errorMessageElem.text(message);
|
||||
|
||||
connDialog.goToState('finished');
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes LoginDialog.
|
||||
*/
|
||||
this.close = function () {
|
||||
stop = true;
|
||||
connDialog.close();
|
||||
};
|
||||
}
|
||||
|
||||
var LoginDialog = {
|
||||
|
||||
/**
|
||||
* Displays login prompt used to establish new XMPP connection. Given
|
||||
* <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
|
||||
* called when we connect successfully(status === CONNECTED) or when we fail
|
||||
* to do so. On connection failure program can call Dialog.close() method in
|
||||
* order to cancel or do nothing to let the user retry.
|
||||
* @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
|
||||
* called when we either fail to connect or succeed(check
|
||||
* Strophe.Status).
|
||||
* @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
|
||||
* Jicofo in order to create session-id after the connection is
|
||||
* established.
|
||||
* @returns {Dialog}
|
||||
*/
|
||||
show: function (callback, obtainSession) {
|
||||
return new Dialog(callback, obtainSession);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LoginDialog;
|
||||
@@ -20,6 +20,12 @@ function isUserMuted(jid) {
|
||||
}
|
||||
}
|
||||
|
||||
if(jid && jid == APP.xmpp.myJid())
|
||||
{
|
||||
var localVideo = APP.RTC.localVideo;
|
||||
return (!localVideo || localVideo.isMuted());
|
||||
}
|
||||
|
||||
if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
||||
return null;
|
||||
}
|
||||
@@ -119,8 +125,9 @@ var Avatar = {
|
||||
} else {
|
||||
if (video && video.length > 0) {
|
||||
setVisibility(video, !show);
|
||||
setVisibility(avatar, show);
|
||||
}
|
||||
setVisibility(avatar, show);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -34,8 +34,9 @@ var Prezi = {
|
||||
var myprezi = APP.xmpp.getPrezi();
|
||||
if (myprezi) {
|
||||
messageHandler.openTwoButtonDialog("dialog.removePreziTitle",
|
||||
"Remove Prezi", "dialog.removePreziMsg",
|
||||
"Are you sure you would like to remove your Prezi?",
|
||||
null,
|
||||
"dialog.removePreziMsg",
|
||||
null,
|
||||
false,
|
||||
"dialog.Remove",
|
||||
function(e,v,m,f) {
|
||||
@@ -47,9 +48,8 @@ var Prezi = {
|
||||
}
|
||||
else if (preziPlayer != null) {
|
||||
messageHandler.openTwoButtonDialog("dialog.sharePreziTitle",
|
||||
"Share a Prezi", "dialog.sharePreziMsg",
|
||||
"Another participant is already sharing a Prezi." +
|
||||
"This conference allows only one Prezi at a time.",
|
||||
null, "dialog.sharePreziMsg",
|
||||
null,
|
||||
false,
|
||||
"dialog.Ok",
|
||||
function(e,v,m,f) {
|
||||
@@ -59,39 +59,47 @@ var Prezi = {
|
||||
}
|
||||
else {
|
||||
var html = APP.translation.generateTranslatonHTML(
|
||||
"dialog.sharePreziTitle", "Share a Prezi");
|
||||
"dialog.sharePreziTitle");
|
||||
var cancelButton = APP.translation.generateTranslatonHTML(
|
||||
"dialog.Cancel", "Cancel");
|
||||
"dialog.Cancel");
|
||||
var shareButton = APP.translation.generateTranslatonHTML(
|
||||
"dialog.Share", "Share");
|
||||
"dialog.Share");
|
||||
var backButton = APP.translation.generateTranslatonHTML(
|
||||
"dialog.Back", "Back");
|
||||
var buttons = {};
|
||||
var buttons1 = {};
|
||||
buttons1.Cancel = buttons.Cancel = {title: cancelButton, value: false};
|
||||
buttons.share = {title: shareButton, value: true};
|
||||
buttons1.Back = {title: backButton, value: true};
|
||||
"dialog.Back");
|
||||
var buttons = [];
|
||||
var buttons1 = [];
|
||||
// Cancel button to both states
|
||||
buttons.push({title: cancelButton, value: false});
|
||||
buttons1.push({title: cancelButton, value: false});
|
||||
// Share button
|
||||
buttons.push({title: shareButton, value: true});
|
||||
// Back button
|
||||
buttons1.push({title: backButton, value: true});
|
||||
var linkError = APP.translation.generateTranslatonHTML(
|
||||
"dialog.preziLinkError", "Please provide a correct prezi link.")
|
||||
"dialog.preziLinkError");
|
||||
var defaultUrl = APP.translation.translateString("defaultPreziLink",
|
||||
{url: "http://prezi.com/wz7vhjycl7e6/my-prezi"});
|
||||
var openPreziState = {
|
||||
state0: {
|
||||
html: '<h2>' + html + '</h2>' +
|
||||
'<input id="preziUrl" type="text" ' +
|
||||
'placeholder="e.g. ' +
|
||||
'http://prezi.com/wz7vhjycl7e6/my-prezi" autofocus>',
|
||||
'<input name="preziUrl" type="text" ' +
|
||||
'data-i18n="[placeholder]defaultPreziLink" data-i18n-options=\'' +
|
||||
JSON.stringify({"url": "http://prezi.com/wz7vhjycl7e6/my-prezi"}) +
|
||||
'\' placeholder="' + defaultUrl + '" autofocus>',
|
||||
persistent: false,
|
||||
buttons: buttons,
|
||||
defaultButton: 1,
|
||||
submit: function(e,v,m,f){
|
||||
focus: ':input:first',
|
||||
defaultButton: 0,
|
||||
submit: function (e, v, m, f) {
|
||||
e.preventDefault();
|
||||
if(v)
|
||||
{
|
||||
var preziUrl = document.getElementById('preziUrl');
|
||||
var preziUrl = f.preziUrl;
|
||||
|
||||
if (preziUrl.value)
|
||||
if (preziUrl)
|
||||
{
|
||||
var urlValue
|
||||
= encodeURI(UIUtil.escapeHtml(preziUrl.value));
|
||||
= encodeURI(UIUtil.escapeHtml(preziUrl));
|
||||
|
||||
if (urlValue.indexOf('http://prezi.com/') != 0
|
||||
&& urlValue.indexOf('https://prezi.com/') != 0)
|
||||
@@ -123,20 +131,18 @@ var Prezi = {
|
||||
linkError,
|
||||
persistent: false,
|
||||
buttons: buttons1,
|
||||
focus: ':input:first',
|
||||
defaultButton: 1,
|
||||
submit:function(e,v,m,f) {
|
||||
submit: function (e, v, m, f) {
|
||||
e.preventDefault();
|
||||
if(v==0)
|
||||
if (v === 0)
|
||||
$.prompt.close();
|
||||
else
|
||||
$.prompt.goToState('state0');
|
||||
}
|
||||
}
|
||||
};
|
||||
var focusPreziUrl = function(e) {
|
||||
document.getElementById('preziUrl').focus();
|
||||
};
|
||||
messageHandler.openDialogWithStates(openPreziState, focusPreziUrl, focusPreziUrl);
|
||||
messageHandler.openDialogWithStates(openPreziState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,15 @@ function createAvatar(id) {
|
||||
*
|
||||
* @param displayName the display name to set
|
||||
*/
|
||||
function createDisplayNameParagraph(displayName) {
|
||||
function createDisplayNameParagraph(key, displayName) {
|
||||
var p = document.createElement('p');
|
||||
p.innerText = displayName;
|
||||
if(displayName)
|
||||
p.innerText = displayName;
|
||||
else if(key)
|
||||
{
|
||||
p.setAttribute("data-i18n",key);
|
||||
p.innerText = APP.translation.translateString(key);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -106,7 +112,7 @@ var ContactList = {
|
||||
};
|
||||
|
||||
newContact.appendChild(createAvatar(id));
|
||||
newContact.appendChild(createDisplayNameParagraph("Participant"));
|
||||
newContact.appendChild(createDisplayNameParagraph("participant"));
|
||||
|
||||
var clElement = contactlist.get(0);
|
||||
|
||||
|
||||
@@ -26,18 +26,44 @@ function generateLanguagesSelectBox()
|
||||
var SettingsMenu = {
|
||||
|
||||
init: function () {
|
||||
$("#updateSettings").before(generateLanguagesSelectBox());
|
||||
$("#startMutedOptions").before(generateLanguagesSelectBox());
|
||||
APP.translation.translateElement($("#languages_selectbox"));
|
||||
$('#settingsmenu>input').keyup(function(event){
|
||||
if(event.keyCode === 13) {//enter
|
||||
SettingsMenu.update();
|
||||
}
|
||||
});
|
||||
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
}
|
||||
|
||||
$("#updateSettings").click(function () {
|
||||
SettingsMenu.update();
|
||||
});
|
||||
},
|
||||
|
||||
onRoleChanged: function () {
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
}
|
||||
},
|
||||
|
||||
setStartMuted: function (audio, video) {
|
||||
$("#startAudioMuted").attr("checked", audio);
|
||||
$("#startVideoMuted").attr("checked", video);
|
||||
},
|
||||
|
||||
update: function() {
|
||||
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
|
||||
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
|
||||
@@ -47,11 +73,17 @@ var SettingsMenu = {
|
||||
APP.xmpp.addToPresence("displayName", displayName, true);
|
||||
}
|
||||
|
||||
APP.translation.setLanguage($("#languages_selectbox").val());
|
||||
var language = $("#languages_selectbox").val();
|
||||
APP.translation.setLanguage(language);
|
||||
Settings.setLanguage(language);
|
||||
|
||||
APP.xmpp.addToPresence("email", newEmail);
|
||||
var email = Settings.setEmail(newEmail);
|
||||
|
||||
var startAudioMuted = ($("#startAudioMuted").is(":checked"));
|
||||
var startVideoMuted = ($("#startVideoMuted").is(":checked"));
|
||||
APP.xmpp.addToPresence("startMuted",
|
||||
[startAudioMuted, startVideoMuted]);
|
||||
|
||||
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
|
||||
},
|
||||
|
||||
@@ -54,6 +54,9 @@ var buttonHandlers =
|
||||
"toolbar_button_sip": function () {
|
||||
return callSipButtonClicked();
|
||||
},
|
||||
"toolbar_button_dialpad": function () {
|
||||
return dialpadButtonClicked();
|
||||
},
|
||||
"toolbar_button_settings": function () {
|
||||
PanelToggler.toggleSettingsMenu();
|
||||
},
|
||||
@@ -66,9 +69,12 @@ var buttonHandlers =
|
||||
"toolbar_button_logout": function () {
|
||||
// Ask for confirmation
|
||||
messageHandler.openTwoButtonDialog(
|
||||
"Logout",
|
||||
"Are you sure you want to logout and stop the conference ?",
|
||||
false, "Yes",
|
||||
"dialog.logoutTitle",
|
||||
null,
|
||||
"dialog.logoutQuestion",
|
||||
null,
|
||||
false,
|
||||
"dialog.Yes",
|
||||
function (evt, yes) {
|
||||
if (yes) {
|
||||
APP.xmpp.logout(function (url) {
|
||||
@@ -96,13 +102,13 @@ function hangup() {
|
||||
}
|
||||
|
||||
var title = APP.translation.generateTranslatonHTML(
|
||||
"dialog.sessTerminated", "Session Terminated");
|
||||
"dialog.sessTerminated");
|
||||
var msg = APP.translation.generateTranslatonHTML(
|
||||
"dialog.hungUp","You hung up the call");
|
||||
"dialog.hungUp");
|
||||
var button = APP.translation.generateTranslatonHTML(
|
||||
"dialog.joinAgain", "Join again");
|
||||
var buttons = {};
|
||||
buttons.joinAgain = {title: button, value: true};
|
||||
"dialog.joinAgain");
|
||||
var buttons = [];
|
||||
buttons.push({title: button, value: true});
|
||||
|
||||
UI.messageHandler.openDialog(
|
||||
title,
|
||||
@@ -124,27 +130,27 @@ function hangup() {
|
||||
function toggleRecording() {
|
||||
APP.xmpp.toggleRecording(function (callback) {
|
||||
var msg = APP.translation.generateTranslatonHTML(
|
||||
"dialog.recordingToken", "Enter recording token");
|
||||
"dialog.recordingToken");
|
||||
var token = APP.translation.translateString("dialog.token");
|
||||
APP.UI.messageHandler.openTwoButtonDialog(null, null, null,
|
||||
'<h2>' + msg + '</h2>' +
|
||||
'<input id="recordingToken" type="text" ' +
|
||||
'placeholder="token" autofocus>',
|
||||
'<input name="recordingToken" type="text" ' +
|
||||
' data-i18n="[placeholder]dialog.token" ' +
|
||||
'placeholder="' + token + '" autofocus>',
|
||||
false,
|
||||
"dialog.Save",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var token = document.getElementById('recordingToken');
|
||||
var token = f.recordingToken;
|
||||
|
||||
if (token.value) {
|
||||
callback(UIUtil.escapeHtml(token.value));
|
||||
if (token) {
|
||||
callback(UIUtil.escapeHtml(token));
|
||||
}
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
document.getElementById('recordingToken').focus();
|
||||
},
|
||||
function () {
|
||||
}
|
||||
null,
|
||||
function () { },
|
||||
':input:first'
|
||||
);
|
||||
}, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
|
||||
}
|
||||
@@ -171,16 +177,13 @@ function lockRoom(lock) {
|
||||
}
|
||||
}, function (err) {
|
||||
console.warn('setting password failed', err);
|
||||
messageHandler.showError("dialog.lockTitle", 'Lock failed',
|
||||
"dialog.lockMessage",
|
||||
'Failed to lock conference.',
|
||||
err);
|
||||
messageHandler.showError("dialog.lockTitle",
|
||||
"dialog.lockMessage");
|
||||
Toolbar.setSharedKey('');
|
||||
}, function () {
|
||||
console.warn('room passwords not supported');
|
||||
messageHandler.showError("dialog.warning", 'Warning',
|
||||
"dialog.passwordNotSupported",
|
||||
'Room passwords are currently not supported.');
|
||||
messageHandler.showError("dialog.warning",
|
||||
"dialog.passwordNotSupported");
|
||||
Toolbar.setSharedKey('');
|
||||
});
|
||||
};
|
||||
@@ -195,25 +198,20 @@ function inviteParticipants() {
|
||||
var sharedKeyText = "";
|
||||
if (sharedKey && sharedKey.length > 0) {
|
||||
sharedKeyText =
|
||||
"This conference is password protected. Please use the " +
|
||||
"following pin when joining:%0D%0A%0D%0A" +
|
||||
sharedKey + "%0D%0A%0D%0A";
|
||||
APP.translation.translateString("email.sharedKey",
|
||||
{sharedKey: sharedKey});
|
||||
sharedKeyText = sharedKeyText.replace(/\n/g, "%0D%0A");
|
||||
}
|
||||
|
||||
var supportedBrowsers = "Chromium, Google Chrome " +
|
||||
APP.translation.translateString("email.and") + " Opera";
|
||||
var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1);
|
||||
var subject = "Invitation to a " + interfaceConfig.APP_NAME + " (" + conferenceName + ")";
|
||||
var body = "Hey there, I%27d like to invite you to a " + interfaceConfig.APP_NAME +
|
||||
" conference I%27ve just set up.%0D%0A%0D%0A" +
|
||||
"Please click on the following link in order" +
|
||||
" to join the conference.%0D%0A%0D%0A" +
|
||||
roomUrl +
|
||||
"%0D%0A%0D%0A" +
|
||||
sharedKeyText +
|
||||
"Note that " + interfaceConfig.APP_NAME + " is currently" +
|
||||
" only supported by Chromium," +
|
||||
" Google Chrome and Opera, so you need" +
|
||||
" to be using one of these browsers.%0D%0A%0D%0A" +
|
||||
"Talk to you in a sec!";
|
||||
var subject = APP.translation.translateString("email.subject",
|
||||
{appName:interfaceConfig.APP_NAME, conferenceName: conferenceName});
|
||||
var body = APP.translation.translateString("email.body",
|
||||
{appName:interfaceConfig.APP_NAME, sharedKeyText: sharedKeyText,
|
||||
roomUrl: roomUrl, supportedBrowsers: supportedBrowsers});
|
||||
body = body.replace(/\n/g, "%0D%0A");
|
||||
|
||||
if (window.localStorage.displayname) {
|
||||
body += "%0D%0A%0D%0A" + window.localStorage.displayname;
|
||||
@@ -226,31 +224,34 @@ function inviteParticipants() {
|
||||
window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
|
||||
}
|
||||
|
||||
function dialpadButtonClicked()
|
||||
{
|
||||
//TODO show the dialpad window
|
||||
}
|
||||
|
||||
function callSipButtonClicked()
|
||||
{
|
||||
var defaultNumber
|
||||
= config.defaultSipNumber ? config.defaultSipNumber : '';
|
||||
|
||||
var sipMsg = APP.translation.generateTranslatonHTML(
|
||||
"dialog.sipMsg", "Enter SIP number");
|
||||
"dialog.sipMsg");
|
||||
messageHandler.openTwoButtonDialog(null, null, null,
|
||||
'<h2>' + sipMsg + '</h2>' +
|
||||
'<input id="sipNumber" type="text"' +
|
||||
'<input name="sipNumber" type="text"' +
|
||||
' value="' + defaultNumber + '" autofocus>',
|
||||
false,
|
||||
"dialog.Dial",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var numberInput = document.getElementById('sipNumber');
|
||||
if (numberInput.value) {
|
||||
APP.xmpp.dial(numberInput.value, 'fromnumber',
|
||||
UI.getRoomName(), sharedKey);
|
||||
var numberInput = f.sipNumber;
|
||||
if (numberInput) {
|
||||
APP.xmpp.dial(
|
||||
numberInput, 'fromnumber', UI.getRoomName(), sharedKey);
|
||||
}
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
document.getElementById('sipNumber').focus();
|
||||
}
|
||||
null, null, ':input:first'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -270,12 +271,6 @@ var Toolbar = (function (my) {
|
||||
loggedIn = true;
|
||||
}
|
||||
|
||||
//FIXME: XMPP authentication need improvements for "live" login
|
||||
if (!APP.xmpp.isExternalAuthEnabled() && !loggedIn)
|
||||
{
|
||||
authenticationEnabled = false;
|
||||
}
|
||||
|
||||
Toolbar.showAuthenticateButton(authenticationEnabled);
|
||||
|
||||
if (authenticationEnabled) {
|
||||
@@ -298,6 +293,10 @@ var Toolbar = (function (my) {
|
||||
|
||||
my.authenticateClicked = function () {
|
||||
Authentication.focusAuthenticationWindow();
|
||||
if (!APP.xmpp.isExternalAuthEnabled()) {
|
||||
Authentication.xmppAuthenticate();
|
||||
return;
|
||||
}
|
||||
// Get authentication URL
|
||||
if (!APP.xmpp.getMUCJoined()) {
|
||||
APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {
|
||||
@@ -317,10 +316,7 @@ var Toolbar = (function (my) {
|
||||
}, url);
|
||||
if (!authenticationWindow) {
|
||||
messageHandler.openMessageDialog(
|
||||
null, null, "dialog.popupError",
|
||||
"Your browser is blocking popup windows from this site." +
|
||||
" Please enable popups in your browser security settings" +
|
||||
" and try again.");
|
||||
null, "dialog.popupError");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -337,7 +333,8 @@ var Toolbar = (function (my) {
|
||||
if (inviteLink) {
|
||||
inviteLink.value = roomUrl;
|
||||
inviteLink.select();
|
||||
document.getElementById('jqi_state0_buttonInvite').disabled = false;
|
||||
$('#inviteLinkRef').parent()
|
||||
.find('button[value=true]').prop('disabled', false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -358,26 +355,16 @@ var Toolbar = (function (my) {
|
||||
// Only the focus is able to set a shared key.
|
||||
if (!APP.xmpp.isModerator()) {
|
||||
if (sharedKey) {
|
||||
messageHandler.openMessageDialog(null, null,
|
||||
"dialog.passwordError",
|
||||
"This conversation is currently protected by" +
|
||||
" a password. Only the owner of the conference" +
|
||||
" could set a password.",
|
||||
false,
|
||||
"Password");
|
||||
messageHandler.openMessageDialog(null,
|
||||
"dialog.passwordError");
|
||||
} else {
|
||||
messageHandler.openMessageDialog(null, null, "dialog.passwordError2",
|
||||
"This conversation isn't currently protected by" +
|
||||
" a password. Only the owner of the conference" +
|
||||
" could set a password.",
|
||||
false,
|
||||
"Password");
|
||||
messageHandler.openMessageDialog(null, "dialog.passwordError2");
|
||||
}
|
||||
} else {
|
||||
if (sharedKey) {
|
||||
messageHandler.openTwoButtonDialog(null, null,
|
||||
"dialog.passwordCheck",
|
||||
"Are you sure you would like to remove your password?",
|
||||
null,
|
||||
false,
|
||||
"dialog.Remove",
|
||||
function (e, v) {
|
||||
@@ -388,28 +375,28 @@ var Toolbar = (function (my) {
|
||||
});
|
||||
} else {
|
||||
var msg = APP.translation.generateTranslatonHTML(
|
||||
"dialog.passwordMsg", "Set a password to lock your room");
|
||||
"dialog.passwordMsg");
|
||||
var yourPassword = APP.translation.translateString(
|
||||
"dialog.yourPassword", null, "your password");
|
||||
"dialog.yourPassword");
|
||||
messageHandler.openTwoButtonDialog(null, null, null,
|
||||
'<h2>' + msg + '</h2>' +
|
||||
'<input id="lockKey" type="text"' +
|
||||
'<input name="lockKey" type="text"' +
|
||||
' data-i18n="[placeholder]dialog.yourPassword" ' +
|
||||
'placeholder="' + yourPassword + '" autofocus>',
|
||||
false,
|
||||
"dialog.Save",
|
||||
function (e, v) {
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
var lockKey = document.getElementById('lockKey');
|
||||
var lockKey = f.lockKey;
|
||||
|
||||
if (lockKey.value) {
|
||||
Toolbar.setSharedKey(UIUtil.escapeHtml(lockKey.value));
|
||||
if (lockKey) {
|
||||
Toolbar.setSharedKey(
|
||||
UIUtil.escapeHtml(lockKey));
|
||||
lockRoom(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
function () {
|
||||
document.getElementById('lockKey').focus();
|
||||
}
|
||||
null, null, 'input:first'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -419,16 +406,18 @@ var Toolbar = (function (my) {
|
||||
* Opens the invite link dialog.
|
||||
*/
|
||||
my.openLinkDialog = function () {
|
||||
var inviteLink;
|
||||
var inviteAttreibutes;
|
||||
|
||||
if (roomUrl === null) {
|
||||
inviteLink = "Your conference is currently being created...";
|
||||
inviteAttreibutes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
|
||||
APP.translation.translateString("roomUrlDefaultMsg") + '"';
|
||||
} else {
|
||||
inviteLink = encodeURI(roomUrl);
|
||||
inviteAttreibutes = "value=\"" + encodeURI(roomUrl) + "\"";
|
||||
}
|
||||
messageHandler.openTwoButtonDialog("dialog.shareLink",
|
||||
"Share this link with everyone you want to invite", null,
|
||||
'<input id="inviteLinkRef" type="text" value="' +
|
||||
inviteLink + '" onclick="this.select();" readonly>',
|
||||
null, null,
|
||||
'<input id="inviteLinkRef" type="text" ' +
|
||||
inviteAttreibutes + ' onclick="this.select();" readonly>',
|
||||
false,
|
||||
"dialog.Invite",
|
||||
function (e, v) {
|
||||
@@ -438,12 +427,13 @@ var Toolbar = (function (my) {
|
||||
}
|
||||
}
|
||||
},
|
||||
function () {
|
||||
function (event) {
|
||||
if (roomUrl) {
|
||||
document.getElementById('inviteLinkRef').select();
|
||||
} else {
|
||||
document.getElementById('jqi_state0_buttonInvite')
|
||||
.disabled = true;
|
||||
if (event && event.target)
|
||||
$(event.target)
|
||||
.find('button[value=true]').prop('disabled', true);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -451,18 +441,18 @@ var Toolbar = (function (my) {
|
||||
|
||||
/**
|
||||
* Opens the settings dialog.
|
||||
* FIXME: not used ?
|
||||
*/
|
||||
my.openSettingsDialog = function () {
|
||||
var settings1 = APP.translation.generateTranslatonHTML(
|
||||
"dialog.settings1", "Configure your conference");
|
||||
"dialog.settings1");
|
||||
var settings2 = APP.translation.generateTranslatonHTML(
|
||||
"dialog.settings2", "Participants join muted");
|
||||
"dialog.settings2");
|
||||
var settings3 = APP.translation.generateTranslatonHTML(
|
||||
"dialog.settings3", "Require nicknames<br/><br/>" +
|
||||
"Set a password to lock your room:");
|
||||
"dialog.settings3");
|
||||
|
||||
var yourPassword = APP.translation.translateString(
|
||||
"dialog.yourPassword", null, "your password");
|
||||
"dialog.yourPassword");
|
||||
|
||||
messageHandler.openTwoButtonDialog(null,
|
||||
'<h2>' + settings1 + '</h2>' +
|
||||
@@ -569,12 +559,13 @@ var Toolbar = (function (my) {
|
||||
|
||||
// Sets the state of the recording button
|
||||
my.setRecordingButtonState = function (isRecording) {
|
||||
var selector = $('#recordButton');
|
||||
if (isRecording) {
|
||||
$('#recordButton').removeClass("icon-recEnable");
|
||||
$('#recordButton').addClass("icon-recEnable active");
|
||||
selector.removeClass("icon-recEnable");
|
||||
selector.addClass("icon-recEnable active");
|
||||
} else {
|
||||
$('#recordButton').removeClass("icon-recEnable active");
|
||||
$('#recordButton').addClass("icon-recEnable");
|
||||
selector.removeClass("icon-recEnable active");
|
||||
selector.addClass("icon-recEnable");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -587,14 +578,24 @@ var Toolbar = (function (my) {
|
||||
}
|
||||
};
|
||||
|
||||
// Shows or hides the dialpad button
|
||||
my.showDialPadButton = function (show) {
|
||||
if (show) {
|
||||
$('#dialPadButton').css({display: "inline-block"});
|
||||
} else {
|
||||
$('#dialPadButton').css({display: "none"});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays user authenticated identity name(login).
|
||||
* @param authIdentity identity name to be displayed.
|
||||
*/
|
||||
my.setAuthenticatedIdentity = function (authIdentity) {
|
||||
if (authIdentity) {
|
||||
$('#toolbar_auth_identity').css({display: "list-item"});
|
||||
$('#toolbar_auth_identity').text(authIdentity);
|
||||
var selector = $('#toolbar_auth_identity');
|
||||
selector.css({display: "list-item"});
|
||||
selector.text(authIdentity);
|
||||
} else {
|
||||
$('#toolbar_auth_identity').css({display: "none"});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ function showDesktopSharingButton() {
|
||||
* Hides the toolbar.
|
||||
*/
|
||||
function hideToolbar() {
|
||||
if(config.alwaysVisibleToolbar)
|
||||
return;
|
||||
|
||||
var header = $("#header"),
|
||||
bottomToolbar = $("#bottomToolbar");
|
||||
var isToolbarHover = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, jQuery */
|
||||
/* global $, APP, jQuery, toastr */
|
||||
var messageHandler = (function(my) {
|
||||
|
||||
/**
|
||||
@@ -7,16 +7,13 @@ var messageHandler = (function(my) {
|
||||
* @param titleString the title of the message
|
||||
* @param messageString the text of the message
|
||||
*/
|
||||
my.openMessageDialog = function(titleKey, titleString,
|
||||
messageKey, messageString) {
|
||||
my.openMessageDialog = function(titleKey, messageKey) {
|
||||
var title = null;
|
||||
if(titleKey)
|
||||
{
|
||||
title = APP.translation.generateTranslatonHTML(titleKey,
|
||||
titleString);
|
||||
title = APP.translation.generateTranslatonHTML(titleKey);
|
||||
}
|
||||
var message = APP.translation.generateTranslatonHTML(messageKey,
|
||||
messageString);
|
||||
var message = APP.translation.generateTranslatonHTML(messageKey);
|
||||
$.prompt(message,
|
||||
{
|
||||
title: title,
|
||||
@@ -35,30 +32,38 @@ var messageHandler = (function(my) {
|
||||
* @param submitFunction function to be called on submit
|
||||
* @param loadedFunction function to be called after the prompt is fully loaded
|
||||
* @param closeFunction function to be called after the prompt is closed
|
||||
* @param focus optional focus selector or button index to be focused after
|
||||
* the dialog is opened
|
||||
* @param defaultButton index of default button which will be activated when
|
||||
* the user press 'enter'. Indexed from 0.
|
||||
*/
|
||||
my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString,
|
||||
persistent, leftButtonKey, submitFunction, loadedFunction,
|
||||
closeFunction)
|
||||
closeFunction, focus, defaultButton)
|
||||
{
|
||||
var buttons = [];
|
||||
|
||||
var leftButton = APP.translation.generateTranslatonHTML(leftButtonKey);
|
||||
var buttons = {};
|
||||
buttons.leftButton = {title: leftButton, value: true};
|
||||
var cancelButton = APP.translation.generateTranslatonHTML("dialog.Cancel",
|
||||
"Cancel");
|
||||
buttons.Cancel = {title: cancelButton, value: false};
|
||||
buttons.push({ title: leftButton, value: true});
|
||||
|
||||
var cancelButton
|
||||
= APP.translation.generateTranslatonHTML("dialog.Cancel");
|
||||
buttons.push({title: cancelButton, value: false});
|
||||
|
||||
var message = msgString, title = titleString;
|
||||
if(titleKey)
|
||||
if (titleKey)
|
||||
{
|
||||
title = APP.translation.generateTranslatonHTML(titleKey, titleString);
|
||||
title = APP.translation.generateTranslatonHTML(titleKey);
|
||||
}
|
||||
if(msgKey) {
|
||||
message = APP.translation.generateTranslatonHTML(msgKey, msgString);
|
||||
if (msgKey) {
|
||||
message = APP.translation.generateTranslatonHTML(msgKey);
|
||||
}
|
||||
$.prompt(message, {
|
||||
title: title,
|
||||
persistent: false,
|
||||
buttons: buttons,
|
||||
defaultButton: 1,
|
||||
defaultButton: defaultButton,
|
||||
focus: focus,
|
||||
loaded: loadedFunction,
|
||||
submit: submitFunction,
|
||||
close: closeFunction
|
||||
@@ -76,7 +81,7 @@ var messageHandler = (function(my) {
|
||||
* @param submitFunction function to be called on submit
|
||||
* @param loadedFunction function to be called after the prompt is fully loaded
|
||||
*/
|
||||
my.openDialog = function (titleString, msgString, persistent, buttons,
|
||||
my.openDialog = function (titleString, msgString, persistent, buttons,
|
||||
submitFunction, loadedFunction) {
|
||||
var args = {
|
||||
title: titleString,
|
||||
@@ -89,7 +94,7 @@ var messageHandler = (function(my) {
|
||||
if (persistent) {
|
||||
args.closeText = '';
|
||||
}
|
||||
return $.prompt(msgString, args);
|
||||
return new Impromptu(msgString, args);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,16 +108,10 @@ var messageHandler = (function(my) {
|
||||
* Shows a dialog with different states to the user.
|
||||
*
|
||||
* @param statesObject object containing all the states of the dialog
|
||||
* @param loadedFunction function to be called after the prompt is fully loaded
|
||||
* @param stateChangedFunction function to be called when the state of the dialog is changed
|
||||
*/
|
||||
my.openDialogWithStates = function(statesObject, loadedFunction, stateChangedFunction) {
|
||||
my.openDialogWithStates = function (statesObject, options) {
|
||||
|
||||
|
||||
var myPrompt = $.prompt(statesObject);
|
||||
|
||||
myPrompt.on('impromptu:loaded', loadedFunction);
|
||||
myPrompt.on('impromptu:statechanged', stateChangedFunction);
|
||||
return new Impromptu(statesObject, options);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -152,9 +151,8 @@ var messageHandler = (function(my) {
|
||||
* @param msgString the text of the message
|
||||
* @param error the error that is being reported
|
||||
*/
|
||||
my.openReportDialog = function(titleKey, titleString, msgKey,
|
||||
msgString, error) {
|
||||
my.openMessageDialog(titleKey, titleString, msgKey, msgString);
|
||||
my.openReportDialog = function(titleKey, msgKey, error) {
|
||||
my.openMessageDialog(titleKey, msgKey);
|
||||
console.log(error);
|
||||
//FIXME send the error to the server
|
||||
};
|
||||
@@ -164,22 +162,20 @@ var messageHandler = (function(my) {
|
||||
* @param title the title of the message
|
||||
* @param message the text of the messafe
|
||||
*/
|
||||
my.showError = function(titleKey, title, msgKey, message) {
|
||||
my.showError = function(titleKey, msgKey) {
|
||||
|
||||
if(!titleKey) {
|
||||
title = "Oops!";
|
||||
titleKey = "dialog.oops";
|
||||
}
|
||||
if(!msgKey)
|
||||
{
|
||||
message = "There was some kind of error";
|
||||
msgKey = "dialog.defaultError";
|
||||
}
|
||||
messageHandler.openMessageDialog(titleKey, title, msgKey, message);
|
||||
messageHandler.openMessageDialog(titleKey, msgKey);
|
||||
};
|
||||
|
||||
my.notify = function(displayName, displayNameKey, displayNameDefault,
|
||||
cls, messageKey, messageDefault, messageArguments) {
|
||||
my.notify = function(displayName, displayNameKey,
|
||||
cls, messageKey, messageArguments, options) {
|
||||
var displayNameSpan = '<span class="nickname" ';
|
||||
if(displayName)
|
||||
{
|
||||
@@ -188,23 +184,18 @@ var messageHandler = (function(my) {
|
||||
else
|
||||
{
|
||||
displayNameSpan += "data-i18n='" + displayNameKey +
|
||||
"'>" + APP.translation.translateString(displayNameKey, null,
|
||||
{defaultValue: displayNameDefault});
|
||||
"'>" + APP.translation.translateString(displayNameKey);
|
||||
}
|
||||
displayNameSpan += "</span>";
|
||||
var lMessageArguments = messageArguments;
|
||||
if(!messageArguments)
|
||||
lMessageArguments = {};
|
||||
lMessageArguments.defaultValue = messageDefault;
|
||||
toastr.info(
|
||||
displayNameSpan + '<br>' +
|
||||
'<span class=' + cls + ' data-i18n="' + messageKey + '"' +
|
||||
(messageArguments?
|
||||
" i18n-options='" + JSON.stringify(messageArguments) + "'"
|
||||
" data-i18n-options='" + JSON.stringify(messageArguments) + "'"
|
||||
: "") + ">" +
|
||||
APP.translation.translateString(messageKey, null,
|
||||
lMessageArguments) +
|
||||
'</span>');
|
||||
APP.translation.translateString(messageKey,
|
||||
messageArguments) +
|
||||
'</span>', null, options);
|
||||
};
|
||||
|
||||
return my;
|
||||
|
||||
@@ -229,47 +229,43 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||
|
||||
}
|
||||
|
||||
var local_address_key = "connectionindicator." +
|
||||
(data.localIP.length > 1? "localaddresses" : "localaddress");
|
||||
var remote_address_key = "connectionindicator." +
|
||||
(data.remoteIP.length > 1? "remoteaddresses" : "remoteaddress");
|
||||
var local_address_key = "connectionindicator.localaddress";
|
||||
var remote_address_key = "connectionindicator.remoteaddress";
|
||||
var localTransport =
|
||||
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
|
||||
local_address_key +"'>" +
|
||||
translate(local_address_key) + "</span></td><td> " +
|
||||
local_address_key +"' data-i18n-options='" +
|
||||
JSON.stringify({count: data.localIP.length}) + "'>" +
|
||||
translate(local_address_key, {count: data.localIP.length}) +
|
||||
"</span></td><td> " +
|
||||
ConnectionIndicator.getStringFromArray(data.localIP) +
|
||||
"</td></tr>";
|
||||
transport =
|
||||
"<tr><td><span class='jitsipopover_blue' data-i18n='" +
|
||||
remote_address_key + "'>" +
|
||||
translate(remote_address_key) + "</span></td><td> " +
|
||||
remote_address_key + "' data-i18n-options='" +
|
||||
JSON.stringify({count: data.remoteIP.length}) + "'>" +
|
||||
translate(remote_address_key,
|
||||
{count: data.remoteIP.length}) +
|
||||
"</span></td><td> " +
|
||||
ConnectionIndicator.getStringFromArray(data.remoteIP) +
|
||||
"</td></tr>";
|
||||
|
||||
var key_remote = "connectionindicator.",
|
||||
key_local = "connectionindicator.";
|
||||
|
||||
if(this.transport.length > 1)
|
||||
{
|
||||
key_remote += "remoteports";
|
||||
key_local += "localports";
|
||||
}
|
||||
else
|
||||
{
|
||||
key_remote += "remoteport";
|
||||
key_local += "localport";
|
||||
}
|
||||
var key_remote = "connectionindicator.remoteport",
|
||||
key_local = "connectionindicator.localport";
|
||||
|
||||
transport += "<tr>" +
|
||||
"<td>" +
|
||||
"<span class='jitsipopover_blue' data-i18n='" + key_remote +
|
||||
"'>" +
|
||||
translate(key_remote) + "</span></td><td>";
|
||||
"' data-i18n-options='" +
|
||||
JSON.stringify({count: this.transport.length}) + "'>" +
|
||||
translate(key_remote, {count: this.transport.length}) +
|
||||
"</span></td><td>";
|
||||
localTransport += "<tr>" +
|
||||
"<td>" +
|
||||
"<span class='jitsipopover_blue' data-i18n='" + key_local +
|
||||
"'>" +
|
||||
translate(key_local) + "</span></td><td>";
|
||||
"' data-i18n-options='" +
|
||||
JSON.stringify({count: this.transport.length}) + "'>" +
|
||||
translate(key_local, {count: this.transport.length}) +
|
||||
"</span></td><td>";
|
||||
|
||||
transport +=
|
||||
ConnectionIndicator.getStringFromArray(data.remotePort);
|
||||
|
||||
@@ -191,9 +191,10 @@ function getCameraVideoSize(videoWidth,
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
*/
|
||||
function setDisplayName(videoSpanId, displayName) {
|
||||
function setDisplayName(videoSpanId, displayName, key) {
|
||||
var nameSpan = $('#' + videoSpanId + '>span.displayname');
|
||||
var defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
var defaultLocalDisplayName = APP.translation.generateTranslatonHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
@@ -202,14 +203,25 @@ function setDisplayName(videoSpanId, displayName) {
|
||||
if (nameSpanElement.id === 'localDisplayName' &&
|
||||
$('#localDisplayName').text() !== displayName) {
|
||||
if (displayName && displayName.length > 0)
|
||||
$('#localDisplayName').html(displayName + ' (me)');
|
||||
{
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
$('#localDisplayName').html(displayName + ' (' + meHTML + ')');
|
||||
}
|
||||
else
|
||||
$('#localDisplayName').text(defaultLocalDisplayName);
|
||||
$('#localDisplayName').html(defaultLocalDisplayName);
|
||||
} else {
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
$('#' + videoSpanId + '_name').html(displayName);
|
||||
}
|
||||
else if (key && key.length > 0)
|
||||
{
|
||||
var nameHtml = APP.translation.generateTranslatonHTML(key);
|
||||
$('#' + videoSpanId + '_name').html(nameHtml);
|
||||
}
|
||||
else
|
||||
$('#' + videoSpanId + '_name').text(interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
|
||||
$('#' + videoSpanId + '_name').text(
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
|
||||
}
|
||||
} else {
|
||||
var editButton = null;
|
||||
@@ -220,15 +232,22 @@ function setDisplayName(videoSpanId, displayName) {
|
||||
|
||||
if (videoSpanId === 'localVideoContainer') {
|
||||
editButton = createEditDisplayNameButton();
|
||||
nameSpan.innerText = defaultLocalDisplayName;
|
||||
if (displayName && displayName.length > 0) {
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
nameSpan.innerHTML = displayName + meHTML;
|
||||
}
|
||||
else
|
||||
nameSpan.innerHTML = defaultLocalDisplayName;
|
||||
}
|
||||
else {
|
||||
nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
if (displayName && displayName.length > 0) {
|
||||
|
||||
nameSpan.innerText = displayName;
|
||||
}
|
||||
else
|
||||
nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
nameSpan.innerText = displayName;
|
||||
}
|
||||
|
||||
if (!editButton) {
|
||||
nameSpan.id = videoSpanId + '_name';
|
||||
@@ -245,11 +264,18 @@ function setDisplayName(videoSpanId, displayName) {
|
||||
|
||||
if (displayName && displayName.length) {
|
||||
editableText.value
|
||||
= displayName.substring(0, displayName.indexOf(' (me)'));
|
||||
= displayName;
|
||||
}
|
||||
|
||||
var defaultNickname = APP.translation.translateString(
|
||||
"defaultNickname", {name: "Jane Pink"});
|
||||
editableText.setAttribute('style', 'display:none;');
|
||||
editableText.setAttribute('placeholder', 'ex. Jane Pink');
|
||||
editableText.setAttribute('data-18n',
|
||||
'[placeholder]defaultNickname');
|
||||
editableText.setAttribute("data-i18n-options",
|
||||
JSON.stringify({name: "Jane Pink"}));
|
||||
editableText.setAttribute("placeholder", defaultNickname);
|
||||
|
||||
$('#' + videoSpanId)[0].appendChild(editableText);
|
||||
|
||||
$('#localVideoContainer .displayname')
|
||||
@@ -524,24 +550,58 @@ var VideoLayout = (function (my) {
|
||||
|| (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
|
||||
};
|
||||
|
||||
my.changeLocalStream = function (stream) {
|
||||
VideoLayout.changeLocalVideo(stream);
|
||||
my.changeLocalStream = function (stream, isMuted) {
|
||||
VideoLayout.changeLocalVideo(stream, isMuted);
|
||||
};
|
||||
|
||||
my.changeLocalAudio = function(stream) {
|
||||
my.changeLocalAudio = function(stream, isMuted) {
|
||||
if(isMuted)
|
||||
APP.UI.setAudioMuted(true, true);
|
||||
APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
|
||||
document.getElementById('localAudio').autoplay = true;
|
||||
document.getElementById('localAudio').volume = 0;
|
||||
if (preMuted) {
|
||||
if(!APP.UI.setAudioMuted(true))
|
||||
{
|
||||
preMuted = mute;
|
||||
}
|
||||
preMuted = false;
|
||||
}
|
||||
};
|
||||
|
||||
my.changeLocalVideo = function(stream) {
|
||||
my.changeLocalVideo = function(stream, isMuted) {
|
||||
// Set default display name.
|
||||
setDisplayName('localVideoContainer');
|
||||
|
||||
if(!VideoLayout.connectionIndicators["localVideoContainer"]) {
|
||||
VideoLayout.connectionIndicators["localVideoContainer"]
|
||||
= new ConnectionIndicator($("#localVideoContainer")[0], null, VideoLayout);
|
||||
}
|
||||
|
||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||
|
||||
var localVideo = null;
|
||||
|
||||
function localVideoClick(event) {
|
||||
event.stopPropagation();
|
||||
VideoLayout.handleVideoThumbClicked(
|
||||
APP.RTC.getVideoSrc(localVideo),
|
||||
false,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
|
||||
$('#localVideoContainer').click(localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
VideoLayout.showDisplayName('localVideoContainer', true);
|
||||
},
|
||||
function() {
|
||||
if (!VideoLayout.isLargeVideoVisible()
|
||||
|| APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
VideoLayout.showDisplayName('localVideoContainer', false);
|
||||
}
|
||||
);
|
||||
|
||||
if(isMuted)
|
||||
{
|
||||
APP.UI.setVideoMute(true);
|
||||
return;
|
||||
}
|
||||
var flipX = true;
|
||||
if(stream.videoType == "screen")
|
||||
flipX = false;
|
||||
@@ -555,55 +615,29 @@ var VideoLayout = (function (my) {
|
||||
var localVideoContainer = document.getElementById('localVideoWrapper');
|
||||
localVideoContainer.appendChild(localVideo);
|
||||
|
||||
// Set default display name.
|
||||
setDisplayName('localVideoContainer');
|
||||
|
||||
if(!VideoLayout.connectionIndicators["localVideoContainer"]) {
|
||||
VideoLayout.connectionIndicators["localVideoContainer"]
|
||||
= new ConnectionIndicator($("#localVideoContainer")[0], null, VideoLayout);
|
||||
}
|
||||
|
||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||
|
||||
var localVideoSelector = $('#' + localVideo.id);
|
||||
|
||||
function localVideoClick(event) {
|
||||
event.stopPropagation();
|
||||
VideoLayout.handleVideoThumbClicked(
|
||||
APP.RTC.getVideoSrc(localVideo),
|
||||
false,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
// Add click handler to both video and video wrapper elements in case
|
||||
// there's no video.
|
||||
localVideoSelector.click(localVideoClick);
|
||||
$('#localVideoContainer').click(localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
VideoLayout.showDisplayName('localVideoContainer', true);
|
||||
},
|
||||
function() {
|
||||
if (!VideoLayout.isLargeVideoVisible()
|
||||
|| APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
VideoLayout.showDisplayName('localVideoContainer', false);
|
||||
}
|
||||
);
|
||||
// Add stream ended handler
|
||||
stream.getOriginalStream().onended = function () {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
|
||||
};
|
||||
// Flip video x axis if needed
|
||||
flipXLocalVideo = flipX;
|
||||
if (flipX) {
|
||||
localVideoSelector.addClass("flipVideoX");
|
||||
}
|
||||
|
||||
// Attach WebRTC stream
|
||||
var videoStream = APP.simulcast.getLocalVideoStream();
|
||||
APP.RTC.attachMediaStream(localVideoSelector, videoStream);
|
||||
|
||||
// Add stream ended handler
|
||||
stream.getOriginalStream().onended = function () {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
|
||||
};
|
||||
|
||||
|
||||
localVideoSrc = APP.RTC.getVideoSrc(localVideo);
|
||||
|
||||
var myResourceJid = APP.xmpp.myResource();
|
||||
@@ -613,6 +647,56 @@ var VideoLayout = (function (my) {
|
||||
|
||||
};
|
||||
|
||||
my.mucJoined = function () {
|
||||
var myResourceJid = APP.xmpp.myResource();
|
||||
|
||||
if(!largeVideoState.userResourceJid)
|
||||
VideoLayout.updateLargeVideo(localVideoSrc, 0,
|
||||
myResourceJid, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or removes icons for not available camera and microphone.
|
||||
* @param resourceJid the jid of user
|
||||
* @param devices available devices
|
||||
*/
|
||||
my.setDeviceAvailabilityIcons = function (resourceJid, devices) {
|
||||
if(!devices)
|
||||
return;
|
||||
|
||||
var container = null
|
||||
if(!resourceJid)
|
||||
{
|
||||
container = $("#localVideoContainer")[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
container = $("#participant_" + resourceJid)[0];
|
||||
}
|
||||
|
||||
if(!container)
|
||||
return;
|
||||
|
||||
$("#" + container.id + " > .noMic").remove();
|
||||
$("#" + container.id + " > .noVideo").remove();
|
||||
if(!devices.audio)
|
||||
{
|
||||
container.appendChild(document.createElement("div")).setAttribute("class","noMic");
|
||||
}
|
||||
|
||||
if(!devices.video)
|
||||
{
|
||||
container.appendChild(document.createElement("div")).setAttribute("class","noVideo");
|
||||
}
|
||||
|
||||
if(!devices.audio && !devices.video)
|
||||
{
|
||||
$("#" + container.id + " > .noMic").css("background-position", "75%");
|
||||
$("#" + container.id + " > .noVideo").css("background-position", "25%");
|
||||
$("#" + container.id + " > .noVideo").css("background-color", "transparent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if removed video is currently displayed and tries to display
|
||||
* another one instead.
|
||||
@@ -638,26 +722,34 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
}
|
||||
|
||||
var src = null, volume = null;
|
||||
// mute if localvideo
|
||||
if (pick) {
|
||||
var container = pick.parentNode;
|
||||
var jid = null;
|
||||
if(container)
|
||||
{
|
||||
if(container.id == "localVideoWrapper")
|
||||
{
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = VideoLayout.getPeerContainerResourceJid(container);
|
||||
}
|
||||
}
|
||||
|
||||
VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(pick), pick.volume, jid);
|
||||
src = APP.RTC.getVideoSrc(pick);
|
||||
volume = pick.volume;
|
||||
} else {
|
||||
console.warn("Failed to elect large video");
|
||||
container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
|
||||
|
||||
}
|
||||
|
||||
var jid = null;
|
||||
if(container)
|
||||
{
|
||||
if(container.id == "localVideoWrapper")
|
||||
{
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = VideoLayout.getPeerContainerResourceJid(container);
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
VideoLayout.updateLargeVideo(src, volume, jid);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -687,7 +779,9 @@ var VideoLayout = (function (my) {
|
||||
container.id = 'mixedstream';
|
||||
container.className = 'videocontainer';
|
||||
remotes.appendChild(container);
|
||||
UIUtil.playSoundNotification('userJoined');
|
||||
if(!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
UIUtil.playSoundNotification('userJoined');
|
||||
}
|
||||
|
||||
if (container) {
|
||||
@@ -706,11 +800,10 @@ var VideoLayout = (function (my) {
|
||||
/**
|
||||
* Updates the large video with the given new video source.
|
||||
*/
|
||||
my.updateLargeVideo = function(newSrc, vol, resourceJid) {
|
||||
console.log('hover in', newSrc);
|
||||
|
||||
if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc) {
|
||||
my.updateLargeVideo = function(newSrc, vol, resourceJid, forceUpdate) {
|
||||
console.log('hover in', newSrc, resourceJid);
|
||||
|
||||
if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc || forceUpdate) {
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
// Due to the simulcast the localVideoSrc may have changed when the
|
||||
// fadeOut event triggers. In that case the getJidFromVideoSrc and
|
||||
@@ -722,7 +815,9 @@ var VideoLayout = (function (my) {
|
||||
|
||||
largeVideoState.newSrc = newSrc;
|
||||
largeVideoState.isVisible = $('#largeVideo').is(':visible');
|
||||
largeVideoState.isDesktop = APP.RTC.isVideoSrcDesktop(resourceJid);
|
||||
largeVideoState.isDesktop = APP.RTC.isVideoSrcDesktop(
|
||||
APP.xmpp.findJidFromResource(resourceJid));
|
||||
|
||||
if(largeVideoState.userResourceJid) {
|
||||
largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
|
||||
} else {
|
||||
@@ -746,7 +841,6 @@ var VideoLayout = (function (my) {
|
||||
largeVideoState.updateInProgress = true;
|
||||
|
||||
var doUpdate = function () {
|
||||
|
||||
Avatar.updateActiveSpeakerAvatarSrc(
|
||||
APP.xmpp.findJidFromResource(
|
||||
largeVideoState.userResourceJid));
|
||||
@@ -1109,7 +1203,7 @@ var VideoLayout = (function (my) {
|
||||
console.log('stream ended', this);
|
||||
|
||||
VideoLayout.removeRemoteStreamElement(
|
||||
stream, isVideo, container);
|
||||
stream, isVideo, container, newElementId);
|
||||
|
||||
// NOTE(gp) it seems that under certain circumstances, the
|
||||
// onended event is not fired and thus the contact list is not
|
||||
@@ -1179,14 +1273,14 @@ var VideoLayout = (function (my) {
|
||||
* @param isVideo <tt>true</tt> if given <tt>stream</tt> is a video one.
|
||||
* @param container
|
||||
*/
|
||||
my.removeRemoteStreamElement = function (stream, isVideo, container) {
|
||||
my.removeRemoteStreamElement = function (stream, isVideo, container, id) {
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
var select = null;
|
||||
var removedVideoSrc = null;
|
||||
if (isVideo) {
|
||||
select = $('#' + container.id + '>video');
|
||||
select = $('#' + id);
|
||||
removedVideoSrc = APP.RTC.getVideoSrc(select.get(0));
|
||||
}
|
||||
else
|
||||
@@ -1208,7 +1302,9 @@ var VideoLayout = (function (my) {
|
||||
// Remove whole container
|
||||
container.remove();
|
||||
|
||||
UIUtil.playSoundNotification('userLeft');
|
||||
if(!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
UIUtil.playSoundNotification('userLeft');
|
||||
VideoLayout.resizeThumbnails();
|
||||
}
|
||||
|
||||
@@ -1270,10 +1366,17 @@ var VideoLayout = (function (my) {
|
||||
|
||||
if (!$('#localDisplayName').is(":visible")) {
|
||||
if (NicknameHandler.getNickname())
|
||||
$('#localDisplayName').text(NicknameHandler.getNickname() + " (me)");
|
||||
{
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
$('#localDisplayName').html(NicknameHandler.getNickname() + " (" + meHTML + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
var defaultHTML = APP.translation.generateTranslatonHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
$('#localDisplayName')
|
||||
.text(interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
.html(defaultHTML);
|
||||
}
|
||||
$('#localDisplayName').show();
|
||||
}
|
||||
|
||||
@@ -1584,10 +1687,9 @@ var VideoLayout = (function (my) {
|
||||
if (videoSpan.classList.contains("dominantspeaker"))
|
||||
videoSpan.classList.remove("dominantspeaker");
|
||||
}
|
||||
|
||||
Avatar.showUserAvatar(
|
||||
APP.xmpp.findJidFromResource(resourceJid));
|
||||
}
|
||||
Avatar.showUserAvatar(
|
||||
APP.xmpp.findJidFromResource(resourceJid));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1752,7 +1854,7 @@ var VideoLayout = (function (my) {
|
||||
*/
|
||||
$(document).bind('videomuted.muc', function (event, jid, value) {
|
||||
var isMuted = (value === "true");
|
||||
if(!APP.RTC.muteRemoteVideoStream(jid, isMuted))
|
||||
if(jid !== APP.xmpp.myJid() && !APP.RTC.muteRemoteVideoStream(jid, isMuted))
|
||||
return;
|
||||
|
||||
Avatar.showUserAvatar(jid, isMuted);
|
||||
@@ -1796,17 +1898,20 @@ var VideoLayout = (function (my) {
|
||||
=== APP.xmpp.myResource())
|
||||
return;
|
||||
|
||||
var members = APP.xmpp.getMembers();
|
||||
// Update the current dominant speaker.
|
||||
if (resourceJid !== currentDominantSpeaker) {
|
||||
var oldSpeakerVideoSpanId = "participant_" + currentDominantSpeaker,
|
||||
newSpeakerVideoSpanId = "participant_" + resourceJid;
|
||||
if($("#" + oldSpeakerVideoSpanId + ">span.displayname").text() ===
|
||||
interfaceConfig.DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME) {
|
||||
var currentJID = APP.xmpp.findJidFromResource(currentDominantSpeaker);
|
||||
var newJID = APP.xmpp.findJidFromResource(resourceJid);
|
||||
if(currentDominantSpeaker && (!members || !members[currentJID] ||
|
||||
!members[currentJID].displayName)) {
|
||||
setDisplayName(oldSpeakerVideoSpanId, null);
|
||||
}
|
||||
if($("#" + newSpeakerVideoSpanId + ">span.displayname").text() ===
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME) {
|
||||
setDisplayName(newSpeakerVideoSpanId,
|
||||
if(resourceJid && (!members || !members[newJID] ||
|
||||
!members[newJID].displayName)) {
|
||||
setDisplayName(newSpeakerVideoSpanId, null,
|
||||
interfaceConfig.DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME);
|
||||
}
|
||||
currentDominantSpeaker = resourceJid;
|
||||
@@ -2195,6 +2300,21 @@ var VideoLayout = (function (my) {
|
||||
focusedVideoInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
my.onVideoTypeChanged = function (jid) {
|
||||
if(jid &&
|
||||
Strophe.getResourceFromJid(jid) === largeVideoState.userResourceJid)
|
||||
{
|
||||
largeVideoState.isDesktop = APP.RTC.isVideoSrcDesktop(jid);
|
||||
VideoLayout.getVideoSize = largeVideoState.isDesktop
|
||||
? getDesktopVideoSize
|
||||
: getCameraVideoSize;
|
||||
VideoLayout.getVideoPosition = largeVideoState.isDesktop
|
||||
? getDesktopVideoPosition
|
||||
: getCameraVideoPosition;
|
||||
VideoLayout.positionLarge(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
return my;
|
||||
}(VideoLayout || {}));
|
||||
|
||||
40
modules/URLProcessor/URLProcessor.js
Normal file
@@ -0,0 +1,40 @@
|
||||
var params = {};
|
||||
function getConfigParamsFromUrl() {
|
||||
if(!location.hash)
|
||||
return {};
|
||||
var hash = location.hash.substr(1);
|
||||
var result = {};
|
||||
hash.split("&").forEach(function(part) {
|
||||
var item = part.split("=");
|
||||
result[item[0]] = JSON.parse(
|
||||
decodeURIComponent(item[1]).replace(/\\&/, "&"));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
params = getConfigParamsFromUrl();
|
||||
|
||||
var URLProcessor = {
|
||||
setConfigParametersFromUrl: function () {
|
||||
for(var k in params)
|
||||
{
|
||||
if(typeof k !== "string" || k.indexOf("config.") === -1)
|
||||
continue;
|
||||
|
||||
var v = params[k];
|
||||
var confKey = k.substr(7);
|
||||
if(config[confKey] && typeof config[confKey] !== typeof v)
|
||||
{
|
||||
console.warn("The type of " + k +
|
||||
" is wrong. That parameter won't be updated in config.js.");
|
||||
continue;
|
||||
}
|
||||
|
||||
config[confKey] = v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = URLProcessor;
|
||||
@@ -1,11 +1,13 @@
|
||||
/* global $, alert, changeLocalVideo, chrome, config, getConferenceHandler, getUserMediaWithConstraints */
|
||||
/* global $, alert, APP, changeLocalVideo, chrome, config, getConferenceHandler,
|
||||
getUserMediaWithConstraints */
|
||||
/**
|
||||
* Indicates that desktop stream is currently in use(for toggle purpose).
|
||||
* @type {boolean}
|
||||
*/
|
||||
var isUsingScreenStream = false;
|
||||
/**
|
||||
* Indicates that switch stream operation is in progress and prevent from triggering new events.
|
||||
* Indicates that switch stream operation is in progress and prevent from
|
||||
* triggering new events.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var switchInProgress = false;
|
||||
@@ -18,7 +20,21 @@ var switchInProgress = false;
|
||||
var obtainDesktopStream = null;
|
||||
|
||||
/**
|
||||
* Flag used to cache desktop sharing enabled state. Do not use directly as it can be <tt>null</tt>.
|
||||
* Indicates whether desktop sharing extension is installed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var extInstalled = false;
|
||||
|
||||
/**
|
||||
* Indicates whether update of desktop sharing extension is required.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var extUpdateRequired = false;
|
||||
|
||||
/**
|
||||
* Flag used to cache desktop sharing enabled state. Do not use directly as
|
||||
* it can be <tt>null</tt>.
|
||||
*
|
||||
* @type {null|boolean}
|
||||
*/
|
||||
var _desktopSharingEnabled = null;
|
||||
@@ -27,7 +43,8 @@ var EventEmitter = require("events");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
|
||||
var DesktopSharingEventTypes = require("../../service/desktopsharing/DesktopSharingEventTypes");
|
||||
var DesktopSharingEventTypes
|
||||
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
||||
|
||||
/**
|
||||
* Method obtains desktop stream from WebRTC 'screen' source.
|
||||
@@ -48,7 +65,8 @@ function obtainWebRTCScreen(streamCallback, failCallback) {
|
||||
*/
|
||||
function getWebStoreInstallUrl()
|
||||
{
|
||||
return "https://chrome.google.com/webstore/detail/" + config.chromeExtensionId;
|
||||
return "https://chrome.google.com/webstore/detail/" +
|
||||
config.chromeExtensionId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,18 +110,16 @@ function isUpdateRequired(minVersion, extVersion)
|
||||
catch (e)
|
||||
{
|
||||
console.error("Failed to parse extension version", e);
|
||||
APP.UI.messageHandler.showError("dialog.error", 'Error',
|
||||
"dialod.detectext",
|
||||
'Error when trying to detect desktopsharing extension.');
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
"dialog.detectext");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkExtInstalled(isInstalledCallback) {
|
||||
function checkExtInstalled(callback) {
|
||||
if (!chrome.runtime) {
|
||||
// No API, so no extension for sure
|
||||
isInstalledCallback(false);
|
||||
callback(false, false);
|
||||
return;
|
||||
}
|
||||
chrome.runtime.sendMessage(
|
||||
@@ -112,26 +128,24 @@ function checkExtInstalled(isInstalledCallback) {
|
||||
function (response) {
|
||||
if (!response || !response.version) {
|
||||
// Communication failure - assume that no endpoint exists
|
||||
console.warn("Extension not installed?: " + chrome.runtime.lastError);
|
||||
isInstalledCallback(false);
|
||||
} else {
|
||||
// Check installed extension version
|
||||
var extVersion = response.version;
|
||||
console.log('Extension version is: ' + extVersion);
|
||||
var updateRequired = isUpdateRequired(config.minChromeExtVersion, extVersion);
|
||||
if (updateRequired) {
|
||||
alert(
|
||||
'Jitsi Desktop Streamer requires update. ' +
|
||||
'Changes will take effect after next Chrome restart.');
|
||||
}
|
||||
isInstalledCallback(!updateRequired);
|
||||
console.warn(
|
||||
"Extension not installed?: ", chrome.runtime.lastError);
|
||||
callback(false, false);
|
||||
return;
|
||||
}
|
||||
// Check installed extension version
|
||||
var extVersion = response.version;
|
||||
console.log('Extension version is: ' + extVersion);
|
||||
var updateRequired
|
||||
= isUpdateRequired(config.minChromeExtVersion, extVersion);
|
||||
callback(!updateRequired, updateRequired);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function doGetStreamFromExtension(streamCallback, failCallback) {
|
||||
// Sends 'getStream' msg to the extension. Extension id must be defined in the config.
|
||||
// Sends 'getStream' msg to the extension.
|
||||
// Extension id must be defined in the config.
|
||||
chrome.runtime.sendMessage(
|
||||
config.chromeExtensionId,
|
||||
{ getStream: true, sources: config.desktopSharingSources },
|
||||
@@ -157,39 +171,44 @@ function doGetStreamFromExtension(streamCallback, failCallback) {
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop' stream for returned stream token.
|
||||
* Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop'
|
||||
* stream for returned stream token.
|
||||
*/
|
||||
function obtainScreenFromExtension(streamCallback, failCallback) {
|
||||
checkExtInstalled(
|
||||
function (isInstalled) {
|
||||
if (isInstalled) {
|
||||
doGetStreamFromExtension(streamCallback, failCallback);
|
||||
} else {
|
||||
chrome.webstore.install(
|
||||
getWebStoreInstallUrl(),
|
||||
function (arg) {
|
||||
console.log("Extension installed successfully", arg);
|
||||
// We need to reload the page in order to get the access to chrome.runtime
|
||||
window.location.reload(false);
|
||||
},
|
||||
function (arg) {
|
||||
console.log("Failed to install the extension", arg);
|
||||
failCallback(arg);
|
||||
APP.UI.messageHandler.showError("dialog.error", 'Error',
|
||||
"dialog.failtoinstall",
|
||||
'Failed to install desktop sharing extension');
|
||||
}
|
||||
);
|
||||
}
|
||||
if (extInstalled) {
|
||||
doGetStreamFromExtension(streamCallback, failCallback);
|
||||
} else {
|
||||
if (extUpdateRequired) {
|
||||
alert(
|
||||
'Jitsi Desktop Streamer requires update. ' +
|
||||
'Changes will take effect after next Chrome restart.');
|
||||
}
|
||||
);
|
||||
|
||||
chrome.webstore.install(
|
||||
getWebStoreInstallUrl(),
|
||||
function (arg) {
|
||||
console.log("Extension installed successfully", arg);
|
||||
// We need to reload the page in order to get the access to
|
||||
// chrome.runtime
|
||||
window.location.reload(false);
|
||||
},
|
||||
function (arg) {
|
||||
console.log("Failed to install the extension", arg);
|
||||
failCallback(arg);
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
"dialog.failtoinstall");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to toggle desktop sharing feature.
|
||||
* @param method pass "ext" to use chrome extension for desktop capture(chrome extension required),
|
||||
* pass "webrtc" to use WebRTC "screen" desktop source('chrome://flags/#enable-usermedia-screen-capture'
|
||||
* must be enabled), pass any other string or nothing in order to disable this feature completely.
|
||||
* @param method pass "ext" to use chrome extension for desktop capture(chrome
|
||||
* extension required), pass "webrtc" to use WebRTC "screen" desktop
|
||||
* source('chrome://flags/#enable-usermedia-screen-capture' must be
|
||||
* enabled), pass any other string or nothing in order to disable this
|
||||
* feature completely.
|
||||
*/
|
||||
function setDesktopSharing(method) {
|
||||
// Check if we are running chrome
|
||||
@@ -209,15 +228,23 @@ function setDesktopSharing(method) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes <link rel=chrome-webstore-item /> with extension id set in config.js to support inline installs.
|
||||
* Host site must be selected as main website of published extension.
|
||||
* Initializes <link rel=chrome-webstore-item /> with extension id set in
|
||||
* config.js to support inline installs. Host site must be selected as main
|
||||
* website of published extension.
|
||||
*/
|
||||
function initInlineInstalls()
|
||||
{
|
||||
$("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
|
||||
}
|
||||
|
||||
function getSwitchStreamFailed(error) {
|
||||
function getVideoStreamFailed(error) {
|
||||
console.error("Failed to obtain the stream to switch to", error);
|
||||
switchInProgress = false;
|
||||
isUsingScreenStream = false;
|
||||
newStreamCreated(null);
|
||||
}
|
||||
|
||||
function getDesktopStreamFailed(error) {
|
||||
console.error("Failed to obtain the stream to switch to", error);
|
||||
switchInProgress = false;
|
||||
}
|
||||
@@ -242,19 +269,22 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
|
||||
* @returns {boolean} <tt>true</tt> if desktop sharing feature is available
|
||||
* and enabled.
|
||||
*/
|
||||
isDesktopSharingEnabled: function () {
|
||||
if (_desktopSharingEnabled === null) {
|
||||
if (obtainDesktopStream === obtainScreenFromExtension) {
|
||||
// Parse chrome version
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
// We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
|
||||
// We can assume that user agent is chrome, because it's
|
||||
// enforced when 'ext' streaming method is set
|
||||
var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
|
||||
console.log("Chrome version" + userAgent, ver);
|
||||
_desktopSharingEnabled = ver >= 34;
|
||||
} else {
|
||||
_desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
|
||||
_desktopSharingEnabled =
|
||||
obtainDesktopStream === obtainWebRTCScreen;
|
||||
}
|
||||
}
|
||||
return _desktopSharingEnabled;
|
||||
@@ -265,18 +295,28 @@ module.exports = {
|
||||
|
||||
// Initialize Chrome extension inline installs
|
||||
if (config.chromeExtensionId) {
|
||||
|
||||
initInlineInstalls();
|
||||
|
||||
// Check if extension is installed
|
||||
checkExtInstalled(function (installed, updateRequired) {
|
||||
extInstalled = installed;
|
||||
extUpdateRequired = updateRequired;
|
||||
console.info(
|
||||
"Chrome extension installed: " + extInstalled +
|
||||
" updateRequired: " + extUpdateRequired);
|
||||
});
|
||||
}
|
||||
|
||||
eventEmitter.emit(DesktopSharingEventTypes.INIT);
|
||||
},
|
||||
|
||||
addListener: function(listener, type)
|
||||
addListener: function (listener, type)
|
||||
{
|
||||
eventEmitter.on(type, listener);
|
||||
},
|
||||
|
||||
removeListener: function (listener,type) {
|
||||
removeListener: function (listener, type) {
|
||||
eventEmitter.removeListener(type, listener);
|
||||
},
|
||||
|
||||
@@ -297,17 +337,18 @@ module.exports = {
|
||||
function (stream) {
|
||||
// We now use screen stream
|
||||
isUsingScreenStream = true;
|
||||
// Hook 'ended' event to restore camera when screen stream stops
|
||||
// Hook 'ended' event to restore camera
|
||||
// when screen stream stops
|
||||
stream.addEventListener('ended',
|
||||
function (e) {
|
||||
if (!switchInProgress && isUsingScreenStream) {
|
||||
toggleScreenSharing();
|
||||
APP.desktopsharing.toggleScreenSharing();
|
||||
}
|
||||
}
|
||||
);
|
||||
newStreamCreated(stream);
|
||||
},
|
||||
getSwitchStreamFailed);
|
||||
getDesktopStreamFailed);
|
||||
} else {
|
||||
// Disable screen stream
|
||||
APP.RTC.getUserMediaWithConstraints(
|
||||
@@ -317,7 +358,7 @@ module.exports = {
|
||||
isUsingScreenStream = false;
|
||||
newStreamCreated(stream);
|
||||
},
|
||||
getSwitchStreamFailed, config.resolution || '360'
|
||||
getVideoStreamFailed, config.resolution || '360'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
129
modules/members/MemberList.js
Normal file
@@ -0,0 +1,129 @@
|
||||
/* global APP */
|
||||
|
||||
/**
|
||||
* This module is meant to (eventually) contain and manage all information
|
||||
* about members/participants of the conference, so that other modules don't
|
||||
* have to do it on their own, and so that other modules can access members'
|
||||
* information from a single place.
|
||||
*
|
||||
* Currently this module only manages information about the support of jingle
|
||||
* DTMF of the members. Other fields, as well as accessor methods are meant to
|
||||
* be added as needed.
|
||||
*/
|
||||
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var Events = require("../../service/members/Events");
|
||||
var EventEmitter = require("events");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
|
||||
/**
|
||||
* The actual container.
|
||||
*/
|
||||
var members = {};
|
||||
|
||||
/**
|
||||
* There is at least one member that supports DTMF (i.e. is jigasi).
|
||||
*/
|
||||
var atLeastOneDtmf = false;
|
||||
|
||||
|
||||
function registerListeners() {
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, onMucMemberJoined);
|
||||
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a new member joining the MUC.
|
||||
*/
|
||||
function onMucMemberJoined(jid, id, displayName) {
|
||||
var member = {
|
||||
displayName: displayName
|
||||
};
|
||||
|
||||
APP.xmpp.getConnection().disco.info(
|
||||
jid, "" /* node */, function(iq) { onDiscoInfoReceived(jid, iq); });
|
||||
|
||||
members[jid] = member;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a member leaving the MUC.
|
||||
*/
|
||||
function onMucMemberLeft(jid) {
|
||||
delete members[jid];
|
||||
updateAtLeastOneDtmf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reception of a disco#info packet from a particular JID.
|
||||
* @param jid the JID sending the packet.
|
||||
* @param iq the packet.
|
||||
*/
|
||||
function onDiscoInfoReceived(jid, iq) {
|
||||
if (!members[jid])
|
||||
return;
|
||||
|
||||
var supportsDtmf
|
||||
= $(iq).find('>query>feature[var="urn:xmpp:jingle:dtmf:0"]').length > 0;
|
||||
updateDtmf(jid, supportsDtmf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the 'supportsDtmf' field for a member.
|
||||
* @param jid the jid of the member.
|
||||
* @param newValue the new value for the 'supportsDtmf' field.
|
||||
*/
|
||||
function updateDtmf(jid, newValue) {
|
||||
var oldValue = members[jid].supportsDtmf;
|
||||
members[jid].supportsDtmf = newValue;
|
||||
|
||||
if (newValue != oldValue) {
|
||||
updateAtLeastOneDtmf();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks each member's 'supportsDtmf' field and updates
|
||||
* 'atLastOneSupportsDtmf'.
|
||||
*/
|
||||
function updateAtLeastOneDtmf(){
|
||||
var newAtLeastOneDtmf = false;
|
||||
for (var key in members) {
|
||||
if (typeof members[key].supportsDtmf !== 'undefined'
|
||||
&& members[key].supportsDtmf) {
|
||||
newAtLeastOneDtmf= true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (atLeastOneDtmf != newAtLeastOneDtmf) {
|
||||
atLeastOneDtmf = newAtLeastOneDtmf;
|
||||
eventEmitter.emit(Events.DTMF_SUPPORT_CHANGED, atLeastOneDtmf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exported interface.
|
||||
*/
|
||||
var Members = {
|
||||
start: function(){
|
||||
registerListeners();
|
||||
},
|
||||
addListener: function(type, listener)
|
||||
{
|
||||
eventEmitter.on(type, listener);
|
||||
},
|
||||
removeListener: function (type, listener) {
|
||||
eventEmitter.removeListener(type, listener);
|
||||
},
|
||||
size: function () {
|
||||
return Object.keys(members).length;
|
||||
},
|
||||
getMembers: function () {
|
||||
return members;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Members;
|
||||
@@ -1,6 +1,7 @@
|
||||
var email = '';
|
||||
var displayName = '';
|
||||
var userId;
|
||||
var language = null;
|
||||
|
||||
|
||||
function supportsLocalStorage() {
|
||||
@@ -28,6 +29,7 @@ if (supportsLocalStorage()) {
|
||||
userId = window.localStorage.jitsiMeetId || '';
|
||||
email = window.localStorage.email || '';
|
||||
displayName = window.localStorage.displayname || '';
|
||||
language = window.localStorage.language;
|
||||
} else {
|
||||
console.log("local storage is not supported");
|
||||
userId = generateUniqueId();
|
||||
@@ -50,8 +52,13 @@ var Settings =
|
||||
return {
|
||||
email: email,
|
||||
displayName: displayName,
|
||||
uid: userId
|
||||
uid: userId,
|
||||
language: language
|
||||
};
|
||||
},
|
||||
setLanguage: function (lang) {
|
||||
language = lang;
|
||||
window.localStorage.language = lang;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, even
|
||||
/**
|
||||
* Gather PeerConnection stats once every this many milliseconds.
|
||||
*/
|
||||
this.GATHER_INTERVAL = 10000;
|
||||
this.GATHER_INTERVAL = 15000;
|
||||
|
||||
/**
|
||||
* Log stats via the focus once every this many milliseconds.
|
||||
@@ -234,7 +234,6 @@ StatsCollector.prototype.start = function ()
|
||||
{
|
||||
var self = this;
|
||||
if(!config.disableAudioLevels) {
|
||||
console.debug("set audio levels interval");
|
||||
this.audioLevelsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -261,8 +260,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if(!config.disableStats) {
|
||||
console.debug("set stats interval");
|
||||
if(!config.disableStats && !navigator.mozGetUserMedia) {
|
||||
this.statsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -296,7 +294,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logStats) {
|
||||
if (config.logStats && !navigator.mozGetUserMedia) {
|
||||
this.gatherStatsIntervalId = setInterval(
|
||||
function () {
|
||||
self.peerconnection.getStats(
|
||||
@@ -316,6 +314,38 @@ StatsCollector.prototype.start = function ()
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether a certain record should be included in the logged statistics.
|
||||
*/
|
||||
function acceptStat(reportId, reportType, statName) {
|
||||
if (reportType == "googCandidatePair" && statName == "googChannelId")
|
||||
return false;
|
||||
|
||||
if (reportType == "ssrc") {
|
||||
if (statName == "googTrackId" ||
|
||||
statName == "transportId" ||
|
||||
statName == "ssrc")
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a certain record should be included in the logged statistics.
|
||||
*/
|
||||
function acceptReport(id, type) {
|
||||
if (id.substring(0, 15) == "googCertificate" ||
|
||||
id.substring(0, 9) == "googTrack" ||
|
||||
id.substring(0, 20) == "googLibjingleSession")
|
||||
return false;
|
||||
|
||||
if (type == "googComponent")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the stats to the format used for logging, and saves the data in
|
||||
* this.statsToBeLogged.
|
||||
@@ -326,12 +356,16 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
|
||||
var num_records = this.statsToBeLogged.timestamps.length;
|
||||
this.statsToBeLogged.timestamps.push(new Date().getTime());
|
||||
reports.map(function (report) {
|
||||
if (!acceptReport(report.id, report.type))
|
||||
return;
|
||||
var stat = self.statsToBeLogged.stats[report.id];
|
||||
if (!stat) {
|
||||
stat = self.statsToBeLogged.stats[report.id] = {};
|
||||
}
|
||||
stat.type = report.type;
|
||||
report.names().map(function (name) {
|
||||
if (!acceptStat(report.id, report.type, name))
|
||||
return;
|
||||
var values = stat[name];
|
||||
if (!values) {
|
||||
values = stat[name] = [];
|
||||
@@ -662,9 +696,10 @@ StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
|
||||
var ssrc = getStatValue(now, 'ssrc');
|
||||
var jid = APP.xmpp.getJidFromSSRC(ssrc);
|
||||
if (!jid && (Date.now() - now.timestamp) < 3000)
|
||||
if (!jid)
|
||||
{
|
||||
console.warn("No jid for ssrc: " + ssrc);
|
||||
if((Date.now() - now.timestamp) < 3000)
|
||||
console.warn("No jid for ssrc: " + ssrc);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -693,18 +728,13 @@ StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
{
|
||||
// TODO: can't find specs about what this value really is,
|
||||
// but it seems to vary between 0 and around 32k.
|
||||
audioLevel = formatAudioLevel(audioLevel / 32767);
|
||||
var oldLevel = jidStats.ssrc2AudioLevel[ssrc];
|
||||
if(jid != APP.xmpp.myJid() && (!oldLevel || oldLevel != audioLevel))
|
||||
{
|
||||
|
||||
jidStats.ssrc2AudioLevel[ssrc] = audioLevel;
|
||||
|
||||
audioLevel = audioLevel / 32767;
|
||||
jidStats.setSsrcAudioLevel(ssrc, audioLevel);
|
||||
if(jid != APP.xmpp.myJid())
|
||||
this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
97
modules/transform/transform.js
Normal file
@@ -0,0 +1,97 @@
|
||||
var transform = require('sdp-transform');
|
||||
|
||||
exports.write = function(session, opts) {
|
||||
|
||||
if (typeof session !== 'undefined' &&
|
||||
typeof session.media !== 'undefined' &&
|
||||
Array.isArray(session.media)) {
|
||||
|
||||
session.media.forEach(function (mLine) {
|
||||
// expand sources to ssrcs
|
||||
if (typeof mLine.sources !== 'undefined' &&
|
||||
Object.keys(mLine.sources).length !== 0) {
|
||||
mLine.ssrcs = [];
|
||||
Object.keys(mLine.sources).forEach(function (ssrc) {
|
||||
var source = mLine.sources[ssrc];
|
||||
Object.keys(source).forEach(function (attribute) {
|
||||
mLine.ssrcs.push({
|
||||
id: ssrc,
|
||||
attribute: attribute,
|
||||
value: source[attribute]
|
||||
});
|
||||
});
|
||||
});
|
||||
delete mLine.sources;
|
||||
}
|
||||
|
||||
// join ssrcs in ssrc groups
|
||||
if (typeof mLine.ssrcGroups !== 'undefined' &&
|
||||
Array.isArray(mLine.ssrcGroups)) {
|
||||
mLine.ssrcGroups.forEach(function (ssrcGroup) {
|
||||
if (typeof ssrcGroup.ssrcs !== 'undefined' &&
|
||||
Array.isArray(ssrcGroup.ssrcs)) {
|
||||
ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// join group mids
|
||||
if (typeof session !== 'undefined' &&
|
||||
typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
|
||||
|
||||
session.groups.forEach(function (g) {
|
||||
if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
|
||||
g.mids = g.mids.join(' ');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return transform.write(session, opts);
|
||||
};
|
||||
|
||||
exports.parse = function(sdp) {
|
||||
var session = transform.parse(sdp);
|
||||
|
||||
if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
|
||||
Array.isArray(session.media)) {
|
||||
|
||||
session.media.forEach(function (mLine) {
|
||||
// group sources attributes by ssrc
|
||||
if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
|
||||
mLine.sources = {};
|
||||
mLine.ssrcs.forEach(function (ssrc) {
|
||||
if (!mLine.sources[ssrc.id])
|
||||
mLine.sources[ssrc.id] = {};
|
||||
mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
|
||||
});
|
||||
|
||||
delete mLine.ssrcs;
|
||||
}
|
||||
|
||||
// split ssrcs in ssrc groups
|
||||
if (typeof mLine.ssrcGroups !== 'undefined' &&
|
||||
Array.isArray(mLine.ssrcGroups)) {
|
||||
mLine.ssrcGroups.forEach(function (ssrcGroup) {
|
||||
if (typeof ssrcGroup.ssrcs === 'string') {
|
||||
ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// split group mids
|
||||
if (typeof session !== 'undefined' &&
|
||||
typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
|
||||
|
||||
session.groups.forEach(function (g) {
|
||||
if (typeof g.mids === 'string') {
|
||||
g.mids = g.mids.split(' ');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
var i18n = require("i18next-client");
|
||||
var languages = require("../../service/translation/languages");
|
||||
var Settings = require("../settings/Settings");
|
||||
var DEFAULT_LANG = languages.EN;
|
||||
var initialized = false;
|
||||
var waitingForInit = [];
|
||||
|
||||
i18n.addPostProcessor("resolveAppName", function(value, key, options) {
|
||||
return value.replace("__app__", interfaceConfig.APP_NAME);
|
||||
@@ -14,7 +13,6 @@ var defaultOptions = {
|
||||
detectLngQS: "lang",
|
||||
useCookie: false,
|
||||
fallbackLng: DEFAULT_LANG,
|
||||
shortcutFunction: 'defaultValue',
|
||||
load: "unspecific",
|
||||
resGetPath: 'lang/__ns__-__lng__.json',
|
||||
ns: {
|
||||
@@ -23,15 +21,17 @@ var defaultOptions = {
|
||||
},
|
||||
lngWhitelist : languages.getLanguages(),
|
||||
fallbackOnNull: true,
|
||||
fallbackOnEmpty: true,
|
||||
useDataAttrOptions: true,
|
||||
defaultValueFromContent: false,
|
||||
app: interfaceConfig.APP_NAME,
|
||||
getAsync: true,
|
||||
getAsync: false,
|
||||
defaultValueFromContent: false,
|
||||
customLoad: function(lng, ns, options, done) {
|
||||
var resPath = "lang/__ns__-__lng__.json";
|
||||
if(lng === languages.EN)
|
||||
resPath = "lang/__ns__.json";
|
||||
var url = i18n.functions.applyReplacement(resPath, { lng: lng, ns: ns });
|
||||
initialized = false;
|
||||
i18n.functions.ajax({
|
||||
url: url,
|
||||
success: function(data, status, xhr) {
|
||||
@@ -65,39 +65,48 @@ var defaultOptions = {
|
||||
|
||||
function initCompleted(t)
|
||||
{
|
||||
initialized = true;
|
||||
$("[data-i18n]").i18n();
|
||||
for(var i = 0; i < waitingForInit.length; i++)
|
||||
{
|
||||
var obj = waitingForInit[i];
|
||||
obj.callback(i18n.t(obj.key));
|
||||
}
|
||||
|
||||
function checkForParameter() {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == "lang")
|
||||
{
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
waitingForInit = [];
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function (lang) {
|
||||
initialized = false;
|
||||
var options = defaultOptions;
|
||||
if(lang)
|
||||
|
||||
|
||||
if(!lang)
|
||||
{
|
||||
lang = checkForParameter();
|
||||
if(!lang)
|
||||
{
|
||||
var settings = Settings.getSettings();
|
||||
if(settings)
|
||||
lang = settings.language;
|
||||
}
|
||||
}
|
||||
|
||||
if(lang) {
|
||||
options.lng = lang;
|
||||
}
|
||||
|
||||
i18n.init(options, initCompleted);
|
||||
},
|
||||
translateString: function (key, cb, options) {
|
||||
if(!cb)
|
||||
return i18n.t(key, options);
|
||||
|
||||
if(initialized)
|
||||
{
|
||||
cb(i18n.t(key, options));
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingForInit.push({"callback": cb, "key": key});
|
||||
}
|
||||
translateString: function (key, options) {
|
||||
return i18n.t(key, options);
|
||||
},
|
||||
setLanguage: function (lang) {
|
||||
initialized = false;
|
||||
if(!lang)
|
||||
lang = DEFAULT_LANG;
|
||||
i18n.setLng(lang, defaultOptions, initCompleted);
|
||||
@@ -108,20 +117,14 @@ module.exports = {
|
||||
translateElement: function (selector) {
|
||||
selector.i18n();
|
||||
},
|
||||
generateTranslatonHTML: function (key, defaultString, options) {
|
||||
generateTranslatonHTML: function (key, options) {
|
||||
var str = "<span data-i18n=\"" + key + "\"";
|
||||
if(options)
|
||||
{
|
||||
str += " data-i18n-options=\"" + JSON.stringify(options) + "\"";
|
||||
}
|
||||
str += ">";
|
||||
if(!options)
|
||||
options = {};
|
||||
if(defaultString)
|
||||
{
|
||||
options.defaultValue = defaultString;
|
||||
}
|
||||
str += this.translateString(key, null, options);
|
||||
str += this.translateString(key, options);
|
||||
str += "</span>";
|
||||
return str;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ var SDPDiffer = require("./SDPDiffer");
|
||||
var SDPUtil = require("./SDPUtil");
|
||||
var SDP = require("./SDP");
|
||||
var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
|
||||
var async = require("async");
|
||||
|
||||
// Jingle stuff
|
||||
function JingleSession(me, sid, connection, service) {
|
||||
@@ -52,10 +53,21 @@ function JingleSession(me, sid, connection, service) {
|
||||
* by the application logic.
|
||||
*/
|
||||
this.videoMuteByUser = false;
|
||||
this.modifySourcesQueue = async.queue(this._modifySources.bind(this), 1);
|
||||
// We start with the queue paused. We resume it when the signaling state is
|
||||
// stable and the ice connection state is connected.
|
||||
this.modifySourcesQueue.pause();
|
||||
}
|
||||
|
||||
//TODO: this array must be removed when firefox implement multistream support
|
||||
JingleSession.notReceivedSSRCs = [];
|
||||
JingleSession.prototype.updateModifySourcesQueue = function() {
|
||||
var signalingState = this.peerconnection.signalingState;
|
||||
var iceConnectionState = this.peerconnection.iceConnectionState;
|
||||
if (signalingState === 'stable' && iceConnectionState === 'connected') {
|
||||
this.modifySourcesQueue.resume();
|
||||
} else {
|
||||
this.modifySourcesQueue.pause();
|
||||
}
|
||||
};
|
||||
|
||||
JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
var self = this;
|
||||
@@ -82,8 +94,16 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
self.sendIceCandidate(event.candidate);
|
||||
};
|
||||
this.peerconnection.onaddstream = function (event) {
|
||||
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
|
||||
self.remoteStreamAdded(event);
|
||||
if (event.stream.id !== 'default') {
|
||||
console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
|
||||
self.remoteStreamAdded(event);
|
||||
} else {
|
||||
// This is a recvonly stream. Clients that implement Unified Plan,
|
||||
// such as Firefox use recvonly "streams/channels/tracks" for
|
||||
// receiving remote stream/tracks, as opposed to Plan B where there
|
||||
// are only 3 channels: audio, video and data.
|
||||
console.log("RECVONLY REMOTE STREAM IGNORED: " + event.stream + " - " + event.stream.id);
|
||||
}
|
||||
};
|
||||
this.peerconnection.onremovestream = function (event) {
|
||||
// Remove the stream from remoteStreams
|
||||
@@ -92,9 +112,11 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
|
||||
};
|
||||
this.peerconnection.onsignalingstatechange = function (event) {
|
||||
if (!(self && self.peerconnection)) return;
|
||||
self.updateModifySourcesQueue();
|
||||
};
|
||||
this.peerconnection.oniceconnectionstatechange = function (event) {
|
||||
if (!(self && self.peerconnection)) return;
|
||||
self.updateModifySourcesQueue();
|
||||
switch (self.peerconnection.iceConnectionState) {
|
||||
case 'connected':
|
||||
this.startTime = new Date();
|
||||
@@ -736,7 +758,7 @@ JingleSession.prototype.addSource = function (elem, fromJid) {
|
||||
$(elem).each(function (idx, content) {
|
||||
var name = $(content).attr('name');
|
||||
var lines = '';
|
||||
tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
||||
$(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
||||
var semantics = this.getAttribute('semantics');
|
||||
var ssrcs = $(this).find('>source').map(function () {
|
||||
return this.getAttribute('ssrc');
|
||||
@@ -746,7 +768,7 @@ JingleSession.prototype.addSource = function (elem, fromJid) {
|
||||
lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
|
||||
}
|
||||
});
|
||||
tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
|
||||
var tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
|
||||
tmp.each(function () {
|
||||
var ssrc = $(this).attr('ssrc');
|
||||
if(mySdp.containsSSRC(ssrc)){
|
||||
@@ -774,7 +796,17 @@ JingleSession.prototype.addSource = function (elem, fromJid) {
|
||||
});
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
});
|
||||
this.modifySources();
|
||||
|
||||
this.modifySourcesQueue.push(function() {
|
||||
// When a source is added and if this is FF, a new channel is allocated
|
||||
// for receiving the added source. We need to diffuse the SSRC of this
|
||||
// new recvonly channel to the rest of the peers.
|
||||
console.log('modify sources done');
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", mySdp, newSdp);
|
||||
self.notifyMySSRCUpdate(mySdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
@@ -801,7 +833,7 @@ JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
$(elem).each(function (idx, content) {
|
||||
var name = $(content).attr('name');
|
||||
var lines = '';
|
||||
tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
||||
$(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
|
||||
var semantics = this.getAttribute('semantics');
|
||||
var ssrcs = $(this).find('>source').map(function () {
|
||||
return this.getAttribute('ssrc');
|
||||
@@ -811,7 +843,7 @@ JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
|
||||
}
|
||||
});
|
||||
tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
|
||||
var tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
|
||||
tmp.each(function () {
|
||||
var ssrc = $(this).attr('ssrc');
|
||||
// This should never happen, but can be useful for bug detection
|
||||
@@ -835,11 +867,22 @@ JingleSession.prototype.removeSource = function (elem, fromJid) {
|
||||
});
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
});
|
||||
this.modifySources();
|
||||
|
||||
this.modifySourcesQueue.push(function() {
|
||||
// When a source is removed and if this is FF, the recvonly channel that
|
||||
// receives the remote stream is deactivated . We need to diffuse the
|
||||
// recvonly SSRC removal to the rest of the peers.
|
||||
console.log('modify sources done');
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", mySdp, newSdp);
|
||||
self.notifyMySSRCUpdate(mySdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.modifySources = function (successCallback) {
|
||||
JingleSession.prototype._modifySources = function (successCallback, queueCallback) {
|
||||
var self = this;
|
||||
|
||||
if (this.peerconnection.signalingState == 'closed') return;
|
||||
if (!(this.addssrc.length || this.removessrc.length || this.pendingop !== null || this.switchstreams)){
|
||||
// There is nothing to do since scheduled job might have been executed by another succeeding call
|
||||
@@ -847,21 +890,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
if(successCallback){
|
||||
successCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: this is a big hack
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2688
|
||||
// ^ has been fixed.
|
||||
if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) {
|
||||
console.warn('modifySources not yet', this.peerconnection.signalingState, this.peerconnection.iceConnectionState);
|
||||
this.wait = true;
|
||||
window.setTimeout(function() { self.modifySources(successCallback); }, 250);
|
||||
return;
|
||||
}
|
||||
if (this.wait) {
|
||||
window.setTimeout(function() { self.modifySources(successCallback); }, 2500);
|
||||
this.wait = false;
|
||||
queueCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -901,6 +930,7 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
|
||||
if(self.signalingState == 'closed') {
|
||||
console.error("createAnswer attempt on closed state");
|
||||
queueCallback("createAnswer attempt on closed state");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -937,30 +967,35 @@ JingleSession.prototype.modifySources = function (successCallback) {
|
||||
if(successCallback){
|
||||
successCallback();
|
||||
}
|
||||
queueCallback();
|
||||
},
|
||||
function(error) {
|
||||
console.error('modified setLocalDescription failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(error) {
|
||||
console.error('modified answer failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(error) {
|
||||
console.error('modify failed', error);
|
||||
queueCallback(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Switches video streams.
|
||||
* @param new_stream new stream that will be used as video of this session.
|
||||
* @param oldStream old video stream of this session.
|
||||
* @param success_callback callback executed after successful stream switch.
|
||||
*/
|
||||
JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
|
||||
JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback, isAudio) {
|
||||
|
||||
var self = this;
|
||||
|
||||
@@ -971,10 +1006,12 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
|
||||
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
}
|
||||
self.peerconnection.removeStream(oldStream, true);
|
||||
self.peerconnection.addStream(new_stream);
|
||||
if(new_stream)
|
||||
self.peerconnection.addStream(new_stream);
|
||||
}
|
||||
|
||||
APP.RTC.switchVideoStreams(new_stream, oldStream);
|
||||
if(!isAudio)
|
||||
APP.RTC.switchVideoStreams(new_stream, oldStream);
|
||||
|
||||
// Conference is not active
|
||||
if(!oldSdp || !self.peerconnection) {
|
||||
@@ -983,7 +1020,7 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
|
||||
}
|
||||
|
||||
self.switchstreams = true;
|
||||
self.modifySources(function() {
|
||||
self.modifySourcesQueue.push(function() {
|
||||
console.log('modify sources done');
|
||||
|
||||
success_callback();
|
||||
@@ -1056,26 +1093,6 @@ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the (local) video is mute i.e. all video tracks are
|
||||
* disabled.
|
||||
*
|
||||
* @return <tt>true</tt> if the (local) video is mute i.e. all video tracks are
|
||||
* disabled; otherwise, <tt>false</tt>
|
||||
*/
|
||||
JingleSession.prototype.isVideoMute = function () {
|
||||
var tracks = APP.RTC.localVideo.getVideoTracks();
|
||||
var mute = true;
|
||||
|
||||
for (var i = 0; i < tracks.length; ++i) {
|
||||
if (tracks[i].enabled) {
|
||||
mute = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mute;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutes/unmutes the (local) video i.e. enables/disables all video tracks.
|
||||
*
|
||||
@@ -1109,36 +1126,25 @@ JingleSession.prototype.setVideoMute = function (mute, callback, options) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hardMuteVideo(mute);
|
||||
|
||||
var self = this;
|
||||
var localCallback = function (mute) {
|
||||
self.connection.emuc.addVideoInfoToPresence(mute);
|
||||
self.connection.emuc.sendPresence();
|
||||
return callback(mute)
|
||||
};
|
||||
|
||||
if (mute == APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
// Even if no change occurs, the specified callback is to be executed.
|
||||
// The specified callback may, optionally, return a successCallback
|
||||
// which is to be executed as well.
|
||||
var successCallback = localCallback(mute);
|
||||
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
var oldSdp = null;
|
||||
if(self.peerconnection) {
|
||||
if(self.peerconnection.localDescription) {
|
||||
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
}
|
||||
} else {
|
||||
APP.RTC.localVideo.setMute(!mute);
|
||||
|
||||
this.hardMuteVideo(mute);
|
||||
|
||||
this.modifySources(localCallback(mute));
|
||||
}
|
||||
};
|
||||
|
||||
// SDP-based mute by going recvonly/sendrecv
|
||||
// FIXME: should probably black out the screen as well
|
||||
JingleSession.prototype.toggleVideoMute = function (callback) {
|
||||
this.service.setVideoMute(APP.RTC.localVideo.isMuted(), callback);
|
||||
this.modifySourcesQueue.push(function() {
|
||||
console.log('modify sources done');
|
||||
|
||||
callback(mute);
|
||||
|
||||
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
console.log("SDPs", oldSdp, newSdp);
|
||||
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
||||
});
|
||||
};
|
||||
|
||||
JingleSession.prototype.hardMuteVideo = function (muted) {
|
||||
@@ -1226,9 +1232,8 @@ JingleSession.onJingleFatalError = function (session, error)
|
||||
{
|
||||
this.service.sessionTerminated = true;
|
||||
this.connection.emuc.doLeave();
|
||||
APP.UI.messageHandler.showError("dialog.sorry", "Sorry",
|
||||
"dialog.internalError",
|
||||
"Internal application error[setRemoteDescription]");
|
||||
APP.UI.messageHandler.showError("dialog.sorry",
|
||||
"dialog.internalError");
|
||||
}
|
||||
|
||||
JingleSession.prototype.setLocalDescription = function () {
|
||||
@@ -1311,7 +1316,7 @@ function sendKeyframe(pc) {
|
||||
}
|
||||
|
||||
|
||||
JingleSession.prototype.remoteStreamAdded = function (data) {
|
||||
JingleSession.prototype.remoteStreamAdded = function (data, times) {
|
||||
var self = this;
|
||||
var thessrc;
|
||||
var ssrc2jid = this.connection.emuc.ssrc2jid;
|
||||
@@ -1340,12 +1345,24 @@ JingleSession.prototype.remoteStreamAdded = function (data) {
|
||||
// presence to arrive.
|
||||
|
||||
if (!ssrc2jid[thessrc]) {
|
||||
// TODO(gp) limit wait duration to 1 sec.
|
||||
setTimeout(function(d) {
|
||||
return function() {
|
||||
self.remoteStreamAdded(d);
|
||||
}
|
||||
}(data), 250);
|
||||
|
||||
if (typeof times === 'undefined')
|
||||
{
|
||||
times = 0;
|
||||
}
|
||||
|
||||
if (times > 10)
|
||||
{
|
||||
console.warning('Waiting for jid timed out', thessrc);
|
||||
}
|
||||
else
|
||||
{
|
||||
setTimeout(function(d) {
|
||||
return function() {
|
||||
self.remoteStreamAdded(d, times++);
|
||||
}
|
||||
}(data), 250);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1357,27 +1374,6 @@ JingleSession.prototype.remoteStreamAdded = function (data) {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: this code should be removed when firefox implement multistream support
|
||||
if(APP.RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
|
||||
{
|
||||
if((JingleSession.notReceivedSSRCs.length == 0) ||
|
||||
!ssrc2jid[JingleSession.notReceivedSSRCs[JingleSession.notReceivedSSRCs.length - 1]])
|
||||
{
|
||||
// TODO(gp) limit wait duration to 1 sec.
|
||||
setTimeout(function(d) {
|
||||
return function() {
|
||||
self.remoteStreamAdded(d);
|
||||
}
|
||||
}(data), 250);
|
||||
return;
|
||||
}
|
||||
|
||||
thessrc = JingleSession.notReceivedSSRCs.pop();
|
||||
if (ssrc2jid[thessrc]) {
|
||||
data.peerjid = ssrc2jid[thessrc];
|
||||
}
|
||||
}
|
||||
|
||||
APP.RTC.createRemoteStream(data, this.sid, thessrc);
|
||||
|
||||
var isVideo = data.stream.getVideoTracks().length > 0;
|
||||
|
||||
@@ -6,6 +6,8 @@ function TraceablePeerConnection(ice_config, constraints) {
|
||||
this.stats = {};
|
||||
this.statsinterval = null;
|
||||
this.maxstats = 0; // limit to 300 values, i.e. 5 minutes; set to 0 to disable
|
||||
var Interop = require('sdp-interop').Interop;
|
||||
this.interop = new Interop();
|
||||
|
||||
// override as desired
|
||||
this.trace = function (what, info) {
|
||||
@@ -98,19 +100,39 @@ function TraceablePeerConnection(ice_config, constraints) {
|
||||
};
|
||||
|
||||
dumpSDP = function(description) {
|
||||
if (typeof description === 'undefined' || description == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'type: ' + description.type + '\r\n' + description.sdp;
|
||||
}
|
||||
};
|
||||
|
||||
if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
|
||||
TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
|
||||
var publicLocalDescription = APP.simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
|
||||
return publicLocalDescription;
|
||||
this.trace('getLocalDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.localDescription));
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
var desc = this.peerconnection.localDescription;
|
||||
if (navigator.mozGetUserMedia) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
} else {
|
||||
desc = APP.simulcast.reverseTransformLocalDescription(this.peerconnection.localDescription);
|
||||
}
|
||||
this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
return desc;
|
||||
});
|
||||
TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
|
||||
var publicRemoteDescription = APP.simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
|
||||
return publicRemoteDescription;
|
||||
this.trace('getRemoteDescription::preTransform (Plan A)', dumpSDP(this.peerconnection.remoteDescription));
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
var desc = this.peerconnection.remoteDescription;
|
||||
if (navigator.mozGetUserMedia) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
} else {
|
||||
desc = APP.simulcast.reverseTransformRemoteDescription(this.peerconnection.remoteDescription);
|
||||
}
|
||||
this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
return desc;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -139,7 +161,13 @@ TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams)
|
||||
track.stop();
|
||||
});
|
||||
}
|
||||
this.peerconnection.removeStream(stream);
|
||||
|
||||
try {
|
||||
// FF doesn't support this yet.
|
||||
this.peerconnection.removeStream(stream);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
|
||||
@@ -148,9 +176,15 @@ TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
|
||||
this.trace('setLocalDescription::preTransform (Plan B)', dumpSDP(description));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
} else {
|
||||
description = APP.simulcast.transformLocalDescription(description);
|
||||
}
|
||||
this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
|
||||
var self = this;
|
||||
description = APP.simulcast.transformLocalDescription(description);
|
||||
this.trace('setLocalDescription', dumpSDP(description));
|
||||
this.peerconnection.setLocalDescription(description,
|
||||
function () {
|
||||
self.trace('setLocalDescriptionOnSuccess');
|
||||
@@ -169,9 +203,16 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
|
||||
this.trace('setRemoteDescription::preTransform (Plan B)', dumpSDP(description));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
}
|
||||
else {
|
||||
description = APP.simulcast.transformRemoteDescription(description);
|
||||
}
|
||||
this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
|
||||
var self = this;
|
||||
description = APP.simulcast.transformRemoteDescription(description);
|
||||
this.trace('setRemoteDescription', dumpSDP(description));
|
||||
this.peerconnection.setRemoteDescription(description,
|
||||
function () {
|
||||
self.trace('setRemoteDescriptionOnSuccess');
|
||||
@@ -203,7 +244,12 @@ TraceablePeerConnection.prototype.createOffer = function (successCallback, failu
|
||||
this.trace('createOffer', JSON.stringify(constraints, null, ' '));
|
||||
this.peerconnection.createOffer(
|
||||
function (offer) {
|
||||
self.trace('createOfferOnSuccess', dumpSDP(offer));
|
||||
self.trace('createOfferOnSuccess::preTransform (Plan A)', dumpSDP(offer));
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
offer = self.interop.toPlanB(offer);
|
||||
}
|
||||
self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
|
||||
successCallback(offer);
|
||||
},
|
||||
function(err) {
|
||||
@@ -219,8 +265,14 @@ TraceablePeerConnection.prototype.createAnswer = function (successCallback, fail
|
||||
this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
|
||||
this.peerconnection.createAnswer(
|
||||
function (answer) {
|
||||
answer = APP.simulcast.transformAnswer(answer);
|
||||
self.trace('createAnswerOnSuccess', dumpSDP(answer));
|
||||
self.trace('createAnswerOnSuccess::preTransfom (Plan A)', dumpSDP(answer));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
answer = self.interop.toPlanB(answer);
|
||||
} else {
|
||||
answer = APP.simulcast.transformAnswer(answer);
|
||||
}
|
||||
self.trace('createAnswerOnSuccess::postTransfom (Plan B)', dumpSDP(answer));
|
||||
successCallback(answer);
|
||||
},
|
||||
function(err) {
|
||||
|
||||
@@ -85,7 +85,7 @@ var Moderator = {
|
||||
}
|
||||
},
|
||||
|
||||
onMucLeft: function (jid) {
|
||||
onMucMemberLeft: function (jid) {
|
||||
console.info("Someone left is it focus ? " + jid);
|
||||
var resource = Strophe.getResourceFromJid(jid);
|
||||
if (resource === 'focus' && !this.xmppService.sessionTerminated) {
|
||||
@@ -177,17 +177,32 @@ var Moderator = {
|
||||
{ name: 'openSctp', value: config.openSctp})
|
||||
.up();
|
||||
}
|
||||
if (config.enableFirefoxSupport !== undefined) {
|
||||
if(config.startAudioMuted !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'enableFirefoxHacks',
|
||||
value: config.enableFirefoxSupport})
|
||||
{ name: 'startAudioMuted', value: config.startAudioMuted})
|
||||
.up();
|
||||
}
|
||||
if(config.startVideoMuted !== undefined)
|
||||
{
|
||||
elem.c(
|
||||
'property',
|
||||
{ name: 'startVideoMuted', value: config.startVideoMuted})
|
||||
.up();
|
||||
}
|
||||
elem.up();
|
||||
return elem;
|
||||
},
|
||||
|
||||
parseSessionId: function (resultIq) {
|
||||
var sessionId = $(resultIq).find('conference').attr('session-id');
|
||||
if (sessionId) {
|
||||
console.info('Received sessionId: ' + sessionId);
|
||||
localStorage.setItem('sessionId', sessionId);
|
||||
}
|
||||
},
|
||||
|
||||
parseConfigOptions: function (resultIq) {
|
||||
|
||||
Moderator.setFocusUserJid(
|
||||
@@ -209,12 +224,7 @@ var Moderator = {
|
||||
|
||||
if (!externalAuthEnabled) {
|
||||
// We expect to receive sessionId in 'internal' authentication mode
|
||||
var sessionId
|
||||
= $(resultIq).find('conference').attr('session-id');
|
||||
if (sessionId) {
|
||||
console.info('Received sessionId: ' + sessionId);
|
||||
localStorage.setItem('sessionId', sessionId);
|
||||
}
|
||||
Moderator.parseSessionId(resultIq);
|
||||
}
|
||||
|
||||
var authIdentity = $(resultIq).find('>conference').attr('identity');
|
||||
@@ -275,25 +285,39 @@ var Moderator = {
|
||||
console.info("Session expired! - removing");
|
||||
localStorage.removeItem("sessionId");
|
||||
}
|
||||
if ($(error).find('>error>graceful-shutdown').length) {
|
||||
eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
|
||||
return;
|
||||
}
|
||||
// Check for error returned by the reservation system
|
||||
var reservationErr = $(error).find('>error>reservation-error');
|
||||
if (reservationErr.length) {
|
||||
// Trigger error event
|
||||
var errorCode = reservationErr.attr('error-code');
|
||||
var errorMsg;
|
||||
if ($(error).find('>error>text')) {
|
||||
errorMsg = $(error).find('>error>text').text();
|
||||
}
|
||||
eventEmitter.emit(
|
||||
XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
|
||||
return;
|
||||
}
|
||||
// Not authorized to create new room
|
||||
if ($(error).find('>error>not-authorized').length) {
|
||||
console.warn("Unauthorized to start the conference", error);
|
||||
var toDomain
|
||||
= Strophe.getDomainFromJid(error.getAttribute('to'));
|
||||
if (toDomain === config.hosts.anonymousdomain) {
|
||||
// we are connected with anonymous domain and
|
||||
// only non anonymous users can create rooms
|
||||
// we must authorize the user
|
||||
self.xmppService.promptLogin();
|
||||
} else {
|
||||
// External authentication mode
|
||||
eventEmitter.emit(
|
||||
XMPPEvents.AUTHENTICATION_REQUIRED,
|
||||
function () {
|
||||
Moderator.allocateConferenceFocus(
|
||||
roomName, callback);
|
||||
});
|
||||
if (toDomain !== config.hosts.anonymousdomain) {
|
||||
// FIXME: "is external" should come either from
|
||||
// the focus or config.js
|
||||
externalAuthEnabled = true;
|
||||
}
|
||||
eventEmitter.emit(
|
||||
XMPPEvents.AUTHENTICATION_REQUIRED,
|
||||
function () {
|
||||
Moderator.allocateConferenceFocus(
|
||||
roomName, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var waitMs = getNextErrorTimeout();
|
||||
@@ -307,9 +331,7 @@ var Moderator = {
|
||||
if (!invalidSession) {
|
||||
APP.UI.messageHandler.notify(
|
||||
null, "notify.focus",
|
||||
'Conference focus', 'disconnected', "notify.focusFail",
|
||||
focusComponent + " not available - retry in " +
|
||||
retrySec + " sec",
|
||||
'disconnected', "notify.focusFail",
|
||||
{component: focusComponent, ms: retrySec});
|
||||
}
|
||||
// Reset response timeout
|
||||
|
||||
@@ -108,7 +108,8 @@ var Recording = {
|
||||
if (!recordingToken && !useJirecon) {
|
||||
tokenEmptyCallback(function (value) {
|
||||
setRecordingToken(value);
|
||||
self.toggleRecording();
|
||||
self.toggleRecording(tokenEmptyCallback,
|
||||
startingCallback, startedCallback, connection);
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
@@ -28,6 +28,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
initPresenceMap: function (myroomjid) {
|
||||
this.presMap['to'] = myroomjid;
|
||||
this.presMap['xns'] = 'http://jabber.org/protocol/muc';
|
||||
if(APP.RTC.localAudio.isMuted())
|
||||
{
|
||||
this.addAudioInfoToPresence(true);
|
||||
}
|
||||
|
||||
if(APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
this.addVideoInfoToPresence(true);
|
||||
}
|
||||
},
|
||||
doJoin: function (jid, password) {
|
||||
this.myroomjid = jid;
|
||||
@@ -143,6 +152,32 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
|
||||
}
|
||||
|
||||
var startMuted = $(pres).find('>startmuted');
|
||||
if (startMuted.length)
|
||||
{
|
||||
eventEmitter.emit(XMPPEvents.START_MUTED,
|
||||
startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
|
||||
}
|
||||
|
||||
var devices = $(pres).find('>devices');
|
||||
if(devices.length)
|
||||
{
|
||||
var audio = devices.find('>audio');
|
||||
var video = devices.find('>video');
|
||||
var devicesValues = {audio: false, video: false};
|
||||
if(audio.length && audio.text() === "true")
|
||||
{
|
||||
devicesValues.audio = true;
|
||||
}
|
||||
|
||||
if(video.length && video.text() === "true")
|
||||
{
|
||||
devicesValues.video = true;
|
||||
}
|
||||
eventEmitter.emit(XMPPEvents.DEVICE_AVAILABLE,
|
||||
Strophe.getResourceFromJid(from), devicesValues);
|
||||
}
|
||||
|
||||
var stats = $(pres).find('>stats');
|
||||
if (stats.length) {
|
||||
var statsObj = {};
|
||||
@@ -182,7 +217,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
if (this.role !== member.role) {
|
||||
this.role = member.role;
|
||||
|
||||
eventEmitter.emit(XMPPEvents.LOCALROLE_CHANGED,
|
||||
eventEmitter.emit(XMPPEvents.LOCAL_ROLE_CHANGED,
|
||||
from, member, pres, Moderator.isModerator());
|
||||
}
|
||||
if (!this.joined) {
|
||||
@@ -200,12 +235,12 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
console.info("Ignore focus: " + from + ", real JID: " + member.jid);
|
||||
}
|
||||
else {
|
||||
var id = $(pres).find('>userID').text();
|
||||
var id = $(pres).find('>userId').text();
|
||||
var email = $(pres).find('>email');
|
||||
if (email.length > 0) {
|
||||
id = email.text();
|
||||
}
|
||||
eventEmitter.emit(XMPPEvents.MUC_ENTER, from, id, member.displayName);
|
||||
eventEmitter.emit(XMPPEvents.MUC_MEMBER_JOINED, from, id, member.displayName);
|
||||
}
|
||||
} else {
|
||||
// Presence update for existing participant
|
||||
@@ -229,6 +264,29 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
},
|
||||
onPresenceUnavailable: function (pres) {
|
||||
var from = pres.getAttribute('from');
|
||||
// room destroyed ?
|
||||
if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]' +
|
||||
'>destroy').length) {
|
||||
var reason;
|
||||
var reasonSelect = $(pres).find(
|
||||
'>x[xmlns="http://jabber.org/protocol/muc#user"]' +
|
||||
'>destroy>reason');
|
||||
if (reasonSelect.length) {
|
||||
reason = reasonSelect.text();
|
||||
}
|
||||
XMPP.disposeConference(false);
|
||||
eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
// Remove old ssrcs coming from the jid
|
||||
Object.keys(this.ssrc2jid).forEach(function (ssrc) {
|
||||
if (self.ssrc2jid[ssrc] == from) {
|
||||
delete self.ssrc2jid[ssrc];
|
||||
}
|
||||
});
|
||||
|
||||
// Status code 110 indicates that this notification is "self-presence".
|
||||
if (!$(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length) {
|
||||
delete this.members[from];
|
||||
@@ -271,26 +329,18 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
// We're either missing Jicofo/Prosody config for anonymous
|
||||
// domains or something is wrong.
|
||||
// XMPP.promptLogin();
|
||||
APP.UI.messageHandler.openReportDialog(null, null,
|
||||
"dialog.joinError",
|
||||
'Oops ! We couldn`t join the conference.' +
|
||||
' There might be some problem with security' +
|
||||
' configuration. Please contact service' +
|
||||
' administrator.', pres);
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.joinError", pres);
|
||||
} else {
|
||||
console.warn('onPresError ', pres);
|
||||
APP.UI.messageHandler.openReportDialog(null, null,
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.connectError",
|
||||
'Oops! Something went wrong and we couldn`t ' +
|
||||
'connect to the conference.',
|
||||
pres);
|
||||
}
|
||||
} else {
|
||||
console.warn('onPresError ', pres);
|
||||
APP.UI.messageHandler.openReportDialog(null, null,
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.connectError",
|
||||
'Oops! Something went wrong and we couldn`t ' +
|
||||
'connect to the conference.',
|
||||
pres);
|
||||
}
|
||||
return true;
|
||||
@@ -380,6 +430,10 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
});
|
||||
},
|
||||
sendPresence: function () {
|
||||
if (!this.presMap['to']) {
|
||||
// Too early to send presence - not initialized
|
||||
return;
|
||||
}
|
||||
var pres = $pres({to: this.presMap['to'] });
|
||||
pres.c('x', {xmlns: this.presMap['xns']});
|
||||
|
||||
@@ -416,6 +470,11 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
.t(this.presMap['displayName']).up();
|
||||
}
|
||||
|
||||
if(this.presMap["devices"])
|
||||
{
|
||||
pres.c('devices').c('audio').t(this.presMap['devices'].audio).up()
|
||||
.c('video').t(this.presMap['devices'].video).up().up();
|
||||
}
|
||||
if (this.presMap['audions']) {
|
||||
pres.c('audiomuted', {xmlns: this.presMap['audions']})
|
||||
.t(this.presMap['audiomuted']).up();
|
||||
@@ -463,6 +522,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
|| 'sendrecv' }
|
||||
).up();
|
||||
}
|
||||
pres.up();
|
||||
}
|
||||
|
||||
if(this.presMap["startMuted"] !== undefined)
|
||||
{
|
||||
pres.c("startmuted", {audio: this.presMap["startMuted"].audio,
|
||||
video: this.presMap["startMuted"].video,
|
||||
xmlns: "http://jitsi.org/jitmeet/start-muted"});
|
||||
delete this.presMap["startMuted"];
|
||||
}
|
||||
|
||||
pres.up();
|
||||
@@ -479,6 +547,9 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
this.presMap['source' + sourceNumber + '_ssrc'] = ssrcs;
|
||||
this.presMap['source' + sourceNumber + '_direction'] = direction;
|
||||
},
|
||||
addDevicesToPresence: function (devices) {
|
||||
this.presMap['devices'] = devices;
|
||||
},
|
||||
clearPresenceMedia: function () {
|
||||
var self = this;
|
||||
Object.keys(this.presMap).forEach(function (key) {
|
||||
@@ -540,6 +611,9 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
addUserIdToPresence: function (userId) {
|
||||
this.presMap['userId'] = userId;
|
||||
},
|
||||
addStartMutedToPresence: function (audio, video) {
|
||||
this.presMap["startMuted"] = {audio: audio, video: video};
|
||||
},
|
||||
isModerator: function () {
|
||||
return this.role === 'moderator';
|
||||
},
|
||||
@@ -551,7 +625,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
},
|
||||
onParticipantLeft: function (jid) {
|
||||
|
||||
eventEmitter.emit(XMPPEvents.MUC_LEFT, jid);
|
||||
eventEmitter.emit(XMPPEvents.MUC_MEMBER_LEFT, jid);
|
||||
|
||||
this.connection.jingle.terminateByJid(jid);
|
||||
|
||||
@@ -560,7 +634,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
[jid, this.getPrezi(jid)]);
|
||||
}
|
||||
|
||||
Moderator.onMucLeft(jid);
|
||||
Moderator.onMucMemberLeft(jid);
|
||||
},
|
||||
parsePresence: function (from, memeber, pres) {
|
||||
if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
|
||||
@@ -584,8 +658,6 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
//console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
|
||||
var ssrcV = ssrc.getAttribute('ssrc');
|
||||
self.ssrc2jid[ssrcV] = from;
|
||||
JingleSession.notReceivedSSRCs.push(ssrcV);
|
||||
|
||||
|
||||
var type = ssrc.getAttribute('type');
|
||||
|
||||
@@ -595,7 +667,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
|
||||
});
|
||||
|
||||
eventEmitter.emit(XMPPEvents.CHANGED_STREAMS, from, changedStreams);
|
||||
eventEmitter.emit(XMPPEvents.STREAMS_CHANGED, from, changedStreams);
|
||||
|
||||
var displayName = !config.displayJids
|
||||
? memeber.displayName : Strophe.getResourceFromJid(from);
|
||||
|
||||
@@ -108,6 +108,14 @@ module.exports = function(XMPP, eventEmitter)
|
||||
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
|
||||
switch (action) {
|
||||
case 'session-initiate':
|
||||
var startMuted = $(iq).find('jingle>startmuted');
|
||||
if(startMuted && startMuted.length > 0)
|
||||
{
|
||||
var audioMuted = startMuted.attr("audio");
|
||||
var videoMuted = startMuted.attr("video");
|
||||
APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
|
||||
(videoMuted === "true"));
|
||||
}
|
||||
sess = new JingleSession(
|
||||
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
||||
this.connection, XMPP);
|
||||
|
||||
@@ -1,79 +1,153 @@
|
||||
/* global $, APP, config, Strophe*/
|
||||
var Moderator = require("./moderator");
|
||||
var EventEmitter = require("events");
|
||||
var Recording = require("./recording");
|
||||
var SDP = require("./SDP");
|
||||
var Settings = require("../settings/Settings");
|
||||
var Pako = require("pako");
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
|
||||
var RTCEvents = require("../../service/RTC/RTCEvents");
|
||||
var UIEvents = require("../../service/UI/UIEvents");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var retry = require('retry');
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
var connection = null;
|
||||
var authenticatedUser = false;
|
||||
|
||||
function connect(jid, password, uiCredentials) {
|
||||
var bosh
|
||||
= (uiCredentials && uiCredentials.bosh? uiCredentials.bosh : null)
|
||||
|| config.bosh || '/http-bind';
|
||||
connection = new Strophe.Connection(bosh);
|
||||
Moderator.setConnection(connection);
|
||||
function connect(jid, password) {
|
||||
|
||||
if(uiCredentials) {
|
||||
var email = uiCredentials.email;
|
||||
var displayName = uiCredentials.displayName;
|
||||
if (email) {
|
||||
connection.emuc.addEmailToPresence(email);
|
||||
} else {
|
||||
connection.emuc.addUserIdToPresence(uiCredentials.uid);
|
||||
var faultTolerantConnect = retry.operation({
|
||||
retries: 3
|
||||
});
|
||||
|
||||
// fault tolerant connect
|
||||
faultTolerantConnect.attempt(function () {
|
||||
|
||||
connection = XMPP.createConnection();
|
||||
Moderator.setConnection(connection);
|
||||
|
||||
if (connection.disco) {
|
||||
// for chrome, add multistream cap
|
||||
}
|
||||
if (displayName) {
|
||||
connection.emuc.addDisplayNameToPresence(displayName);
|
||||
connection.jingle.pc_constraints = APP.RTC.getPCConstraints();
|
||||
if (config.useIPv6) {
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2828
|
||||
if (!connection.jingle.pc_constraints.optional)
|
||||
connection.jingle.pc_constraints.optional = [];
|
||||
connection.jingle.pc_constraints.optional.push({googIPv6: true});
|
||||
}
|
||||
}
|
||||
|
||||
if (connection.disco) {
|
||||
// for chrome, add multistream cap
|
||||
}
|
||||
connection.jingle.pc_constraints = APP.RTC.getPCConstraints();
|
||||
if (config.useIPv6) {
|
||||
// https://code.google.com/p/webrtc/issues/detail?id=2828
|
||||
if (!connection.jingle.pc_constraints.optional)
|
||||
connection.jingle.pc_constraints.optional = [];
|
||||
connection.jingle.pc_constraints.optional.push({googIPv6: true});
|
||||
}
|
||||
// Include user info in MUC presence
|
||||
var settings = Settings.getSettings();
|
||||
if (settings.email) {
|
||||
connection.emuc.addEmailToPresence(settings.email);
|
||||
}
|
||||
if (settings.uid) {
|
||||
connection.emuc.addUserIdToPresence(settings.uid);
|
||||
}
|
||||
if (settings.displayName) {
|
||||
connection.emuc.addDisplayNameToPresence(settings.displayName);
|
||||
}
|
||||
|
||||
if(!password)
|
||||
password = uiCredentials.password;
|
||||
|
||||
var anonymousConnectionFailed = false;
|
||||
connection.connect(jid, password, function (status, msg) {
|
||||
console.log('Strophe status changed to',
|
||||
Strophe.getStatusString(status));
|
||||
if (status === Strophe.Status.CONNECTED) {
|
||||
if (config.useStunTurn) {
|
||||
connection.jingle.getStunAndTurnCredentials();
|
||||
}
|
||||
APP.UI.disableConnect();
|
||||
// connection.connect() starts the connection process.
|
||||
//
|
||||
// As the connection process proceeds, the user supplied callback will
|
||||
// be triggered multiple times with status updates. The callback should
|
||||
// take two arguments - the status code and the error condition.
|
||||
//
|
||||
// The status code will be one of the values in the Strophe.Status
|
||||
// constants. The error condition will be one of the conditions defined
|
||||
// in RFC 3920 or the condition ‘strophe-parsererror’.
|
||||
//
|
||||
// The Parameters wait, hold and route are optional and only relevant
|
||||
// for BOSH connections. Please see XEP 124 for a more detailed
|
||||
// explanation of the optional parameters.
|
||||
//
|
||||
// Connection status constants for use by the connection handler
|
||||
// callback.
|
||||
//
|
||||
// Status.ERROR - An error has occurred (websockets specific)
|
||||
// Status.CONNECTING - The connection is currently being made
|
||||
// Status.CONNFAIL - The connection attempt failed
|
||||
// Status.AUTHENTICATING - The connection is authenticating
|
||||
// Status.AUTHFAIL - The authentication attempt failed
|
||||
// Status.CONNECTED - The connection has succeeded
|
||||
// Status.DISCONNECTED - The connection has been terminated
|
||||
// Status.DISCONNECTING - The connection is currently being terminated
|
||||
// Status.ATTACHED - The connection has been attached
|
||||
|
||||
console.info("My Jabber ID: " + connection.jid);
|
||||
var anonymousConnectionFailed = false;
|
||||
var connectionFailed = false;
|
||||
var lastErrorMsg;
|
||||
connection.connect(jid, password, function (status, msg) {
|
||||
console.log('Strophe status changed to',
|
||||
Strophe.getStatusString(status), msg);
|
||||
if (status === Strophe.Status.CONNECTED) {
|
||||
if (config.useStunTurn) {
|
||||
connection.jingle.getStunAndTurnCredentials();
|
||||
}
|
||||
|
||||
if(password)
|
||||
authenticatedUser = true;
|
||||
maybeDoJoin();
|
||||
} else if (status === Strophe.Status.CONNFAIL) {
|
||||
if(msg === 'x-strophe-bad-non-anon-jid') {
|
||||
anonymousConnectionFailed = true;
|
||||
}
|
||||
} else if (status === Strophe.Status.DISCONNECTED) {
|
||||
if(anonymousConnectionFailed) {
|
||||
// prompt user for username and password
|
||||
console.info("My Jabber ID: " + connection.jid);
|
||||
|
||||
if (password)
|
||||
authenticatedUser = true;
|
||||
maybeDoJoin();
|
||||
} else if (status === Strophe.Status.CONNFAIL) {
|
||||
if (msg === 'x-strophe-bad-non-anon-jid') {
|
||||
anonymousConnectionFailed = true;
|
||||
} else {
|
||||
connectionFailed = true;
|
||||
}
|
||||
lastErrorMsg = msg;
|
||||
} else if (status === Strophe.Status.DISCONNECTED) {
|
||||
if (anonymousConnectionFailed) {
|
||||
// prompt user for username and password
|
||||
XMPP.promptLogin();
|
||||
} else {
|
||||
|
||||
// Strophe already has built-in HTTP/BOSH error handling and
|
||||
// request retry logic. Requests are resent automatically
|
||||
// until their error count reaches 5. Strophe.js disconnects
|
||||
// if the error count is > 5. We are not replicating this
|
||||
// here.
|
||||
//
|
||||
// The "problem" is that failed HTTP/BOSH requests don't
|
||||
// trigger a callback with a status update, so when a
|
||||
// callback with status Strophe.Status.DISCONNECTED arrives,
|
||||
// we can't be sure if it's a graceful disconnect or if it's
|
||||
// triggered by some HTTP/BOSH error.
|
||||
//
|
||||
// But that's a minor issue in Jitsi Meet as we never
|
||||
// disconnect anyway, not even when the user closes the
|
||||
// browser window (which is kind of wrong, but the point is
|
||||
// that we should never ever get disconnected).
|
||||
//
|
||||
// On the other hand, failed connections due to XMPP layer
|
||||
// errors, trigger a callback with status Strophe.Status.CONNFAIL.
|
||||
//
|
||||
// Here we implement retry logic for failed connections due
|
||||
// to XMPP layer errors and we display an error to the user
|
||||
// if we get disconnected from the XMPP server permanently.
|
||||
|
||||
// If the connection failed, retry.
|
||||
if (connectionFailed
|
||||
&& faultTolerantConnect.retry("connection-failed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we failed to connect to the XMPP server, fire an event
|
||||
// to let all the interested module now about it.
|
||||
eventEmitter.emit(XMPPEvents.CONNECTION_FAILED,
|
||||
msg ? msg : lastErrorMsg);
|
||||
}
|
||||
} else if (status === Strophe.Status.AUTHFAIL) {
|
||||
// wrong password or username, prompt user
|
||||
XMPP.promptLogin();
|
||||
}
|
||||
} else if (status === Strophe.Status.AUTHFAIL) {
|
||||
// wrong password or username, prompt user
|
||||
XMPP.promptLogin();
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,13 +182,21 @@ function initStrophePlugins()
|
||||
function registerListeners() {
|
||||
APP.RTC.addStreamListener(maybeDoJoin,
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
|
||||
APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
|
||||
XMPP.addToPresence("devices", devices);
|
||||
})
|
||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
|
||||
XMPP.addToPresence("displayName", nickname);
|
||||
});
|
||||
}
|
||||
|
||||
function setupEvents() {
|
||||
$(window).bind('beforeunload', function () {
|
||||
var unload = (function () {
|
||||
var unloaded = false;
|
||||
|
||||
return function () {
|
||||
if (unloaded) { return; }
|
||||
unloaded = true;
|
||||
|
||||
if (connection && connection.connected) {
|
||||
// ensure signout
|
||||
$.ajax({
|
||||
@@ -123,34 +205,58 @@ function setupEvents() {
|
||||
async: false,
|
||||
cache: false,
|
||||
contentType: 'application/xml',
|
||||
data: "<body rid='" + (connection.rid || connection._proto.rid)
|
||||
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='"
|
||||
+ (connection.sid || connection._proto.sid)
|
||||
+ "' type='terminate'>" +
|
||||
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
||||
"</body>",
|
||||
data: "<body rid='" + (connection.rid || connection._proto.rid) +
|
||||
"' xmlns='http://jabber.org/protocol/httpbind' sid='" +
|
||||
(connection.sid || connection._proto.sid) +
|
||||
"' type='terminate'>" +
|
||||
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
||||
"</body>",
|
||||
success: function (data) {
|
||||
console.log('signed out');
|
||||
console.log(data);
|
||||
},
|
||||
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
||||
console.log('signout error',
|
||||
textStatus + ' (' + errorThrown + ')');
|
||||
textStatus + ' (' + errorThrown + ')');
|
||||
}
|
||||
});
|
||||
}
|
||||
XMPP.disposeConference(true);
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
function setupEvents() {
|
||||
// In recent versions of FF the 'beforeunload' event is not fired when the
|
||||
// window or the tab is closed. It is only fired when we leave the page
|
||||
// (change URL). If this participant doesn't unload properly, then it
|
||||
// becomes a ghost for the rest of the participants that stay in the
|
||||
// conference. Thankfully handling the 'unload' event in addition to the
|
||||
// 'beforeunload' event seems to garante the execution of the 'unload'
|
||||
// method at least once.
|
||||
//
|
||||
// The 'unload' method can safely be run multiple times, it will actually do
|
||||
// something only the first time that it's run, so we're don't have to worry
|
||||
// about browsers that fire both events.
|
||||
|
||||
$(window).bind('beforeunload', unload);
|
||||
$(window).bind('unload', unload);
|
||||
}
|
||||
|
||||
var XMPP = {
|
||||
getConnection: function(){ return connection; },
|
||||
sessionTerminated: false,
|
||||
|
||||
/**
|
||||
* XMPP connection status
|
||||
*/
|
||||
Status: Strophe.Status,
|
||||
|
||||
/**
|
||||
* Remembers if we were muted by the focus.
|
||||
* @type {boolean}
|
||||
*/
|
||||
forceMuted: false,
|
||||
start: function (uiCredentials) {
|
||||
start: function () {
|
||||
setupEvents();
|
||||
initStrophePlugins();
|
||||
registerListeners();
|
||||
@@ -161,10 +267,19 @@ var XMPP = {
|
||||
window.location.search.indexOf("login=true") !== -1) {
|
||||
configDomain = config.hosts.domain;
|
||||
}
|
||||
var jid = uiCredentials.jid || configDomain || window.location.hostname;
|
||||
connect(jid, null, uiCredentials);
|
||||
var jid = configDomain || window.location.hostname;
|
||||
connect(jid, null);
|
||||
},
|
||||
createConnection: function () {
|
||||
var bosh = config.bosh || '/http-bind';
|
||||
|
||||
return new Strophe.Connection(bosh);
|
||||
},
|
||||
getStatusString: function (status) {
|
||||
return Strophe.getStatusString(status);
|
||||
},
|
||||
promptLogin: function () {
|
||||
// FIXME: re-use LoginDialog which supports retries
|
||||
APP.UI.showLoginPopup(connect);
|
||||
},
|
||||
joinRoom: function(roomName, useNicks, nick)
|
||||
@@ -206,10 +321,12 @@ var XMPP = {
|
||||
// FIXME: probably removing streams is not required and close() should
|
||||
// be enough
|
||||
if (APP.RTC.localAudio) {
|
||||
handler.peerconnection.removeStream(APP.RTC.localAudio.getOriginalStream(), onUnload);
|
||||
handler.peerconnection.removeStream(
|
||||
APP.RTC.localAudio.getOriginalStream(), onUnload);
|
||||
}
|
||||
if (APP.RTC.localVideo) {
|
||||
handler.peerconnection.removeStream(APP.RTC.localVideo.getOriginalStream(), onUnload);
|
||||
handler.peerconnection.removeStream(
|
||||
APP.RTC.localVideo.getOriginalStream(), onUnload);
|
||||
}
|
||||
handler.peerconnection.close();
|
||||
}
|
||||
@@ -245,23 +362,40 @@ var XMPP = {
|
||||
isExternalAuthEnabled: function () {
|
||||
return Moderator.isExternalAuthEnabled();
|
||||
},
|
||||
switchStreams: function (stream, oldStream, callback) {
|
||||
switchStreams: function (stream, oldStream, callback, isAudio) {
|
||||
if (connection && connection.jingle.activecall) {
|
||||
// FIXME: will block switchInProgress on true value in case of exception
|
||||
connection.jingle.activecall.switchStreams(stream, oldStream, callback);
|
||||
connection.jingle.activecall.switchStreams(stream, oldStream, callback, isAudio);
|
||||
} else {
|
||||
// We are done immediately
|
||||
console.error("No conference handler");
|
||||
APP.UI.messageHandler.showError("dialog.error", 'Error',
|
||||
"dialog.unableToSwitch", 'Unable to switch video stream.');
|
||||
console.warn("No conference handler or conference not started yet");
|
||||
callback();
|
||||
}
|
||||
},
|
||||
sendVideoInfoPresence: function (mute) {
|
||||
if(!connection)
|
||||
return;
|
||||
connection.emuc.addVideoInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
},
|
||||
setVideoMute: function (mute, callback, options) {
|
||||
if(connection && APP.RTC.localVideo && connection.jingle.activecall)
|
||||
{
|
||||
connection.jingle.activecall.setVideoMute(mute, callback, options);
|
||||
}
|
||||
if(!connection)
|
||||
return;
|
||||
var self = this;
|
||||
var localCallback = function (mute) {
|
||||
self.sendVideoInfoPresence(mute);
|
||||
return callback(mute);
|
||||
};
|
||||
|
||||
if(connection.jingle.activecall)
|
||||
{
|
||||
connection.jingle.activecall.setVideoMute(
|
||||
mute, localCallback, options);
|
||||
}
|
||||
else {
|
||||
localCallback(mute);
|
||||
}
|
||||
|
||||
},
|
||||
setAudioMute: function (mute, callback) {
|
||||
if (!(connection && APP.RTC.localAudio)) {
|
||||
@@ -284,10 +418,17 @@ var XMPP = {
|
||||
// It is not clear what is the right way to handle multiple tracks.
|
||||
// So at least make sure that they are all muted or all unmuted and
|
||||
// that we send presence just once.
|
||||
APP.RTC.localAudio.mute();
|
||||
APP.RTC.localAudio.setMute(!mute);
|
||||
// isMuted is the opposite of audioEnabled
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
this.sendAudioInfoPresence(mute, callback);
|
||||
return true;
|
||||
},
|
||||
sendAudioInfoPresence: function(mute, callback)
|
||||
{
|
||||
if(connection) {
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
}
|
||||
callback();
|
||||
return true;
|
||||
},
|
||||
@@ -315,9 +456,7 @@ var XMPP = {
|
||||
function (error) {
|
||||
console.log('mute SLD error');
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
'Error', "dialog.SLDFailure",
|
||||
'Oops! Something went wrong and we failed to ' +
|
||||
'mute! (SLD Failure)');
|
||||
"dialog.SLDFailure");
|
||||
}
|
||||
);
|
||||
},
|
||||
@@ -329,10 +468,8 @@ var XMPP = {
|
||||
},
|
||||
function (error) {
|
||||
console.log('muteVideo SRD error');
|
||||
APP.UI.messageHandler.showError("dialog.error", 'Error',
|
||||
"dialog.SRDFailure",
|
||||
'Oops! Something went wrong and we failed to stop video!' +
|
||||
'(SRD Failure)');
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
"dialog.SRDFailure");
|
||||
|
||||
}
|
||||
);
|
||||
@@ -362,16 +499,32 @@ var XMPP = {
|
||||
break;
|
||||
case "email":
|
||||
connection.emuc.addEmailToPresence(value);
|
||||
break;
|
||||
case "devices":
|
||||
connection.emuc.addDevicesToPresence(value);
|
||||
break;
|
||||
case "startMuted":
|
||||
if(!Moderator.isModerator())
|
||||
return;
|
||||
connection.emuc.addStartMutedToPresence(value[0],
|
||||
value[1]);
|
||||
break;
|
||||
default :
|
||||
console.log("Unknown tag for presence.");
|
||||
console.log("Unknown tag for presence: " + name);
|
||||
return;
|
||||
}
|
||||
if(!dontSend)
|
||||
if (!dontSend)
|
||||
connection.emuc.sendPresence();
|
||||
},
|
||||
/**
|
||||
* Sends 'data' as a log message to the focus. Returns true iff a message
|
||||
* was sent.
|
||||
* @param data
|
||||
* @returns {boolean} true iff a message was sent.
|
||||
*/
|
||||
sendLogs: function (data) {
|
||||
if(!connection.emuc.focusMucJid)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var deflate = true;
|
||||
|
||||
@@ -391,6 +544,7 @@ var XMPP = {
|
||||
message.up();
|
||||
|
||||
connection.send(message);
|
||||
return true;
|
||||
},
|
||||
populateData: function () {
|
||||
var data = {};
|
||||
@@ -448,8 +602,13 @@ var XMPP = {
|
||||
},
|
||||
getSessions: function () {
|
||||
return connection.jingle.sessions;
|
||||
},
|
||||
removeStream: function (stream) {
|
||||
if(!connection || !connection.jingle.activecall ||
|
||||
!connection.jingle.activecall.peerconnection)
|
||||
return;
|
||||
connection.jingle.activecall.peerconnection.removeStream(stream);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = XMPP;
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
"dependencies": {
|
||||
"events": "*",
|
||||
"pako": "*",
|
||||
"i18next-client": "*"
|
||||
"i18next-client": "1.7.7",
|
||||
"sdp-interop": "0.1.4",
|
||||
"sdp-transform": "1.4.0",
|
||||
"async": "0.9.0",
|
||||
"retry": "0.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
|
||||
0
plugin.head.html
Normal file
0
plugin.header.text.html
Normal file
0
plugin.welcomepage.footer.html
Normal file
@@ -5,7 +5,8 @@ var RTCEvents = {
|
||||
SIMULCAST_LAYER_CHANGED: "rtc.simulcast_layer_changed",
|
||||
SIMULCAST_LAYER_CHANGING: "rtc.simulcast_layer_changing",
|
||||
SIMULCAST_START: "rtc.simlcast_start",
|
||||
SIMULCAST_STOP: "rtc.simlcast_stop"
|
||||
SIMULCAST_STOP: "rtc.simlcast_stop",
|
||||
AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed"
|
||||
};
|
||||
|
||||
module.exports = RTCEvents;
|
||||
@@ -7,7 +7,9 @@ var StreamEventTypes = {
|
||||
|
||||
EVENT_TYPE_REMOTE_CREATED: "stream.remote_created",
|
||||
|
||||
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended"
|
||||
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended",
|
||||
|
||||
EVENT_TYPE_REMOTE_CHANGED: "stream.changed"
|
||||
};
|
||||
|
||||
module.exports = StreamEventTypes;
|
||||
5
service/members/Events.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var Events = {
|
||||
DTMF_SUPPORT_CHANGED: "members.dtmf_support_changed"
|
||||
};
|
||||
|
||||
module.exports = Events;
|
||||
@@ -8,5 +8,8 @@ module.exports = {
|
||||
}
|
||||
return languages;
|
||||
},
|
||||
EN: "en"
|
||||
EN: "en",
|
||||
BG: "bg",
|
||||
DE: "de",
|
||||
TR: "tr"
|
||||
}
|
||||