mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-06 06:42:28 +00:00
Compare commits
87 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 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
node_modules
|
||||
*.swp
|
||||
.idea/
|
||||
*.iml
|
||||
*.iml
|
||||
.*.tmp
|
||||
|
||||
2
Makefile
2
Makefile
@@ -21,4 +21,4 @@ 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
|
||||
|
||||
5
app.js
5
app.js
@@ -15,6 +15,8 @@ var APP =
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,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
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
|
||||
@@ -26,13 +26,11 @@ 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: true, //firefox support is still experimental and
|
||||
// will work when simulcast is *disabled* and rtcpMux & bundle are *enabled*.
|
||||
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. ' +
|
||||
|
||||
@@ -119,4 +119,8 @@
|
||||
|
||||
.icon-settings:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
}
|
||||
|
||||
.icon-dialPad:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
2
debian/jitsi-meet-prosody.postinst
vendored
2
debian/jitsi-meet-prosody.postinst
vendored
@@ -29,7 +29,7 @@ case "$1" in
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
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
|
||||
|
||||
2
debian/watch
vendored
2
debian/watch
vendored
@@ -1,2 +1,2 @@
|
||||
version=3
|
||||
https://github.com/jitsi/jitsi-meet/releases/ /jitsi/jitsi-meet/archive/(\S+)\.tar\.gz
|
||||
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
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,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
BIN
fonts/jitsi.eot
Binary file not shown.
@@ -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.ttf
Binary file not shown.
BIN
fonts/jitsi.woff
BIN
fonts/jitsi.woff
Binary file not shown.
@@ -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/noMic.png
Normal file
BIN
images/noMic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
images/noVideo.png
Normal file
BIN
images/noVideo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
31
index.html
31
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,12 +19,12 @@
|
||||
<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=42"></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">
|
||||
@@ -43,6 +43,7 @@
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/diibjkoicjeejcmhdnailmkgecihlobk">
|
||||
<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">
|
||||
@@ -69,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">
|
||||
@@ -117,6 +120,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--#include virtual="plugin.welcomepage.footer.html" -->
|
||||
</div>
|
||||
<div id="videoconference_page">
|
||||
<div style="position: relative;" id="header_container">
|
||||
@@ -197,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>
|
||||
@@ -290,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>
|
||||
|
||||
@@ -8,7 +8,7 @@ var interfaceConfig = {
|
||||
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
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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"en": "Английски",
|
||||
"bg": "Български",
|
||||
"de": "Немски",
|
||||
"tr": "Турски"
|
||||
"tr": "Турски",
|
||||
"it": "Италиански"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"en": "Englisch",
|
||||
"bg": "Bulgarisch",
|
||||
"de": "Deutsch",
|
||||
"tr": "Türkisch"
|
||||
"tr": "Türkisch",
|
||||
"it": "Italienisch"
|
||||
}
|
||||
7
lang/languages-fr.json
Normal file
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
7
lang/languages-it.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"en": "Inglese",
|
||||
"bg": "Bulgaro",
|
||||
"de": "Tedesco",
|
||||
"tr": "Turco",
|
||||
"it": "Italiano"
|
||||
}
|
||||
@@ -2,5 +2,6 @@
|
||||
"en": "English",
|
||||
"bg": "Bulgarian",
|
||||
"de": "German",
|
||||
"tr": "Turkish"
|
||||
}
|
||||
"tr": "Turkish",
|
||||
"it": "Italian"
|
||||
}
|
||||
|
||||
@@ -1,191 +1,200 @@
|
||||
{
|
||||
"contactlist": "СПИСЪК С КОНТАКТИ",
|
||||
"connectionsettings": "Настройки на връзката",
|
||||
"poweredby": "",
|
||||
"downloadlogs": "",
|
||||
"roomUrlDefaultMsg": "",
|
||||
"poweredby": "powered by",
|
||||
"downloadlogs": "Изтегли логовете",
|
||||
"roomUrlDefaultMsg": "Конференцията се създава...",
|
||||
"participant": "Участник",
|
||||
"me": "аз",
|
||||
"speaker": "",
|
||||
"defaultNickname": "",
|
||||
"defaultPreziLink": "",
|
||||
"speaker": "Говорител",
|
||||
"defaultNickname": "например __name__",
|
||||
"defaultPreziLink": "например __url__",
|
||||
"welcomepage": {
|
||||
"go": "",
|
||||
"go": "Влез",
|
||||
"roomname": "Въведете име на стаята",
|
||||
"disable": "",
|
||||
"disable": "Не показвай страницата следващия път",
|
||||
"feature1": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Лесен за употреба",
|
||||
"content": "Не е необходимо да сваляте нищо. _app_ работи директно във вашия браузър. Просто споделете адреса на вашата конференция с другите за да започнете."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"content": "Видео конференциите могат да работят с по-малко от 128Kbps, а аудио конференциите и конференциите с споделен екран дори с по-малко."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Отворен код",
|
||||
"content": "__app__ е лицензиран под MIT лиценз. Можете свободно да го изтеглите, използвате, променяте и споделяте според тези лицензи."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Неограничен брой потребители",
|
||||
"content": "Няма изкуствени ограничения за броя на потребителите или участниците в конференция. Единствените ограничения са мощността на вашия сървър и качеството на интернет връзката му."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Споделяне на екрана",
|
||||
"content": "Лесно е да споделите екрана си с другите. __app__ е идеален за онлайн презентации, лекции и техническа подръжка."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Сигурни стаи",
|
||||
"content": "Нуждаете се от уединение? _app__ конферентните стай могат да бъдат защитени от парола за да се препазите от нежелани гости или прекъсвания."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Споделени бележки",
|
||||
"content": "__app__ използва Etherpad, с който можете да редактирате текст в реално време заедно."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
"title": "Статистики за използване",
|
||||
"content": "Научете повече за своите потребители като интегрирате лесно Piwik, Google Analytics и други статистики за изполването."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "",
|
||||
"videomute": "",
|
||||
"mute": "Включи / Изключи микрофона",
|
||||
"videomute": "Спри / пусни камерата",
|
||||
"authenticate": "",
|
||||
"record": "",
|
||||
"lock": "",
|
||||
"invite": "",
|
||||
"record": "Запис",
|
||||
"lock": "Заключи / отключи стаята",
|
||||
"invite": "Поканване на други",
|
||||
"chat": "",
|
||||
"prezi": "",
|
||||
"etherpad": "",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "",
|
||||
"sip": "",
|
||||
"Settings": "",
|
||||
"hangup": "",
|
||||
"login": "",
|
||||
"prezi": "Сподели Prezi",
|
||||
"etherpad": "Споделяне на документ",
|
||||
"sharescreen": "Споделяне на екрана",
|
||||
"fullscreen": "Влез / Излез от Пълен екран",
|
||||
"sip": "Обади се на SIP номер",
|
||||
"Settings": "Настройки",
|
||||
"hangup": "Затвори",
|
||||
"login": "Влез",
|
||||
"logout": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
"chat": "Отвори / затвори чат",
|
||||
"filmstrip": "Покажи / скрий лентата с видеа",
|
||||
"contactlist": "Отвори / затвори контакт листа"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "",
|
||||
"popover": ""
|
||||
"title": "Въведете име в полето",
|
||||
"popover": "Избор на име"
|
||||
},
|
||||
"messagebox": ""
|
||||
"messagebox": "Въведете текст..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "НАСТРОЙКИ",
|
||||
"update": "",
|
||||
"name": ""
|
||||
"update": "Актуализиране",
|
||||
"name": "Име"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "",
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": ""
|
||||
"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": "",
|
||||
"packetloss": "Загуба на пакети:",
|
||||
"resolution": "Резолюция:",
|
||||
"less": "Скрий",
|
||||
"more": "Покажи",
|
||||
"address": "Адрес:",
|
||||
"remoteport": "Отдалечен порт:",
|
||||
"remoteport_plural": "Отдалечени портове:",
|
||||
"localport": "Локален порт:",
|
||||
"localport_plural": "Локални портове:",
|
||||
"localaddress": "Локален адрес:",
|
||||
"localaddress_plural": "Локални адреси:",
|
||||
"remoteaddress": "Отдалечен адрес:",
|
||||
"remoteaddress_plural": "Отдалечени адреси:",
|
||||
"transport": "Транспорт:",
|
||||
"bandwidth": "",
|
||||
"na": ""
|
||||
"na": "Върнете се тук за информацията относно вашата връзка, когато започне конференцията"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": ""
|
||||
"disconnected": "връзката е прекъсната",
|
||||
"moderator": "Придобихте права на модератор!",
|
||||
"connected": "свързан",
|
||||
"somebody": "Някой",
|
||||
"me": "Аз",
|
||||
"focus": "Конферентен фокус",
|
||||
"focusFail": "__component__ не е на раположения - следващ опит след __ms__ секунди",
|
||||
"grantedTo": "Даване на роля модератор на __to__!",
|
||||
"grantedToUnknown": "Даване на роля модератор на $t(somebody)!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "",
|
||||
"popupError": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"joinError": "",
|
||||
"connectError": "",
|
||||
"kickMessage": "Бяхте изгонен от срещата!",
|
||||
"popupError": "Вашия браузър блокира попъп прозорците от този сайт. Моля позволете попъп прозорците от настройките на браузъра и опитайте пак.",
|
||||
"passwordError": "Тази конференция е защитена с парола. Само създателя и може да промени паролата.",
|
||||
"passwordError2": "Тази конференция не е защитена с парола. Само създателя и може да сложи парола.",
|
||||
"joinError": "Не можете да се присъедините към конференцията. Може би имате проблем с конфигурацията на сифурността. Моля обърнете се към администратора на услугата.",
|
||||
"connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "",
|
||||
"failtoinstall": "",
|
||||
"failedpermissions": "",
|
||||
"bridgeUnavailable": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupported": "",
|
||||
"sorry": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"removePreziTitle": "",
|
||||
"removePreziMsg": "",
|
||||
"sharePreziTitle": "",
|
||||
"sharePreziMsg": "",
|
||||
"Remove": "",
|
||||
"Stop": "",
|
||||
"AuthMsg": "",
|
||||
"Authenticate": "",
|
||||
"Cancel": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"preziLinkError": "",
|
||||
"Save": "",
|
||||
"recordingToken": "",
|
||||
"Dial": "",
|
||||
"sipMsg": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"Invite": "",
|
||||
"shareLink": "",
|
||||
"settings1": "",
|
||||
"settings2": "",
|
||||
"settings3": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": ""
|
||||
"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": "",
|
||||
"subject": "",
|
||||
"sharedKey": [
|
||||
"Тази конференция е защитена с парола. Моля използвайте следния код за да се присъедините:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Покана за __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Здравей, Бих искал да те поканя в една __appName__ конференция, която създадох.",
|
||||
"",
|
||||
@@ -199,6 +208,17 @@
|
||||
"",
|
||||
"Ще се видим след секунда!"
|
||||
],
|
||||
"and": ""
|
||||
"and": "и"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Грешка",
|
||||
"CONNECTING": "Свързване",
|
||||
"CONNFAIL": "Връзката е неуспешна",
|
||||
"AUTHENTICATING": "Идентификация",
|
||||
"AUTHFAIL": "Неуспешна идентификация",
|
||||
"CONNECTED": "Свързан",
|
||||
"DISCONNECTED": "Изключен",
|
||||
"DISCONNECTING": "Прекъсване на връзката",
|
||||
"ATTACHED": "Прикрепен"
|
||||
}
|
||||
}
|
||||
@@ -127,6 +127,7 @@
|
||||
"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.",
|
||||
@@ -154,6 +155,7 @@
|
||||
"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",
|
||||
@@ -213,5 +215,16 @@
|
||||
"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
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
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"
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,8 @@
|
||||
"Settings": "Settings",
|
||||
"hangup": "Hang Up",
|
||||
"login": "Login",
|
||||
"logout": "Logout"
|
||||
"logout": "Logout",
|
||||
"dialpad": "Show dialpad"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Open / close chat",
|
||||
@@ -80,7 +81,9 @@
|
||||
{
|
||||
"title": "SETTINGS",
|
||||
"update": "Update",
|
||||
"name": "Name"
|
||||
"name": "Name",
|
||||
"startAudioMuted": "everyone join audio muted",
|
||||
"startVideoMuted": "everyone join video muted"
|
||||
},
|
||||
"videothumbnail":
|
||||
{
|
||||
@@ -122,8 +125,9 @@
|
||||
"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!",
|
||||
@@ -132,6 +136,7 @@
|
||||
"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.",
|
||||
"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.",
|
||||
@@ -156,9 +161,9 @@
|
||||
"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",
|
||||
|
||||
4247
libs/app.bundle.js
4247
libs/app.bundle.js
File diff suppressed because it is too large
Load Diff
@@ -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
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,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;
|
||||
|
||||
@@ -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, videoType) {
|
||||
createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
|
||||
|
||||
var localStream = new LocalStream(stream, type, eventEmitter, videoType);
|
||||
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) {
|
||||
@@ -111,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") {
|
||||
@@ -133,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;
|
||||
@@ -145,7 +182,7 @@ var RTC = {
|
||||
}
|
||||
|
||||
if(!stream)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if (value != stream.muted) {
|
||||
stream.setMute(value);
|
||||
@@ -166,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.
|
||||
@@ -196,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,7 +130,8 @@ 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 >= 39) {
|
||||
if (version >= 40
|
||||
&& !config.enableSimulcast && config.useBundle && config.useRtcpMux) {
|
||||
this.peerconnection = mozRTCPeerConnection;
|
||||
this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
|
||||
this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
|
||||
@@ -143,6 +144,8 @@ function RTCUtils(RTCService)
|
||||
//
|
||||
// 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();
|
||||
};
|
||||
@@ -155,10 +158,13 @@ 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;
|
||||
@@ -181,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'}]};
|
||||
@@ -225,6 +234,8 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
|
||||
var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
|
||||
var self = this;
|
||||
|
||||
try {
|
||||
if (config.enableSimulcast
|
||||
&& constraints.video
|
||||
@@ -236,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);
|
||||
}
|
||||
@@ -249,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) {
|
||||
@@ -268,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) {
|
||||
@@ -321,42 +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",
|
||||
"dialog.failedpermissions");
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
newStream = stream;
|
||||
|
||||
return newStream;
|
||||
};
|
||||
|
||||
module.exports = RTCUtils;
|
||||
|
||||
140
modules/UI/UI.js
140
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);
|
||||
@@ -105,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;
|
||||
@@ -137,6 +165,7 @@ 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(
|
||||
@@ -182,7 +211,7 @@ function registerListeners() {
|
||||
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
|
||||
@@ -204,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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -230,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);
|
||||
}
|
||||
|
||||
@@ -417,13 +442,16 @@ function onMucJoined(jid, info) {
|
||||
|
||||
if (displayName)
|
||||
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();
|
||||
@@ -450,12 +478,13 @@ 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();
|
||||
@@ -479,7 +508,7 @@ function onModeratorStatusChanged(isModerator) {
|
||||
}
|
||||
};
|
||||
|
||||
function onPasswordReqiured(callback) {
|
||||
function onPasswordRequired(callback) {
|
||||
// password is required
|
||||
Toolbar.lockLockButton();
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
@@ -508,7 +537,17 @@ function onPasswordReqiured(callback) {
|
||||
':input:first'
|
||||
);
|
||||
}
|
||||
function onMucEntered(jid, id, displayName) {
|
||||
|
||||
/**
|
||||
* 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');
|
||||
@@ -690,6 +729,12 @@ 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.
|
||||
*/
|
||||
@@ -707,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");
|
||||
@@ -743,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;
|
||||
|
||||
|
||||
@@ -21,12 +21,14 @@ var Authentication = {
|
||||
// extract room name from 'room@muc.server.net'
|
||||
var room = roomName.substr(0, roomName.indexOf('@'));
|
||||
|
||||
var title = APP.translation.generateTranslatonHTML("dialog.Stop");
|
||||
var msg = APP.translation.generateTranslatonHTML("dialog.AuthMsg",
|
||||
{room: room});
|
||||
var title
|
||||
= APP.translation.generateTranslatonHTML("dialog.WaitingForHost");
|
||||
var msg
|
||||
= APP.translation.generateTranslatonHTML(
|
||||
"dialog.WaitForHostMsg", {room: room});
|
||||
|
||||
var buttonTxt
|
||||
= APP.translation.generateTranslatonHTML("dialog.Authenticate");
|
||||
= APP.translation.generateTranslatonHTML("dialog.IamHost");
|
||||
var buttons = [];
|
||||
buttons.push({title: buttonTxt, value: "authNow"});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ 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
|
||||
@@ -34,11 +34,36 @@ var SettingsMenu = {
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
@@ -55,6 +80,10 @@ var SettingsMenu = {
|
||||
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();
|
||||
},
|
||||
@@ -221,6 +224,11 @@ function inviteParticipants() {
|
||||
window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
|
||||
}
|
||||
|
||||
function dialpadButtonClicked()
|
||||
{
|
||||
//TODO show the dialpad window
|
||||
}
|
||||
|
||||
function callSipButtonClicked()
|
||||
{
|
||||
var defaultNumber
|
||||
@@ -243,8 +251,7 @@ function callSipButtonClicked()
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
':input:first'
|
||||
null, null, ':input:first'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -326,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -419,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);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -550,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");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -568,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;
|
||||
|
||||
@@ -175,7 +175,7 @@ var messageHandler = (function(my) {
|
||||
};
|
||||
|
||||
my.notify = function(displayName, displayNameKey,
|
||||
cls, messageKey, messageArguments) {
|
||||
cls, messageKey, messageArguments, options) {
|
||||
var displayNameSpan = '<span class="nickname" ';
|
||||
if(displayName)
|
||||
{
|
||||
@@ -195,7 +195,7 @@ var messageHandler = (function(my) {
|
||||
: "") + ">" +
|
||||
APP.translation.translateString(messageKey,
|
||||
messageArguments) +
|
||||
'</span>');
|
||||
'</span>', null, options);
|
||||
};
|
||||
|
||||
return my;
|
||||
|
||||
@@ -550,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;
|
||||
@@ -581,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();
|
||||
@@ -639,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.
|
||||
@@ -664,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -713,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) {
|
||||
@@ -732,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
|
||||
@@ -774,7 +841,6 @@ var VideoLayout = (function (my) {
|
||||
largeVideoState.updateInProgress = true;
|
||||
|
||||
var doUpdate = function () {
|
||||
|
||||
Avatar.updateActiveSpeakerAvatarSrc(
|
||||
APP.xmpp.findJidFromResource(
|
||||
largeVideoState.userResourceJid));
|
||||
@@ -1137,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
|
||||
@@ -1207,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
|
||||
@@ -1236,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();
|
||||
}
|
||||
|
||||
@@ -1619,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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
40
modules/URLProcessor/URLProcessor.js
Normal file
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;
|
||||
@@ -237,7 +237,14 @@ 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;
|
||||
}
|
||||
@@ -341,7 +348,7 @@ module.exports = {
|
||||
);
|
||||
newStreamCreated(stream);
|
||||
},
|
||||
getSwitchStreamFailed);
|
||||
getDesktopStreamFailed);
|
||||
} else {
|
||||
// Disable screen stream
|
||||
APP.RTC.getUserMediaWithConstraints(
|
||||
@@ -351,7 +358,7 @@ module.exports = {
|
||||
isUsingScreenStream = false;
|
||||
newStreamCreated(stream);
|
||||
},
|
||||
getSwitchStreamFailed, config.resolution || '360'
|
||||
getVideoStreamFailed, config.resolution || '360'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
129
modules/members/MemberList.js
Normal file
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;
|
||||
@@ -260,7 +260,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if(!config.disableStats) {
|
||||
if(!config.disableStats && !navigator.mozGetUserMedia) {
|
||||
this.statsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -294,7 +294,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logStats) {
|
||||
if (config.logStats && !navigator.mozGetUserMedia) {
|
||||
this.gatherStatsIntervalId = setInterval(
|
||||
function () {
|
||||
self.peerconnection.getStats(
|
||||
@@ -696,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;
|
||||
}
|
||||
|
||||
|
||||
97
modules/transform/transform.js
Normal file
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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
@@ -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) {
|
||||
@@ -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.
|
||||
*
|
||||
@@ -1111,13 +1128,23 @@ JingleSession.prototype.setVideoMute = function (mute, callback, options) {
|
||||
|
||||
this.hardMuteVideo(mute);
|
||||
|
||||
this.modifySources(callback(mute));
|
||||
};
|
||||
var self = this;
|
||||
var oldSdp = null;
|
||||
if(self.peerconnection) {
|
||||
if(self.peerconnection.localDescription) {
|
||||
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -179,7 +179,7 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
|
||||
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.toPlanA(description);
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
} else {
|
||||
description = APP.simulcast.transformLocalDescription(description);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ TraceablePeerConnection.prototype.setRemoteDescription = function (description,
|
||||
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.toPlanA(description);
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
}
|
||||
else {
|
||||
description = APP.simulcast.transformRemoteDescription(description);
|
||||
|
||||
@@ -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,11 +177,18 @@ 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();
|
||||
|
||||
@@ -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
|
||||
@@ -243,6 +278,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
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];
|
||||
@@ -386,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']});
|
||||
|
||||
@@ -422,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();
|
||||
@@ -469,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();
|
||||
@@ -485,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) {
|
||||
@@ -546,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';
|
||||
},
|
||||
@@ -557,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);
|
||||
|
||||
@@ -566,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) {
|
||||
@@ -590,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');
|
||||
|
||||
@@ -601,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);
|
||||
|
||||
@@ -3,58 +3,151 @@ 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) {
|
||||
connection = XMPP.createConnection();
|
||||
Moderator.setConnection(connection);
|
||||
|
||||
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});
|
||||
}
|
||||
var faultTolerantConnect = retry.operation({
|
||||
retries: 3
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
// fault tolerant connect
|
||||
faultTolerantConnect.attempt(function () {
|
||||
|
||||
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 if (status === Strophe.Status.DISCONNECTED) {
|
||||
if(anonymousConnectionFailed) {
|
||||
// prompt user for username and password
|
||||
XMPP.promptLogin();
|
||||
}
|
||||
} else if (status === Strophe.Status.AUTHFAIL) {
|
||||
// wrong password or username, prompt user
|
||||
XMPP.promptLogin();
|
||||
connection = XMPP.createConnection();
|
||||
Moderator.setConnection(connection);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,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({
|
||||
@@ -104,27 +205,45 @@ 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,
|
||||
|
||||
/**
|
||||
@@ -202,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();
|
||||
}
|
||||
@@ -241,48 +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.warn("No conference handler or conference not started yet");
|
||||
callback();
|
||||
}
|
||||
},
|
||||
setVideoMute: function (mute, callback, options) {
|
||||
if(!connection || !APP.RTC.localVideo)
|
||||
sendVideoInfoPresence: function (mute) {
|
||||
if(!connection)
|
||||
return;
|
||||
|
||||
connection.emuc.addVideoInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
},
|
||||
setVideoMute: function (mute, callback, options) {
|
||||
if(!connection)
|
||||
return;
|
||||
var self = this;
|
||||
var localCallback = function (mute) {
|
||||
connection.emuc.addVideoInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
self.sendVideoInfoPresence(mute);
|
||||
return callback(mute);
|
||||
};
|
||||
|
||||
if (mute == APP.RTC.localVideo.isMuted())
|
||||
if(connection.jingle.activecall)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
} else {
|
||||
APP.RTC.localVideo.setMute(!mute);
|
||||
if(connection.jingle.activecall)
|
||||
{
|
||||
connection.jingle.activecall.setVideoMute(
|
||||
mute, localCallback, options);
|
||||
}
|
||||
else {
|
||||
localCallback(mute);
|
||||
}
|
||||
|
||||
connection.jingle.activecall.setVideoMute(
|
||||
mute, localCallback, options);
|
||||
}
|
||||
else {
|
||||
localCallback(mute);
|
||||
}
|
||||
|
||||
},
|
||||
setAudioMute: function (mute, callback) {
|
||||
if (!(connection && APP.RTC.localAudio)) {
|
||||
@@ -305,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;
|
||||
},
|
||||
@@ -379,11 +499,21 @@ 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();
|
||||
},
|
||||
/**
|
||||
@@ -472,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;
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
"events": "*",
|
||||
"pako": "*",
|
||||
"i18next-client": "1.7.7",
|
||||
"sdp-interop": "jitsi/sdp-interop#f65fedfe57a"
|
||||
"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.head.html
Normal file
0
plugin.header.text.html
Normal file
0
plugin.header.text.html
Normal file
0
plugin.welcomepage.footer.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;
|
||||
5
service/members/Events.js
Normal file
5
service/members/Events.js
Normal file
@@ -0,0 +1,5 @@
|
||||
var Events = {
|
||||
DTMF_SUPPORT_CHANGED: "members.dtmf_support_changed"
|
||||
};
|
||||
|
||||
module.exports = Events;
|
||||
@@ -1,21 +1,25 @@
|
||||
var XMPPEvents = {
|
||||
CONFERENCE_CERATED: "xmpp.conferenceCreated.jingle",
|
||||
CONNECTION_FAILED: "xmpp.connection.failed",
|
||||
CONFERENCE_CREATED: "xmpp.conferenceCreated.jingle",
|
||||
CALL_TERMINATED: "xmpp.callterminated.jingle",
|
||||
CALL_INCOMING: "xmpp.callincoming.jingle",
|
||||
DISPOSE_CONFERENCE: "xmpp.dispoce_confernce",
|
||||
DISPOSE_CONFERENCE: "xmpp.dispose_conference",
|
||||
GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
|
||||
KICKED: "xmpp.kicked",
|
||||
BRIDGE_DOWN: "xmpp.bridge_down",
|
||||
USER_ID_CHANGED: "xmpp.user_id_changed",
|
||||
CHANGED_STREAMS: "xmpp.changed_streams",
|
||||
STREAMS_CHANGED: "xmpp.streams_changed",
|
||||
// We joined the MUC
|
||||
MUC_JOINED: "xmpp.muc_joined",
|
||||
MUC_ENTER: "xmpp.muc_enter",
|
||||
// A member joined the MUC
|
||||
MUC_MEMBER_JOINED: "xmpp.muc_member_joined",
|
||||
// A member left the MUC
|
||||
MUC_MEMBER_LEFT: "xmpp.muc_member_left",
|
||||
MUC_ROLE_CHANGED: "xmpp.muc_role_changed",
|
||||
MUC_LEFT: "xmpp.muc_left",
|
||||
MUC_DESTROYED: "xmpp.muc_destroyed",
|
||||
DISPLAY_NAME_CHANGED: "xmpp.display_name_changed",
|
||||
REMOTE_STATS: "xmpp.remote_stats",
|
||||
LOCALROLE_CHANGED: "xmpp.localrole_changed",
|
||||
LOCAL_ROLE_CHANGED: "xmpp.localrole_changed",
|
||||
PRESENCE_STATUS: "xmpp.presence_status",
|
||||
RESERVATION_ERROR: "xmpp.room_reservation_error",
|
||||
SUBJECT_CHANGED: "xmpp.subject_changed",
|
||||
@@ -24,6 +28,8 @@ var XMPPEvents = {
|
||||
PASSWORD_REQUIRED: "xmpp.password_required",
|
||||
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
||||
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
||||
ETHERPAD: "xmpp.etherpad"
|
||||
ETHERPAD: "xmpp.etherpad",
|
||||
DEVICE_AVAILABLE: "xmpp.device_available",
|
||||
START_MUTED: "xmpp.start_muted"
|
||||
};
|
||||
module.exports = XMPPEvents;
|
||||
Reference in New Issue
Block a user