mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-09 00:00:19 +00:00
Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2b43843b7 | ||
|
|
2f03a0a7fe | ||
|
|
4c2f0d3600 | ||
|
|
a8a0945d73 | ||
|
|
a7048fba06 | ||
|
|
7b35dd89bb | ||
|
|
3561204bb5 | ||
|
|
ee50d07dc3 | ||
|
|
9ec4bc91fc | ||
|
|
88071e5258 | ||
|
|
e79d476d89 | ||
|
|
0fe4999beb | ||
|
|
ae96b9f365 | ||
|
|
922d0bd512 | ||
|
|
9a7bc4ebab | ||
|
|
2081757ba1 | ||
|
|
e9c9fc5e69 | ||
|
|
562761196d | ||
|
|
420514b921 | ||
|
|
eb63b24a9a | ||
|
|
c8bbded994 | ||
|
|
2a2702c13a | ||
|
|
5fc868ee96 | ||
|
|
502eab7278 | ||
|
|
332aafbe20 | ||
|
|
d5258e6197 | ||
|
|
9cc9e6132c | ||
|
|
f60c1d9751 | ||
|
|
5d32318d93 | ||
|
|
fee8482bae | ||
|
|
f2b5cdbfb8 | ||
|
|
60afe2d202 | ||
|
|
18f03e296b | ||
|
|
5cd9db1b6a | ||
|
|
f83404a99e | ||
|
|
7c1ba9242b | ||
|
|
bfcc587047 | ||
|
|
e90d8f5531 | ||
|
|
59033aab28 | ||
|
|
7f1eb617c3 | ||
|
|
fd7e8c9162 | ||
|
|
51e886142b | ||
|
|
dcc206b2b4 | ||
|
|
da75e17ff5 | ||
|
|
8fea9b76ee | ||
|
|
cb024be2d6 | ||
|
|
4c4e99c51a | ||
|
|
4b8bc398dd | ||
|
|
466e7dcc91 | ||
|
|
de30ce0f5c | ||
|
|
fc6f5717cb | ||
|
|
b680ecd2ff | ||
|
|
2bea2eec74 | ||
|
|
f52b1380ee | ||
|
|
baf720c553 | ||
|
|
deaff6af5b | ||
|
|
6ca1e131af | ||
|
|
57b9aeb38c | ||
|
|
cc20a4d776 | ||
|
|
fd404b8465 | ||
|
|
cc29df6376 | ||
|
|
44136e8a55 | ||
|
|
fb875423a9 | ||
|
|
ab4c29eddc | ||
|
|
95e964a089 | ||
|
|
c288aa6e84 | ||
|
|
e5d03d1d11 | ||
|
|
59147f059d | ||
|
|
7793d65a99 | ||
|
|
b77791f4b2 | ||
|
|
4092d67853 | ||
|
|
2ea6be9b2c | ||
|
|
74e7507a73 | ||
|
|
9a31fa3d63 | ||
|
|
fd44cfa7a0 | ||
|
|
ab570d63fa | ||
|
|
b4983b2566 | ||
|
|
fdb470d22f | ||
|
|
c163a22415 | ||
|
|
1dea41d3d4 | ||
|
|
9d321df49e | ||
|
|
d92d8e8299 | ||
|
|
6b48bf0d84 | ||
|
|
de82a8e32b | ||
|
|
fe4661078e | ||
|
|
46554f75a2 | ||
|
|
dbd68d2daa | ||
|
|
67a52e6f72 | ||
|
|
2c790f86ad | ||
|
|
fd5a739f3c | ||
|
|
35e46a2cfa | ||
|
|
24f9a1c8d1 | ||
|
|
71229bdba9 | ||
|
|
84a8d00234 | ||
|
|
ee95e99f57 | ||
|
|
be6d7af377 | ||
|
|
b8548757b4 | ||
|
|
df932bb89f | ||
|
|
50e67a0658 | ||
|
|
3289f6f68e | ||
|
|
a701821698 | ||
|
|
5588bcd167 | ||
|
|
a7058747ac | ||
|
|
5017fed28e | ||
|
|
8469a282c1 | ||
|
|
2ffe178456 | ||
|
|
349f196664 | ||
|
|
f2bd76ac93 | ||
|
|
baee96734c | ||
|
|
4cac7ac97f | ||
|
|
46a17948d0 | ||
|
|
79ac1e800f | ||
|
|
b0c81985d4 | ||
|
|
f8b7d048b0 | ||
|
|
dc19620edc | ||
|
|
4aa6fbc4b9 | ||
|
|
03902de511 | ||
|
|
d44aed2c11 | ||
|
|
9bbf17e541 | ||
|
|
156b218bfc | ||
|
|
ca5a1001a1 | ||
|
|
1010a56899 | ||
|
|
0902cbb164 | ||
|
|
9b4ee7c0c5 | ||
|
|
2545441def | ||
|
|
9206b470ef | ||
|
|
cb7ff545b6 | ||
|
|
c149b22ac2 | ||
|
|
2d522f735e | ||
|
|
bc935eb5dc | ||
|
|
c3548eb866 | ||
|
|
c1226d8c07 | ||
|
|
dd8371f49c | ||
|
|
0c3802183d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ node_modules
|
||||
.idea/
|
||||
*.iml
|
||||
.*.tmp
|
||||
deploy-local.sh
|
||||
|
||||
7
Makefile
7
Makefile
@@ -18,7 +18,10 @@ app:
|
||||
$(NPM) update && $(BROWSERIFY) $(FLAGS) app.js -s APP -o $(OUTPUT_DIR)/app.bundle.js
|
||||
|
||||
clean:
|
||||
@rm -f $(OUTPUT_DIR)/*.bundle.js
|
||||
rm -f $(OUTPUT_DIR)/*.bundle.js
|
||||
|
||||
deploy:
|
||||
@mkdir -p $(DEPLOY_DIR) && cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR) && ./bump-js-versions.sh
|
||||
mkdir -p $(DEPLOY_DIR) && \
|
||||
cp $(OUTPUT_DIR)/*.bundle.js $(DEPLOY_DIR) && \
|
||||
./bump-js-versions.sh && \
|
||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Jitsi Meet - Secure, Simple and Scalable Video Conferences
|
||||
====
|
||||
Jitsi Meet is an open-source (MIT) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the 482 session of the VoIP Users Conference.
|
||||
Jitsi Meet is an open-source (Apache) WebRTC JavaScript application that uses [Jitsi Videobridge](https://jitsi.org/videobridge) to provide high quality, scalable video conferences. You can see [Jitsi Meet in action](http://youtu.be/7vFUVClsNh0) here at the session #482 of the VoIP Users Conference.
|
||||
|
||||
You can also try it out yourself at https://meet.jit.si .
|
||||
|
||||
@@ -30,7 +30,7 @@ make
|
||||
```
|
||||
|
||||
## Discuss
|
||||
Please use the [Jitsi dev mailing list](http://lists.jitsi.org/pipermail/dev/) to discuss feature requests before opening an issue on github.
|
||||
Please use the [Jitsi dev mailing list](http://lists.jitsi.org/pipermail/dev/) to discuss feature requests before opening an issue on Github.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
@@ -38,7 +38,7 @@ Jitsi Meet started out as a sample conferencing application using Jitsi Videobri
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
This project was originally contributed to the community under the MIT licesne and with the following notice:
|
||||
This project was originally contributed to the community under the MIT license and with the following notice:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
||||
35
app.js
35
app.js
@@ -16,6 +16,7 @@ var APP =
|
||||
this.settings = require("./modules/settings/Settings");
|
||||
this.DTMF = require("./modules/DTMF/DTMF");
|
||||
this.members = require("./modules/members/MemberList");
|
||||
this.configFetch = require("./modules/config/HttpConfigFetch");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,11 +31,39 @@ function init() {
|
||||
APP.members.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have HTTP endpoint for getting confgi.json configured we're going to
|
||||
* read it and override properties from config.js and interfaceConfig.js.
|
||||
* If there is no endpoint we'll just continue with initialization.
|
||||
* Keep in mind that if the endpoint has been configured and we fail to obtain
|
||||
* the config for any reason then the conference won't start and error message
|
||||
* will be displayed to the user.
|
||||
*/
|
||||
function obtainConfigAndInit() {
|
||||
if (config.configLocation) {
|
||||
APP.configFetch.obtainConfig(
|
||||
config.configLocation, APP.UI.getRoomNode(),
|
||||
// Get config result callback
|
||||
function(success, error) {
|
||||
if (success) {
|
||||
init();
|
||||
} else {
|
||||
// Show obtain config error,
|
||||
// pass the error object for report
|
||||
APP.UI.messageHandler.openReportDialog(
|
||||
null, "dialog.connectError", error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
var URLPRocessor = require("./modules/URLProcessor/URLProcessor");
|
||||
URLPRocessor.setConfigParametersFromUrl();
|
||||
var URLProcessor = require("./modules/config/URLProcessor");
|
||||
URLProcessor.setConfigParametersFromUrl();
|
||||
APP.init();
|
||||
|
||||
APP.translation.init();
|
||||
@@ -42,7 +71,7 @@ $(document).ready(function () {
|
||||
if(APP.API.isEnabled())
|
||||
APP.API.init();
|
||||
|
||||
APP.UI.start(init);
|
||||
APP.UI.start(obtainConfigAndInit);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var config = {
|
||||
// configLocation: './config.json', // see ./modules/HttpConfigFetch.js
|
||||
hosts: {
|
||||
domain: 'jitsi-meet.example.com',
|
||||
//anonymousdomain: 'guest.example.com',
|
||||
@@ -26,8 +27,6 @@ var config = {
|
||||
channelLastN: -1, // The default value of the channel attribute last-n.
|
||||
adaptiveLastN: false,
|
||||
adaptiveSimulcast: false,
|
||||
useRtcpMux: true, // required for FF support
|
||||
useBundle: true, // required for FF support
|
||||
enableRecording: false,
|
||||
enableWelcomePage: true,
|
||||
enableSimulcast: false, // blocks FF support
|
||||
|
||||
36
css/main.css
36
css/main.css
@@ -38,17 +38,30 @@ html, body{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#toolbar a.button::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: #373737;
|
||||
}
|
||||
|
||||
#toolbar a.button:last-child::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
color: #FFFFFF;
|
||||
top: 0px;
|
||||
padding: 10px 0px;
|
||||
top: 0;
|
||||
padding: 10px 0;
|
||||
width: 38px;
|
||||
cursor: pointer;
|
||||
font-size: 11pt;
|
||||
text-align: center;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.6);
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.3), 0 -1px 0 rgba(0,0,0,.6);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -61,13 +74,13 @@ html, body{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#chatButton, #chatBottomButton, #contactListButton, #numberOfParticipants {
|
||||
#toolbar_button_chat, #chatBottomButton, #contactListButton, #numberOfParticipants {
|
||||
-webkit-transition: all .5s ease-in-out;
|
||||
-moz-transition: all .5s ease-in-out;
|
||||
transition: all .5s ease-in-out;
|
||||
}
|
||||
/*#ffde00*/
|
||||
#chatButton.active, #contactListButton.glowing, #chatBottomButton.glowing {
|
||||
#toolbar_button_chat.active, #contactListButton.glowing, #chatBottomButton.glowing {
|
||||
-webkit-text-shadow: -1px 0 10px #00ccff,
|
||||
0 1px 10px #00ccff,
|
||||
1px 0 10px #00ccff,
|
||||
@@ -82,6 +95,11 @@ html, body{
|
||||
0 -1px 10px #00ccff;
|
||||
}
|
||||
|
||||
#toolbar_button_hangup {
|
||||
color: #ff0000;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
#numberOfParticipants {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
@@ -100,13 +118,13 @@ html, body{
|
||||
color: #00ccff;
|
||||
}
|
||||
|
||||
#recordButton {
|
||||
#toolbar_button_record {
|
||||
-webkit-transition: all .5s ease-in-out;
|
||||
-moz-transition: all .5s ease-in-out;
|
||||
transition: all .5s ease-in-out;
|
||||
}
|
||||
/*#ffde00*/
|
||||
#recordButton.active {
|
||||
#toolbar_button_record.active {
|
||||
-webkit-text-shadow: -1px 0 10px #00ccff,
|
||||
0 1px 10px #00ccff,
|
||||
1px 0 10px #00ccff,
|
||||
@@ -155,6 +173,8 @@ a.bottomToolbarButton:hover {
|
||||
}
|
||||
|
||||
input[type='text'], input[type='password'], textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
|
||||
@@ -101,12 +101,14 @@
|
||||
}
|
||||
|
||||
#largeVideo,
|
||||
#largeVideoWrapper,
|
||||
#largeVideoContainer {
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#largeVideo
|
||||
#largeVideo,
|
||||
#largeVideoWrapper
|
||||
{
|
||||
object-fit: cover;
|
||||
}
|
||||
@@ -116,6 +118,9 @@
|
||||
#localVideoWrapper>video,
|
||||
#localVideoWrapper>object,
|
||||
#localVideoWrapper,
|
||||
#largeVideoWrapper,
|
||||
#largeVideoWrapper>video,
|
||||
#largeVideoWrapper>object,
|
||||
.videocontainer>video,
|
||||
.videocontainer>object {
|
||||
position: absolute;
|
||||
@@ -135,7 +140,7 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#etherpadButton {
|
||||
#toolbar_button_etherpad {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -446,3 +451,29 @@
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.videoMessageFilter {
|
||||
-webkit-filter: grayscale(.5) opacity(0.8);
|
||||
filter: grayscale(.5) opacity(0.8);
|
||||
}
|
||||
|
||||
.videoProblemFilter {
|
||||
-webkit-filter: blur(10px) grayscale(.5) opacity(0.8);
|
||||
filter: blur(10px) grayscale(.5) opacity(0.8);
|
||||
}
|
||||
|
||||
#videoConnectionMessage {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top:50%;
|
||||
z-index: 10000;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
opacity: .80;
|
||||
text-shadow: 0px 0px 1px rgba(0,0,0,0.3),
|
||||
0px 1px 1px rgba(0,0,0,0.3),
|
||||
1px 0px 1px rgba(0,0,0,0.3),
|
||||
0px 0px 1px rgba(0,0,0,0.3);
|
||||
}
|
||||
10
debian/control
vendored
10
debian/control
vendored
@@ -10,14 +10,15 @@ Homepage: https://jitsi.org/meet
|
||||
|
||||
Package: jitsi-meet
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, jitsi-videobridge, nginx, jitsi-meet-prosody, libjs-strophe (>= 1.1.3),
|
||||
libjs-jquery, libjs-jquery-ui
|
||||
Depends: ${misc:Depends}, jitsi-videobridge, jitsi-meet-prosody, libjs-strophe (>= 1.1.3),
|
||||
libjs-jquery, libjs-jquery-ui, openjdk-8-jre-headless | nginx
|
||||
Description: WebRTC JavaScript video conferences
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with nginx
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
@@ -27,7 +28,8 @@ Description: Prosody configuration for Jitsi Meet
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with nginx
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge
|
||||
.
|
||||
This package contains configuration for Prosody to be used with
|
||||
Jitsi Meet.
|
||||
|
||||
1
debian/jitsi-meet-prosody.templates
vendored
1
debian/jitsi-meet-prosody.templates
vendored
@@ -1,5 +1,4 @@
|
||||
Template: jitsi-meet-prosody/jvb-hostname
|
||||
Type: string
|
||||
Default: ${default-key}
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
|
||||
162
debian/jitsi-meet.postinst
vendored
162
debian/jitsi-meet.postinst
vendored
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
# postinst script for jitsi-meet
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
@@ -20,67 +20,159 @@ set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
|
||||
. /etc/jitsi/videobridge/config
|
||||
JVB_ETC_CONFIG="/etc/jitsi/videobridge/config"
|
||||
JVB_CONFIG="/usr/share/jitsi-videobridge/.sip-communicator/sip-communicator.properties"
|
||||
|
||||
. $JVB_ETC_CONFIG
|
||||
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
# detect dpkg-reconfigure
|
||||
RECONFIGURING="false"
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
|
||||
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
|
||||
RECONFIGURING="true"
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
|
||||
fi
|
||||
|
||||
JVB_SERVE="false"
|
||||
db_get jitsi-meet/jvb-serve
|
||||
if [ -n "$RET" ] && [ "$RET" = "true" ] ; then
|
||||
JVB_SERVE="true"
|
||||
fi
|
||||
|
||||
# stores the hostname so we will reuse it later, like in purge
|
||||
db_set jitsi-meet/jvb-hostname $JVB_HOSTNAME
|
||||
|
||||
# nginx conf
|
||||
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ]; then
|
||||
cp /usr/share/doc/jitsi-meet/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ]; then
|
||||
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
NGINX_INSTALL_CHECK="$(dpkg-query -W -f '${PackageSpec}:${Status}\n' nginx 2>&1 | grep -v "ok installed" || :)"
|
||||
if [ -z "${NGINX_INSTALL_CHECK}" ]; then
|
||||
FORCE_NGINX="true"
|
||||
fi
|
||||
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
if [ "$CERT_CHOICE" = 'A certificate is available and the files are uploaded on the server' ]; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
db_input critical jitsi-meet/cert-path-key || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_set jitsi-meet/cert-path-crt "/etc/ssl/$JVB_HOSTNAME.crt"
|
||||
db_input critical jitsi-meet/cert-path-crt || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate_key\ \/var\/lib\/prosody\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate\ \/var\/lib\/prosody\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
UPLOADED_CERT_CHOICE="A certificate is available and the files are uploaded on the server"
|
||||
|
||||
# jitsi meet
|
||||
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
|
||||
if [ ! -f $JITSI_MEET_CONFIG ]; then
|
||||
if [ ! -f $JITSI_MEET_CONFIG ] ; then
|
||||
cp /usr/share/doc/jitsi-meet/config.js $JITSI_MEET_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
# this is new install let's configure jvb to serve meet
|
||||
if [[ -z $FORCE_NGINX && ( -z $JVB_HOSTNAME_OLD || "$JVB_SERVE" = "true" ) ]] ; then
|
||||
# this is a reconfigure, lets just delete old links
|
||||
if [ "$RECONFIGURING" = "true" ] ; then
|
||||
rm -f $JVB_CONFIG
|
||||
fi
|
||||
|
||||
# configure jvb
|
||||
echo "AUTHBIND=yes" >> $JVB_ETC_CONFIG
|
||||
sed -i "s/JVB_OPTS=.*/JVB_OPTS=--apis=rest,xmpp/g" $JVB_ETC_CONFIG
|
||||
|
||||
echo "org.jitsi.videobridge.rest.jetty.host=::" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.port=443" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.hostHeader=$JVB_HOSTNAME" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.pathSpec=/http-bind" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ProxyServlet.proxyTo=http://localhost:5280/http-bind" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.resourceBase=/usr/share/jitsi-meet" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.ResourceHandler.alias./config.js=/etc/jitsi/meet/$JVB_HOSTNAME-config.js" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.regex=^/([a-zA-Z0-9]+)$" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.RewriteHandler.replacement=/" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.tls.port=443" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.TCP_HARVESTER_PORT=443" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePath=/etc/jitsi/videobridge/$JVB_HOSTNAME.jks" >> $JVB_CONFIG
|
||||
echo "org.jitsi.videobridge.rest.jetty.sslContextFactory.keyStorePassword=changeit" >> $JVB_CONFIG
|
||||
|
||||
# configure authbind to allow jvb to bind to privileged ports
|
||||
OWNER=$(stat -c '%U' /usr/share/jitsi-videobridge)
|
||||
GROUP=$(stat -c '%G' /usr/share/jitsi-videobridge)
|
||||
JVB_UID="`id -u $OWNER`"
|
||||
if [ ! -f "/etc/authbind/byuid/$JVB_UID" ] ; then
|
||||
if [ ! -d "/etc/authbind/byuid" ] ; then
|
||||
mkdir -p /etc/authbind/byuid
|
||||
chmod 755 /etc/authbind
|
||||
chmod 755 /etc/authbind/byuid
|
||||
fi
|
||||
echo '::,443' >/etc/authbind/byuid/$JVB_UID
|
||||
chown $OWNER:$GROUP /etc/authbind/byuid/$JVB_UID
|
||||
chmod 700 /etc/authbind/byuid/$JVB_UID
|
||||
fi
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
# create jks from uploaded certs
|
||||
openssl pkcs12 -export \
|
||||
-in /etc/ssl/$JVB_HOSTNAME.crt \
|
||||
-inkey /etc/ssl/$JVB_HOSTNAME.key \
|
||||
-passout pass:changeit > /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
|
||||
keytool -importkeystore \
|
||||
-srckeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.p12 \
|
||||
-destkeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.jks \
|
||||
-srcstoretype pkcs12 \
|
||||
-noprompt -storepass changeit -srcstorepass changeit
|
||||
else
|
||||
# create jks from self-signed certs
|
||||
openssl pkcs12 -export \
|
||||
-in /var/lib/prosody/$JVB_HOSTNAME.crt \
|
||||
-inkey /var/lib/prosody/$JVB_HOSTNAME.key \
|
||||
-passout pass:changeit > /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
|
||||
keytool -importkeystore \
|
||||
-srckeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.p12 \
|
||||
-destkeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.jks \
|
||||
-srcstoretype pkcs12 \
|
||||
-noprompt -storepass changeit -srcstorepass changeit
|
||||
fi
|
||||
|
||||
db_set jitsi-meet/jvb-serve "true"
|
||||
|
||||
invoke-rc.d jitsi-videobridge restart
|
||||
elif [[ "$FORCE_NGINX" = "true" || ( -n $JVB_HOSTNAME_OLD && "$JVB_SERVE" = "false" ) ]] ; then
|
||||
# this is a reconfigure, lets just delete old links
|
||||
if [ "$RECONFIGURING" = "true" ] ; then
|
||||
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
|
||||
fi
|
||||
|
||||
# nginx conf
|
||||
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ] ; then
|
||||
cp /usr/share/doc/jitsi-meet/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] ; then
|
||||
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
db_input critical jitsi-meet/cert-path-key || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_set jitsi-meet/cert-path-crt "/etc/ssl/$JVB_HOSTNAME.crt"
|
||||
db_input critical jitsi-meet/cert-path-crt || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate_key\ \/var\/lib\/prosody\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate\ \/var\/lib\/prosody\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
invoke-rc.d nginx reload
|
||||
fi
|
||||
|
||||
# and we're done with debconf
|
||||
db_stop
|
||||
|
||||
invoke-rc.d nginx reload
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
|
||||
9
debian/jitsi-meet.templates
vendored
9
debian/jitsi-meet.templates
vendored
@@ -10,20 +10,23 @@ _Description: SSL certificate for the Jitsi Meet instance
|
||||
|
||||
Template: jitsi-meet/cert-path-key
|
||||
Type: string
|
||||
Default: ${default-key}
|
||||
_Description: Full local server path to the SSL key file:
|
||||
The full path to the SSL key file on the server.
|
||||
If it has not been uploaded, now is a good time to do so.
|
||||
|
||||
Template: jitsi-meet/cert-path-crt
|
||||
Type: string
|
||||
Default: ${default-crt}
|
||||
_Description: Full local server path to the SSL certificate file:
|
||||
The full path to the SSL certificate file on the server.
|
||||
If you haven't uploaded it, now is a good time to upload it in another console.
|
||||
|
||||
Template: jitsi-meet/jvb-hostname
|
||||
Type: string
|
||||
Default: ${default-key}
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
|
||||
Template: jitsi-meet/jvb-serve
|
||||
Type: boolean
|
||||
Default: false
|
||||
_Description: for internal use
|
||||
for internal use.
|
||||
|
||||
10
debian/patches/jquery-package
vendored
10
debian/patches/jquery-package
vendored
@@ -3,14 +3,16 @@ Index: jitsi-meet/index.html
|
||||
===================================================================
|
||||
--- jitsi-meet.orig/index.html
|
||||
+++ jitsi-meet/index.html
|
||||
@@ -9,12 +9,12 @@
|
||||
<meta itemprop="name" content="Jitsi Meet"/>
|
||||
@@ -10,14 +10,14 @@
|
||||
<meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
|
||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||
<script src="https://api.callstats.io/static/callstats.min.js"></script>
|
||||
- <script src="libs/jquery-2.1.1.min.js"></script>
|
||||
+ <script src="libs/jquery.min.js"></script>
|
||||
<script src="config.js?v=9"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
||||
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/1.5.0/sha.js"></script>
|
||||
<script src="config.js?v=11"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=2"></script>
|
||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||
- <script src="libs/jquery-ui.js"></script>
|
||||
|
||||
19
debian/po/templates.pot
vendored
19
debian/po/templates.pot
vendored
@@ -1,7 +1,3 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
@@ -74,7 +70,6 @@ msgid ""
|
||||
"uploaded it, now is a good time to upload it in another console."
|
||||
msgstr ""
|
||||
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:4001
|
||||
@@ -87,3 +82,17 @@ msgstr ""
|
||||
msgid ""
|
||||
"The value for the hostname that is set in Jitsi Videobridge installation."
|
||||
msgstr ""
|
||||
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:5001
|
||||
msgid "for internal use"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:5001
|
||||
msgid ""
|
||||
"Jitsi Videobridge installation can use its internal jetty to serve static meet pages."
|
||||
msgstr ""
|
||||
|
||||
19
doc/api.md
19
doc/api.md
@@ -29,6 +29,13 @@ constructor.
|
||||
```
|
||||
If you don't specify room the user will enter in new conference with random room name.
|
||||
|
||||
You can overwrite options set in config.js and interface_config.js. For example, to enable the film-strip-only interface mode and disable simulcast, you can use:
|
||||
```javascript
|
||||
var configOverwrite = {enableSimulcast: false};
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, true, configOverwrite, interfaceConfigOverwrite);
|
||||
```
|
||||
|
||||
Controlling embedded Jitsi Meet Conference
|
||||
=========
|
||||
|
||||
@@ -49,13 +56,13 @@ the new display name to be set
|
||||
```
|
||||
api.executeCommand('displayName', ['New Nickname']);
|
||||
```
|
||||
* **muteAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
* **toggleAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```
|
||||
api.executeCommand('muteAudio', [])
|
||||
api.executeCommand('toggleAudio', [])
|
||||
```
|
||||
* **muteVideo** - mutes / unmutes the video for the local participant. No arguments are required.
|
||||
* **toggleVideo** - mutes / unmutes the video for the local participant. No arguments are required.
|
||||
```
|
||||
api.executeCommand('muteVideo', [])
|
||||
api.executeCommand('toggleVideo', [])
|
||||
```
|
||||
* **toggleFilmStrip** - hides / shows the film strip. No arguments are required.
|
||||
```
|
||||
@@ -78,7 +85,7 @@ The ```commands``` parameter is object with keys the names of the commands and v
|
||||
commands.
|
||||
|
||||
```
|
||||
api.executeCommands({displayName: ['nickname'], muteAudio: []});
|
||||
api.executeCommands({displayName: ['nickname'], toggleAudio: []});
|
||||
```
|
||||
|
||||
You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method.
|
||||
@@ -166,4 +173,4 @@ You can remove the embedded Jitsi Meet Conference with the following code:
|
||||
api.dispose()
|
||||
```
|
||||
|
||||
It is a good practice to remove the conference before the page is unloaded.
|
||||
It is a good practice to remove the conference before the page is unloaded.
|
||||
|
||||
@@ -150,7 +150,7 @@ ant dist.{os-name}
|
||||
Run jicofo:
|
||||
```sh
|
||||
cd dist/{os-name}'
|
||||
./jicofo.sh --domain=jitsi.exmaple.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
./jicofo.sh --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3
|
||||
```
|
||||
|
||||
## Deploy Jitsi Meet
|
||||
@@ -220,4 +220,4 @@ enableRecording: true
|
||||
|
||||
Restart jitsi-videobridge and start a new conference (making sure that the page
|
||||
is reloaded with the new config.js) -- the organizer of the conference should
|
||||
now have a "recoriding" button in the floating menu, near the "mute" button.
|
||||
now have a "recording" button in the floating menu, near the "mute" button.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Jitsi Meet quick install
|
||||
|
||||
This documents decribes the needed steps for quick Jitsi Meet installation on a Debian based GNU/Linux system.
|
||||
This document describes the required steps for a quick Jitsi Meet installation on a Debian based GNU/Linux system.
|
||||
|
||||
N.B.: All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands.
|
||||
|
||||
@@ -24,13 +24,13 @@ apt-get update
|
||||
apt-get -y install jitsi-meet
|
||||
```
|
||||
|
||||
During the installation you'll be asked to enter the hostname of the Jitsi Meet instance. If you have a FQDN hostname for the instance already set ip in DNS, enter it there. If you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change).
|
||||
During the installation, you will be asked to enter the hostname of the Jitsi Meet instance. If you have a FQDN hostname for the instance already set up in DNS, enter it there. If you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change).
|
||||
|
||||
This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also you and your correspondents will be using it to access the web conferences.
|
||||
This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences.
|
||||
|
||||
### Open a conference
|
||||
|
||||
Launch a web broswer (Chrome, Chromium or latest Opera) and enter in the URL bar the hostname (or IP address) you used in the previous step.
|
||||
Launch a web browser (Chrome, Chromium or latest Opera) and enter in the URL bar the hostname (or IP address) you used in the previous step.
|
||||
|
||||
Confirm that you trust the self-signed certificate of the newly installed Jitsi Meet.
|
||||
|
||||
@@ -50,7 +50,7 @@ wget https://download.jitsi.org/jigasi_1.0-1_amd64.deb
|
||||
dpkg -i jigasi_1.0-1_amd64.deb
|
||||
```
|
||||
|
||||
During the installation you'll be asked to enter your SIP account and password. This account will be used to invite the other SIP participants.
|
||||
During the installation, you will be asked to enter your SIP account and password. This account will be used to invite the other SIP participants.
|
||||
|
||||
### Reload Jitsi Meet
|
||||
|
||||
@@ -58,19 +58,17 @@ Launch again a browser with the Jitsi Meet URL and you'll see a telephone icon o
|
||||
|
||||
Enjoy!
|
||||
|
||||
## Deinstall
|
||||
## Uninstall
|
||||
|
||||
```sh
|
||||
apt-get purge jigasi jitsi-meet jicofo jitsi-videobridge
|
||||
```
|
||||
|
||||
Somethimes the following packages will fail to uninstall properly:
|
||||
Sometimes the following packages will fail to uninstall properly:
|
||||
|
||||
- jigasi
|
||||
- jitsi-videobridge
|
||||
|
||||
When this happens, just run the deinstall command a second time and it should be ok.
|
||||
When this happens, just run the uninstall command a second time and it should be ok.
|
||||
|
||||
The reason for failure is that not allways the daemons are stopped right away, there is a timeout before the actual stop. And if the unistall script goes on before the services' stop, there is an error.
|
||||
|
||||
The second run of the deinstall command fixes this, as by then the jigasi or jvb daemons are already stopped.
|
||||
The reason for failure is that sometimes, the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jvb daemons are already stopped.
|
||||
|
||||
119
external_api.js
119
external_api.js
@@ -23,17 +23,21 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param width width of the iframe
|
||||
* @param height height of the iframe
|
||||
* @param parent_node the node that will contain the iframe
|
||||
* @param filmStripOnly if the value is true only the small videos will be
|
||||
* visible.
|
||||
* @constructor
|
||||
*/
|
||||
function JitsiMeetExternalAPI(domain, room_name, width, height, parent_node)
|
||||
{
|
||||
function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode,
|
||||
configOverwrite, interfaceConfigOverwrite) {
|
||||
if((!width || width < MIN_WIDTH) && !filmStripOnly)
|
||||
width = MIN_WIDTH;
|
||||
if((!height || height < MIN_HEIGHT) && !filmStripOnly)
|
||||
height = MIN_HEIGHT;
|
||||
|
||||
this.parentNode = null;
|
||||
if(parent_node)
|
||||
{
|
||||
this.parentNode = parent_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parentNode) {
|
||||
this.parentNode = parentNode;
|
||||
} else {
|
||||
var scriptTag = document.scripts[document.scripts.length - 1];
|
||||
this.parentNode = scriptTag.parentNode;
|
||||
}
|
||||
@@ -41,17 +45,35 @@ var JitsiMeetExternalAPI = (function()
|
||||
this.iframeHolder =
|
||||
this.parentNode.appendChild(document.createElement("div"));
|
||||
this.iframeHolder.id = "jitsiConference" + JitsiMeetExternalAPI.id;
|
||||
if(width < MIN_WIDTH)
|
||||
width = MIN_WIDTH;
|
||||
if(height < MIN_HEIGHT)
|
||||
height = MIN_HEIGHT;
|
||||
this.iframeHolder.style.width = width + "px";
|
||||
this.iframeHolder.style.height = height + "px";
|
||||
if(width)
|
||||
this.iframeHolder.style.width = width + "px";
|
||||
if(height)
|
||||
this.iframeHolder.style.height = height + "px";
|
||||
this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id;
|
||||
this.url = "//" + domain + "/";
|
||||
if(room_name)
|
||||
this.url += room_name;
|
||||
this.url += "#external";
|
||||
this.url += "#external=true";
|
||||
|
||||
var key;
|
||||
if (configOverwrite) {
|
||||
for (key in configOverwrite) {
|
||||
if (!configOverwrite.hasOwnProperty(key) ||
|
||||
typeof key !== 'string')
|
||||
continue;
|
||||
this.url += "&config." + key + "=" + configOverwrite[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (interfaceConfigOverwrite) {
|
||||
for (key in interfaceConfigOverwrite) {
|
||||
if (!interfaceConfigOverwrite.hasOwnProperty(key) ||
|
||||
typeof key !== 'string')
|
||||
continue;
|
||||
this.url += "&interfaceConfig." + key + "=" + interfaceConfigOverwrite[key];
|
||||
}
|
||||
}
|
||||
|
||||
JitsiMeetExternalAPI.id++;
|
||||
|
||||
this.frame = document.createElement("iframe");
|
||||
@@ -80,15 +102,12 @@ var JitsiMeetExternalAPI = (function()
|
||||
* Sends the passed object to Jitsi Meet
|
||||
* @param object the object to be sent
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.sendMessage = function(object)
|
||||
{
|
||||
if(this.frameLoaded)
|
||||
{
|
||||
JitsiMeetExternalAPI.prototype.sendMessage = function(object) {
|
||||
if (this.frameLoaded) {
|
||||
this.frame.contentWindow.postMessage(
|
||||
JSON.stringify(object), this.frame.src);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
this.initialCommands.push(object);
|
||||
}
|
||||
|
||||
@@ -98,8 +117,8 @@ var JitsiMeetExternalAPI = (function()
|
||||
* Executes command. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* muteAudio - mutes / unmutes audio with no arguments
|
||||
* muteVideo - mutes / unmutes video with no arguments
|
||||
* toggleAudio - mutes / unmutes audio with no arguments
|
||||
* toggleVideo - mutes / unmutes video with no arguments
|
||||
* filmStrip - hides / shows the film strip with no arguments
|
||||
* If the command doesn't require any arguments the parameter should be set
|
||||
* to empty array or it may be omitted.
|
||||
@@ -107,10 +126,9 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param arguments array of arguments
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.executeCommand = function(name,
|
||||
argumentsList)
|
||||
{
|
||||
argumentsList) {
|
||||
var argumentsArray = argumentsList;
|
||||
if(!argumentsArray)
|
||||
if (!argumentsArray)
|
||||
argumentsArray = [];
|
||||
var object = {type: "command", action: "execute"};
|
||||
object[name] = argumentsArray;
|
||||
@@ -121,8 +139,8 @@ var JitsiMeetExternalAPI = (function()
|
||||
* Executes commands. The available commands are:
|
||||
* displayName - sets the display name of the local participant to the value
|
||||
* passed in the arguments array.
|
||||
* muteAudio - mutes / unmutes audio with no arguments
|
||||
* muteVideo - mutes / unmutes video with no arguments
|
||||
* toggleAudio - mutes / unmutes audio with no arguments
|
||||
* toggleVideo - mutes / unmutes video with no arguments
|
||||
* filmStrip - hides / shows the film strip with no arguments
|
||||
* @param object the object with commands to be executed. The keys of the
|
||||
* object are the commands that will be executed and the values are the
|
||||
@@ -135,8 +153,8 @@ var JitsiMeetExternalAPI = (function()
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds event listeners to Meet Jitsi. The object key should be the name of the
|
||||
* event and value - the listener.
|
||||
* Adds event listeners to Meet Jitsi. The object key should be the name of
|
||||
* the event and value - the listener.
|
||||
* Currently we support the following
|
||||
* events:
|
||||
* incomingMessage - receives event notifications about incoming
|
||||
@@ -170,8 +188,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param object
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.addEventListeners
|
||||
= function (object)
|
||||
{
|
||||
= function (object) {
|
||||
|
||||
var message = {type: "event", action: "add", events: []};
|
||||
for(var i in object)
|
||||
@@ -217,8 +234,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param listener the listener
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.addEventListener
|
||||
= function (event, listener)
|
||||
{
|
||||
= function (event, listener) {
|
||||
|
||||
var message = {type: "event", action: "add", events: [event]};
|
||||
this.eventHandlers[event] = listener;
|
||||
@@ -230,8 +246,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param event the name of the event.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.removeEventListener
|
||||
= function (event)
|
||||
{
|
||||
= function (event) {
|
||||
if(!this.eventHandlers[event])
|
||||
{
|
||||
console.error("The event " + event + " is not registered.");
|
||||
@@ -247,8 +262,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
* @param events array with the names of the events.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.removeEventListeners
|
||||
= function (events)
|
||||
{
|
||||
= function (events) {
|
||||
var eventsArray = [];
|
||||
for(var i = 0; i < events.length; i++)
|
||||
{
|
||||
@@ -274,8 +288,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
* Processes message events sent from Jitsi Meet
|
||||
* @param event the event
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.processMessage = function(event)
|
||||
{
|
||||
JitsiMeetExternalAPI.prototype.processMessage = function(event) {
|
||||
var message;
|
||||
try {
|
||||
message = JSON.parse(event.data);
|
||||
@@ -285,18 +298,15 @@ var JitsiMeetExternalAPI = (function()
|
||||
console.error("Message without type is received.");
|
||||
return;
|
||||
}
|
||||
switch (message.type)
|
||||
{
|
||||
switch (message.type) {
|
||||
case "system":
|
||||
if(message.loaded)
|
||||
{
|
||||
if(message.loaded) {
|
||||
this.onFrameLoaded();
|
||||
}
|
||||
break;
|
||||
case "event":
|
||||
if(message.action != "result" ||
|
||||
!message.event || !this.eventHandlers[message.event])
|
||||
{
|
||||
!message.event || !this.eventHandlers[message.event]) {
|
||||
console.warn("The received event cannot be parsed.");
|
||||
return;
|
||||
}
|
||||
@@ -306,8 +316,6 @@ var JitsiMeetExternalAPI = (function()
|
||||
console.error("Unknown message type.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -316,8 +324,7 @@ var JitsiMeetExternalAPI = (function()
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.onFrameLoaded = function () {
|
||||
this.frameLoaded = true;
|
||||
for (var i = 0; i < this.initialCommands.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.initialCommands.length; i++) {
|
||||
this.sendMessage(this.initialCommands[i]);
|
||||
}
|
||||
this.initialCommands = null;
|
||||
@@ -331,13 +338,11 @@ var JitsiMeetExternalAPI = (function()
|
||||
this.eventListener = function (event) {
|
||||
self.processMessage(event);
|
||||
};
|
||||
if (window.addEventListener)
|
||||
{
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('message',
|
||||
this.eventListener, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
window.attachEvent('onmessage', this.eventListener);
|
||||
}
|
||||
};
|
||||
@@ -346,13 +351,11 @@ var JitsiMeetExternalAPI = (function()
|
||||
* Removes the listeners and removes the Jitsi Meet frame.
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.dispose = function () {
|
||||
if (window.removeEventListener)
|
||||
{
|
||||
if (window.removeEventListener) {
|
||||
window.removeEventListener('message',
|
||||
this.eventListener, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
window.detachEvent('onmessage',
|
||||
this.eventListener);
|
||||
}
|
||||
|
||||
112
index.html
112
index.html
@@ -11,9 +11,7 @@
|
||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||
<script src="https://api.callstats.io/static/callstats.min.js"></script>
|
||||
<script src="libs/jquery-2.1.1.min.js"></script>
|
||||
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/1.5.0/sha.js"></script>
|
||||
<script src="config.js?v=11"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="config.js?v=12"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||
<script src="libs/strophe/strophe.min.js?v=2"></script>
|
||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||
@@ -22,12 +20,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=106"></script>
|
||||
<script src="libs/app.bundle.js?v=130"></script>
|
||||
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
|
||||
<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=18" id="videolayout_default"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=31"/>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=20" 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">
|
||||
@@ -133,9 +131,7 @@
|
||||
</div>
|
||||
<span id="toolbar">
|
||||
<span id="authentication" class="authentication" style="display: none">
|
||||
<a class="button" id="toolbar_button_authentication" >
|
||||
<i id="authButton" class="icon-avatar"></i>
|
||||
</a>
|
||||
<a class="button icon-avatar" id="toolbar_button_authentication" data-i18n="[content]toolbar.authenticate"></a>
|
||||
<ul class="loginmenu">
|
||||
<span class="loginmenuPadding"></span>
|
||||
<li id="toolbar_auth_identity" class="identity"></li>
|
||||
@@ -146,97 +142,29 @@
|
||||
<a class="authButton" data-i18n="toolbar.logout"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="header_button_separator"></div>
|
||||
</span>
|
||||
<a class="button" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute">
|
||||
<i id="mute" class="icon-microphone"></i>
|
||||
<a class="button icon-microphone" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute"></a>
|
||||
<a class="button icon-camera" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera"></a>
|
||||
<a class="button icon-recEnable" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record" style="display: none"></a>
|
||||
<a class="button icon-security" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room"></a>
|
||||
<a class="button icon-link" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others"></a>
|
||||
<a class="button icon-chat" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
|
||||
<span id="unreadMessages"></span>
|
||||
</a>
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera">
|
||||
<i id="video" class="icon-camera"></i>
|
||||
</a>
|
||||
<span id="recording" style="display: none">
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record">
|
||||
<i id="recordButton" class="icon-recEnable"></i>
|
||||
</a>
|
||||
</span>
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room">
|
||||
<i id="lockIcon" class="icon-security"></i>
|
||||
</a>
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others">
|
||||
<i class="icon-link"></i>
|
||||
</a>
|
||||
<div class="header_button_separator"></div>
|
||||
<span class="toolbar_span">
|
||||
<a class="button" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
|
||||
<i id="chatButton" class="icon-chat">
|
||||
<span id="unreadMessages"></span>
|
||||
</i>
|
||||
</a>
|
||||
</span>
|
||||
<span id="prezi_button">
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.prezi" content="Share Prezi">
|
||||
<i class="icon-prezi"></i>
|
||||
</a>
|
||||
</span>
|
||||
<span id="etherpadButton">
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad">
|
||||
<i class="icon-share-doc"></i>
|
||||
</a>
|
||||
</span>
|
||||
<span id="desktopsharing" style="display: none">
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen" data-i18n="[content]toolbar.sharescreen">
|
||||
<i class="icon-share-desktop"></i>
|
||||
</a>
|
||||
</span>
|
||||
<div class="header_button_separator"></div>
|
||||
<a class="button" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen">
|
||||
<i id="fullScreen" class="icon-full-screen"></i>
|
||||
</a>
|
||||
<span id="sipCallButton" style="display: none">
|
||||
<div class="header_button_separator"></div>
|
||||
<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>
|
||||
</a>
|
||||
<div class="header_button_separator"></div>
|
||||
<span id="hangup">
|
||||
<a class="button" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up" data-i18n="[content]toolbar.hangup">
|
||||
<i class="icon-hangup" style="color:#ff0000;font-size: 1.4em;"></i>
|
||||
</a>
|
||||
</span>
|
||||
<a class="button icon-prezi" id="toolbar_button_prezi" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.prezi" content="Share Prezi"></a>
|
||||
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
|
||||
<a class="button icon-share-desktop" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" content="Share screen" data-i18n="[content]toolbar.sharescreen" style="display: none"></a>
|
||||
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen"></a>
|
||||
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip" style="display: none"></a>
|
||||
<a class="button icon-dialpad" id="toolbar_button_dialpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Open dialpad" data-i18n="[content]toolbar.dialpad" style="display: none"></a>
|
||||
<a class="button icon-settings" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" data-i18n="[content]toolbar.Settings"></a>
|
||||
<a class="button icon-hangup" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up" data-i18n="[content]toolbar.hangup"></a>
|
||||
</span>
|
||||
</div>
|
||||
<div id="subject"></div>
|
||||
</div>
|
||||
<div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
|
||||
<div id="videospace">
|
||||
<div id="largeVideoContainer" class="videocontainer">
|
||||
<div id="presentation"></div>
|
||||
<div id="etherpad"></div>
|
||||
<a target="_new"><div class="watermark leftwatermark"></div></a>
|
||||
<a target="_new"><div class="watermark rightwatermark"></div></a>
|
||||
<a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
|
||||
<div id="activeSpeaker">
|
||||
<img id="activeSpeakerAvatar" src=""/>
|
||||
<canvas id="activeSpeakerAudioLevel"></canvas>
|
||||
</div>
|
||||
<video id="largeVideo" autoplay oncontextmenu="return false;"></video>
|
||||
</div>
|
||||
<div id="remoteVideos">
|
||||
<span id="localVideoContainer" class="videocontainer">
|
||||
<span id="localNick" class="nick"></span>
|
||||
|
||||
@@ -15,5 +15,9 @@ var interfaceConfig = {
|
||||
GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true,
|
||||
APP_NAME: "Jitsi Meet",
|
||||
INVITATION_POWERED_BY: true,
|
||||
ACTIVE_SPEAKER_AVATAR_SIZE: 100
|
||||
ACTIVE_SPEAKER_AVATAR_SIZE: 100,
|
||||
/**
|
||||
* Whether to only show the filmstrip (and hide the toolbar).
|
||||
*/
|
||||
filmStripOnly: false
|
||||
};
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"de": "Deutsch",
|
||||
"tr": "Türkisch",
|
||||
"it": "Italienisch",
|
||||
"fr": "Französisch"
|
||||
"fr": "Französisch",
|
||||
"sl": "Slowenisch"
|
||||
}
|
||||
9
lang/languages-sl.json
Normal file
9
lang/languages-sl.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"en": "Angleščina",
|
||||
"bg": "Bolgarščina",
|
||||
"de": "Nemščina",
|
||||
"tr": "Turščina",
|
||||
"it": "Italjanščina",
|
||||
"fr": "Francoščina",
|
||||
"sl": ""
|
||||
}
|
||||
@@ -4,5 +4,6 @@
|
||||
"de": "German",
|
||||
"tr": "Turkish",
|
||||
"it": "Italian",
|
||||
"fr": "French"
|
||||
"fr": "French",
|
||||
"sl": "Slovenian"
|
||||
}
|
||||
|
||||
@@ -226,6 +226,7 @@
|
||||
"connection": {
|
||||
"ERROR": "Fehler",
|
||||
"CONNECTING": "Verbindung wird hergestellt",
|
||||
"RECONNECTING": "Es ist ein Netzwerkproblem aufgetreten. Verbinde...",
|
||||
"CONNFAIL": "Verbindungsaufbau gescheitert",
|
||||
"AUTHENTICATING": "Anmeldung läuft",
|
||||
"AUTHFAIL": "Authentifizierung fehlgeschlagen",
|
||||
@@ -233,5 +234,10 @@
|
||||
"DISCONNECTED": "Getrennt",
|
||||
"DISCONNECTING": "Verbindung wird getrennt",
|
||||
"ATTACHED": "Angehängt"
|
||||
},
|
||||
"recording": {
|
||||
"toaster": "Wird aufgezeichnet",
|
||||
"pending": "Die Aufzeichnung wird gestartet sobald ein weiterer Teilnehmer beitritt",
|
||||
"on": "Aufzeichnung wurde gestartet"
|
||||
}
|
||||
}
|
||||
249
lang/main-sl.json
Normal file
249
lang/main-sl.json
Normal file
@@ -0,0 +1,249 @@
|
||||
{
|
||||
"contactlist": "STIKI",
|
||||
"connectionsettings": "Nastavitve povezave",
|
||||
"poweredby": "poganja",
|
||||
"downloadlogs": "Shrani zapis",
|
||||
"roomUrlDefaultMsg": "Ustvarjanje vaše konference ...",
|
||||
"participant": "Udeleženec",
|
||||
"me": "jaz",
|
||||
"speaker": "Govornik",
|
||||
"defaultNickname": "npr. __name__",
|
||||
"defaultPreziLink": "npr. __url__",
|
||||
"welcomepage": {
|
||||
"go": "POJDI",
|
||||
"roomname": "Vpišite ime sobe",
|
||||
"disable": "Prihodnjič ne prikaži te strani",
|
||||
"feature1": {
|
||||
"title": "Enostavna uporaba",
|
||||
"content": "Nič ni treba namestiti. __app__ deluje direktno v vašem brskalniku. Enostavno sporočite ostalim udeležencem URL svoje konference in začnite."
|
||||
},
|
||||
"feature2": {
|
||||
"title": "Ozka pasovna širina",
|
||||
"content": "Video konferenca z več udeleženci s samo 128Kbps. Deljenje zaslona in samo avdio konference so možne že z veliko nižjo pasovno širino."
|
||||
},
|
||||
"feature3": {
|
||||
"title": "Odprta koda",
|
||||
"content": "__app__ je objavljen po licenci MIT. Pod pogoji te licence lahko prosto snamete, uporabljate, spreminjate in delite."
|
||||
},
|
||||
"feature4": {
|
||||
"title": "Neomejeno število uporabnikov",
|
||||
"content": "Nobene umetne omejitve števila uporabnikov ali udeležencev konference. Zmogljivost strežnika in pasovna širina sta edini omejitvi."
|
||||
},
|
||||
"feature5": {
|
||||
"title": "Skupna raba zaslona",
|
||||
"content": "Skupna raba zaslona z drugimi je zelo enostavna. __app__ je idealna rešitev za spletne predstavitve, predavanja in tehnično podporo."
|
||||
},
|
||||
"feature6": {
|
||||
"title": "Varne sobe",
|
||||
"content": "Rabite zasebnost? Konferenčne sobe __app__ so lahko zaklenjene z geslom, da preprečite dostop neželenim gostom ter prekinitve."
|
||||
},
|
||||
"feature7": {
|
||||
"title": "Skupna raba zapiskov",
|
||||
"content": "__app__ vsebuje Etherpad, realnočasovni skupinski urejevalnik besedil, ki je idealen za pisanje zapisnikov sestankov, člankov in še mnogo drugega."
|
||||
},
|
||||
"feature8": {
|
||||
"title": "Statistika uporabe",
|
||||
"content": "Spoznajte svoje uporabnike z enostavno integracijo v Piwik, Google Analytics ter druge sisteme za nadzor uporabe in statistiko."
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "Utišaj / Povrni glasnost",
|
||||
"videomute": "Zaženi / Ustavi kamero",
|
||||
"authenticate": "Overi",
|
||||
"record": "Snemaj",
|
||||
"lock": "Zakleni / Odkleni sobo",
|
||||
"invite": "Povabite ostale",
|
||||
"chat": "Odpri / zapri klepetalnico",
|
||||
"prezi": "Skupna raba Prezi",
|
||||
"etherpad": "Dokument v skupni rabi",
|
||||
"sharescreen": "Zaslon v souporabi",
|
||||
"fullscreen": "Vklopi / Izklopi celozaslonski način",
|
||||
"sip": "Pokliči številko SIP",
|
||||
"Settings": "Nastavitve",
|
||||
"hangup": "Odloži",
|
||||
"login": "Prijava",
|
||||
"logout": "Odjava",
|
||||
"dialpad": "Pokaži številčnico"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Odpri / zapri klepetalnico",
|
||||
"filmstrip": "Pokaži / Skrij filmski trak",
|
||||
"contactlist": "Odpri / Zapri stike"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Vpišite vzdevek v spodnje polje",
|
||||
"popover": "Izberite vzdevek"
|
||||
},
|
||||
"messagebox": "Vnesite besedilo ..."
|
||||
},
|
||||
"settings": {
|
||||
"title": "NASTAVITVE",
|
||||
"update": "Posodobi",
|
||||
"name": "Ime",
|
||||
"startAudioMuted": "začni brez zvoka",
|
||||
"startVideoMuted": "začni brez slike"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "Kliknite, da spremenite<br/>svoje ime",
|
||||
"moderator": "Lastnik<br/>konference",
|
||||
"videomute": "Udeleženec je<br/>izključil kamero.",
|
||||
"mute": "Udeleženec je utišan",
|
||||
"kick": "Izženi",
|
||||
"muted": "Utišan",
|
||||
"domute": "Utišaj"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "Bitna hitrost:",
|
||||
"packetloss": "Izgubljeni paketi:",
|
||||
"resolution": "Ločljivost:",
|
||||
"less": "Pokaži manj",
|
||||
"more": "Pokaži več",
|
||||
"address": "Naslov:",
|
||||
"remoteport_plural_5": "Oddaljena vrata:",
|
||||
"remoteport": "Oddaljena vrata:",
|
||||
"remoteport_plural_2": "Oddaljena vrata:",
|
||||
"remoteport_plural_3": "Oddaljena vrata:",
|
||||
"localport_plural_5": "Krajevna vrata:",
|
||||
"localport": "Krajevna vrata:",
|
||||
"localport_plural_2": "Krajevna vrata:",
|
||||
"localport_plural_3": "Krajevna vrata:",
|
||||
"localaddress_plural_5": "Krajevni naslov:",
|
||||
"localaddress": "Krajevna naslova:",
|
||||
"localaddress_plural_2": "Krajevni naslovi:",
|
||||
"localaddress_plural_3": "Krajevni naslov:",
|
||||
"remoteaddress_plural_5": "Oddaljeni naslov:",
|
||||
"remoteaddress": "Oddaljena naslova:",
|
||||
"remoteaddress_plural_2": "Oddaljeni naslovi:",
|
||||
"remoteaddress_plural_3": "Oddaljeni naslovi:",
|
||||
"transport": "Prenos:",
|
||||
"bandwidth": "Ocenjena pasovna širina:",
|
||||
"na": "Ko se konferenca začne se vrnite sem za informacije o povezavi"
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "odklopjeno",
|
||||
"moderator": "Dodeljene moderatorske pravice!",
|
||||
"connected": "povezano",
|
||||
"somebody": "Nekdo",
|
||||
"me": "Jaz",
|
||||
"focus": "Fokus na konferenco",
|
||||
"focusFail": "__component__ ni na razpolago - ponovni poskus čez __ms__ sec",
|
||||
"grantedTo": "Moderatorske pravice dodeljene uporabniku __to__!",
|
||||
"grantedToUnknown": "Moderatorske pravice dodeljene uporabniku $t(somebody)!",
|
||||
"muted": "Pogovor ste začeli utišano.",
|
||||
"mutedTitle": "Utišani ste!"
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "Ojej! Izgnali so vas iz srečanja!",
|
||||
"popupError": "Vaš brskalnik ne dovoli pojavnih oken iz te spletne strani. Omogočite prosim pojavna okna v varnostnih nastavitvah svojega brskalnika in ponovno poskusite.",
|
||||
"passwordError": "Ta pogovor je zaščiten z geslom. Samo lastnik konference lahko nastavi geslo.",
|
||||
"passwordError2": "Ta pogovor ni zaščiten z geslom. Samo lastnik konference lahko nastavi geslo.",
|
||||
"joinError": "Ups! Ni se bilo mogoče pridružiti konferenci. Mogoče je kakšna težava z varnostnimi nastavitvami. Pišite prosim administratorju storitve.",
|
||||
"connectError": "Ups! Nekaj je narobe in se ni bilo mogoče povezati s konferenco.",
|
||||
"connectErrorWithMsg": "Ups! Nekaj je narobe in se ni bilo mogoče povezati s konferenco: __msg__",
|
||||
"connecting": "Povezovanje",
|
||||
"error": "Napaka",
|
||||
"detectext": "Napaka pri zaznavanju razširitve za skupno uporabo namizja.",
|
||||
"failtoinstall": "Razširitve za skupno uporabo namizja ni bilo mogoče namestiti",
|
||||
"failedpermissions": "Ni bilo mogoče pridobiti dovoljenja za uporabo lokalnega mikrofona ali kamere.",
|
||||
"bridgeUnavailable": "Jitsi Videobridge trenutno ni na razpolago. Prosim poskusite kasneje!",
|
||||
"lockTitle": "Zaklepanje ni uspelo",
|
||||
"lockMessage": "Konference ni bilo mogoče zakleniti.",
|
||||
"warning": "Opozorilo",
|
||||
"passwordNotSupported": "Trenutno ni mogoče zakleniti sobe z geslom.",
|
||||
"sorry": "Oprostite",
|
||||
"internalError": "Notranja napaka [setRemoteDescription]",
|
||||
"unableToSwitch": "Ni mogoče preklopiti video pretoka.",
|
||||
"SLDFailure": "Ups! Nekaj je narobe in zvoka se ne da utišati! (Napaka SLD)",
|
||||
"SRDFailure": "Ups! Nekaj je narobe in slike ni mogoče ustaviti! (Napaka SRD)",
|
||||
"oops": "Ups!",
|
||||
"defaultError": "Prišlo je do neke napake",
|
||||
"passwordRequired": "Potrebno je geslo",
|
||||
"Ok": "V redu",
|
||||
"removePreziTitle": "Odstrani Prezi",
|
||||
"removePreziMsg": "Ali res želite odstraniti Prezi?",
|
||||
"sharePreziTitle": "Dajte Prezi v skupno rabo",
|
||||
"sharePreziMsg": "Drug uporabnik je že dal Prezi v skupno rabo. Ta konferenca podpira samo en Prezi naenkrat.",
|
||||
"Remove": "Odstrani",
|
||||
"WaitingForHost": "Čakanje na gostitelja ...",
|
||||
"WaitForHostMsg": "Ta konferenca <b>__room__ </b> se še ni začela. V primeru, da ste vi gostitelj se prosim overite. Drugače počakajte prosim na prihod gostitelja.",
|
||||
"IamHost": "Jaz sem gostitelj",
|
||||
"Cancel": "Prekliči",
|
||||
"retry": "Poskusi ponovno",
|
||||
"logoutTitle": "Odjava",
|
||||
"logoutQuestion": "Ali se res želite odjaviti in prekiniti konferenco?",
|
||||
"sessTerminated": "Seja je končana",
|
||||
"hungUp": "Prekinili ste klic",
|
||||
"joinAgain": "Ponovno se pridruži",
|
||||
"Share": "Souporaba",
|
||||
"preziLinkError": "Prosim, pravilno vpišite povezavo Prezi.",
|
||||
"Save": "Shrani",
|
||||
"recordingToken": "Vnesite žeton za registracijo",
|
||||
"Dial": "Pokliči",
|
||||
"sipMsg": "Vnesite številko SIP",
|
||||
"passwordCheck": "Ali res želite odstraniti geslo?",
|
||||
"passwordMsg": "Nastavite geslo za zaklepanje sobe",
|
||||
"Invite": "Povabi",
|
||||
"shareLink": "To povezavo pošljite vsem, ki jih želite povabiti",
|
||||
"settings1": "Nastavite svojo konferenco",
|
||||
"settings2": "Utišaj udeležence ob pristopu",
|
||||
"settings3": "Zahtevaj vzdevke<br/><br/>Nastavi geslo za zaklep sobe:",
|
||||
"yourPassword": "vaše geslo",
|
||||
"Back": "Nazaj",
|
||||
"serviceUnavailable": "Storitev ni na voljo",
|
||||
"gracefulShutdown": "Storitev trenutno ni na voljo zaradi vzdrževanja. Poskusite ponovno kasneje.",
|
||||
"Yes": "Da",
|
||||
"reservationError": "Napaka v sistemu rezervacije",
|
||||
"reservationErrorMsg": "Koda napake: __code__, sporočilo: __msg__",
|
||||
"password": "geslo",
|
||||
"userPassword": "uporabniško geslo",
|
||||
"token": "žeton",
|
||||
"displayNameRequired": "Vpišite svoje ime:"
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Ta konferenca je zaklenjena z geslom. Uporabite sledeči PIN ko se pridružite:",
|
||||
"",
|
||||
"",
|
||||
"__sharedKey__",
|
||||
"",
|
||||
""
|
||||
],
|
||||
"subject": "Povabilo na __appName__ (__conferenceName__)",
|
||||
"body": [
|
||||
"Pozdravljeni,",
|
||||
"želim vas povabiti na ravnokar pripravljeno konferenco __appName__.",
|
||||
"",
|
||||
"",
|
||||
"Prosim, kliknite na sledečo povezavo, da se pridružite konferenci.",
|
||||
"",
|
||||
"",
|
||||
"__roomUrl__",
|
||||
"",
|
||||
"",
|
||||
"__sharedKeyText__",
|
||||
" Opomba: __appName__ trenutno nudi podporo samo za __supportedBrowsers__, uporabiti morate enega izmed teh brskalnikov.",
|
||||
"",
|
||||
"",
|
||||
"Se slišimo čez trenutek!"
|
||||
],
|
||||
"and": "in"
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Napaka",
|
||||
"CONNECTING": "Povezovanje",
|
||||
"RECONNECTING": "Prišlo je do omrežne napake. Ponovni poskus ...",
|
||||
"CONNFAIL": "Povezovanje je spodletelo",
|
||||
"AUTHENTICATING": "Overjanje",
|
||||
"AUTHFAIL": "Overitev je spodletela",
|
||||
"CONNECTED": "Povezano",
|
||||
"DISCONNECTED": "Ni povezave",
|
||||
"DISCONNECTING": "Prekinjanje povezave",
|
||||
"ATTACHED": "Priključeno"
|
||||
},
|
||||
"recording": {
|
||||
"toaster": "Trenutno poteka snemanje!",
|
||||
"pending": "Snemanje se bo začelo takoj, ko se bo pridružil drugi udeleženec",
|
||||
"on": "Snemanje se je začelo"
|
||||
}
|
||||
}
|
||||
@@ -229,6 +229,7 @@
|
||||
{
|
||||
"ERROR": "Error",
|
||||
"CONNECTING": "Connecting",
|
||||
"RECONNECTING": "A network problem occurred. Reconnecting...",
|
||||
"CONNFAIL": "Connection failed",
|
||||
"AUTHENTICATING": "Authenticating",
|
||||
"AUTHFAIL": "Authentication failed",
|
||||
@@ -239,5 +240,11 @@
|
||||
"FETCH_SESSION_ID": "Obtaining session-id...",
|
||||
"GOT_SESSION_ID": "Obtaining session-id... Done",
|
||||
"GET_SESSION_ID_ERROR": "Get session-id error: "
|
||||
},
|
||||
"recording":
|
||||
{
|
||||
"toaster": "Currently recording!",
|
||||
"pending": "Your recording will start as soon as another participant joins",
|
||||
"on": "Recording has been started"
|
||||
}
|
||||
}
|
||||
|
||||
46073
libs/app.bundle.js
46073
libs/app.bundle.js
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
/* global APP */
|
||||
/**
|
||||
* Implements API class that communicates with external api class
|
||||
* and provides interface to access Jitsi Meet features by external
|
||||
@@ -10,19 +11,20 @@ var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
* List of the available commands.
|
||||
* @type {{
|
||||
* displayName: inputDisplayNameHandler,
|
||||
* muteAudio: toggleAudio,
|
||||
* muteVideo: toggleVideo,
|
||||
* filmStrip: toggleFilmStrip
|
||||
* toggleAudio: toggleAudio,
|
||||
* toggleVideo: toggleVideo,
|
||||
* toggleFilmStrip: toggleFilmStrip,
|
||||
* toggleChat: toggleChat,
|
||||
* toggleContactList: toggleContactList
|
||||
* }}
|
||||
*/
|
||||
var commands = {};
|
||||
|
||||
function initCommands() {
|
||||
commands =
|
||||
{
|
||||
commands = {
|
||||
displayName: APP.UI.inputDisplayNameHandler,
|
||||
muteAudio: APP.UI.toggleAudio,
|
||||
muteVideo: APP.UI.toggleVideo,
|
||||
toggleAudio: APP.UI.toggleAudio,
|
||||
toggleVideo: APP.UI.toggleVideo,
|
||||
toggleFilmStrip: APP.UI.toggleFilmStrip,
|
||||
toggleChat: APP.UI.toggleChat,
|
||||
toggleContactList: APP.UI.toggleContactList
|
||||
@@ -41,8 +43,7 @@ function initCommands() {
|
||||
* participantLeft: boolean
|
||||
* }}
|
||||
*/
|
||||
var events =
|
||||
{
|
||||
var events = {
|
||||
incomingMessage: false,
|
||||
outgoingMessage:false,
|
||||
displayNameChange: false,
|
||||
@@ -53,18 +54,15 @@ var events =
|
||||
var displayName = {};
|
||||
|
||||
/**
|
||||
* Processes commands from external applicaiton.
|
||||
* Processes commands from external application.
|
||||
* @param message the object with the command
|
||||
*/
|
||||
function processCommand(message)
|
||||
{
|
||||
if(message.action != "execute")
|
||||
{
|
||||
function processCommand(message) {
|
||||
if (message.action != "execute") {
|
||||
console.error("Unknown action of the message");
|
||||
return;
|
||||
}
|
||||
for(var key in message)
|
||||
{
|
||||
for (var key in message) {
|
||||
if(commands[key])
|
||||
commands[key].apply(null, message[key]);
|
||||
}
|
||||
@@ -75,31 +73,26 @@ function processCommand(message)
|
||||
* @param event the event
|
||||
*/
|
||||
function processEvent(event) {
|
||||
if(!event.action)
|
||||
{
|
||||
if (!event.action) {
|
||||
console.error("Event with no action is received.");
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
switch(event.action)
|
||||
{
|
||||
switch(event.action) {
|
||||
case "add":
|
||||
for(; i < event.events.length; i++)
|
||||
{
|
||||
for (; i < event.events.length; i++) {
|
||||
events[event.events[i]] = true;
|
||||
}
|
||||
break;
|
||||
case "remove":
|
||||
for(; i < event.events.length; i++)
|
||||
{
|
||||
for (; i < event.events.length; i++) {
|
||||
events[event.events[i]] = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown action for event.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,8 +107,7 @@ function sendMessage(object) {
|
||||
* Processes a message event from the external application
|
||||
* @param event the message event
|
||||
*/
|
||||
function processMessage(event)
|
||||
{
|
||||
function processMessage(event) {
|
||||
var message;
|
||||
try {
|
||||
message = JSON.parse(event.data);
|
||||
@@ -123,8 +115,7 @@ function processMessage(event)
|
||||
|
||||
if(!message.type)
|
||||
return;
|
||||
switch (message.type)
|
||||
{
|
||||
switch (message.type) {
|
||||
case "command":
|
||||
processCommand(message);
|
||||
break;
|
||||
@@ -135,7 +126,6 @@ function processMessage(event)
|
||||
console.error("Unknown type of the message");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setupListeners() {
|
||||
@@ -151,7 +141,7 @@ function setupListeners() {
|
||||
API.triggerEvent("participantLeft", {jid: jid});
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, function (jid, newDisplayName) {
|
||||
name = displayName[jid];
|
||||
var name = displayName[jid];
|
||||
if(!name || name != newDisplayName) {
|
||||
API.triggerEvent("displayNameChange", {jid: jid, displayname: newDisplayName});
|
||||
displayName[jid] = newDisplayName;
|
||||
@@ -169,7 +159,7 @@ var API = {
|
||||
*/
|
||||
isEnabled: function () {
|
||||
var hash = location.hash;
|
||||
if(hash && hash.indexOf("external") > -1 && window.postMessage)
|
||||
if (hash && hash.indexOf("external") > -1 && window.postMessage)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
@@ -181,13 +171,11 @@ var API = {
|
||||
*/
|
||||
init: function () {
|
||||
initCommands();
|
||||
if (window.addEventListener)
|
||||
{
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('message',
|
||||
processMessage, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
window.attachEvent('onmessage', processMessage);
|
||||
}
|
||||
sendMessage({type: "system", loaded: true});
|
||||
@@ -218,19 +206,14 @@ var API = {
|
||||
* Removes the listeners.
|
||||
*/
|
||||
dispose: function () {
|
||||
if(window.removeEventListener)
|
||||
{
|
||||
if(window.removeEventListener) {
|
||||
window.removeEventListener("message",
|
||||
processMessage, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
window.detachEvent('onmessage', processMessage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
module.exports = API;
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global Strophe, focusedVideoSrc*/
|
||||
/* global config, APP, Strophe */
|
||||
|
||||
// cache datachannels to avoid garbage collection
|
||||
// https://code.google.com/p/chromium/issues/detail?id=405545
|
||||
@@ -8,19 +8,13 @@ var _dataChannels = [];
|
||||
var eventEmitter = null;
|
||||
|
||||
|
||||
|
||||
|
||||
var DataChannels =
|
||||
{
|
||||
|
||||
var DataChannels = {
|
||||
/**
|
||||
* Callback triggered by PeerConnection when new data channel is opened
|
||||
* on the bridge.
|
||||
* @param event the event info object.
|
||||
*/
|
||||
|
||||
onDataChannel: function (event)
|
||||
{
|
||||
onDataChannel: function (event) {
|
||||
var dataChannel = event.channel;
|
||||
|
||||
dataChannel.onopen = function () {
|
||||
@@ -32,14 +26,7 @@ var DataChannels =
|
||||
// Sends 12 bytes binary message to the bridge
|
||||
//dataChannel.send(new ArrayBuffer(12));
|
||||
|
||||
// when the data channel becomes available, tell the bridge about video
|
||||
// selections so that it can do adaptive simulcast,
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
var userJid = APP.UI.getLargeVideoJid();
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
onSelectedEndpointChanged(userJid);
|
||||
eventEmitter.emit(RTCEvents.DATA_CHANNEL_OPEN);
|
||||
};
|
||||
|
||||
dataChannel.onerror = function (error) {
|
||||
@@ -72,8 +59,7 @@ var DataChannels =
|
||||
dominantSpeakerEndpoint);
|
||||
eventEmitter.emit(RTCEvents.DOMINANTSPEAKER_CHANGED, dominantSpeakerEndpoint);
|
||||
}
|
||||
else if ("InLastNChangeEvent" === colibriClass)
|
||||
{
|
||||
else if ("InLastNChangeEvent" === colibriClass) {
|
||||
var oldValue = obj.oldValue;
|
||||
var newValue = obj.newValue;
|
||||
// Make sure that oldValue and newValue are of type boolean.
|
||||
@@ -96,15 +82,13 @@ var DataChannels =
|
||||
|
||||
eventEmitter.emit(RTCEvents.LASTN_CHANGED, oldValue, newValue);
|
||||
}
|
||||
else if ("LastNEndpointsChangeEvent" === colibriClass)
|
||||
{
|
||||
else if ("LastNEndpointsChangeEvent" === colibriClass) {
|
||||
// The new/latest list of last-n endpoint IDs.
|
||||
var lastNEndpoints = obj.lastNEndpoints;
|
||||
// The list of endpoint IDs which are entering the list of
|
||||
// last-n at this time i.e. were not in the old list of last-n
|
||||
// endpoint IDs.
|
||||
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
|
||||
var stream = obj.stream;
|
||||
|
||||
console.log(
|
||||
"Data channel new last-n event: ",
|
||||
@@ -112,15 +96,13 @@ var DataChannels =
|
||||
eventEmitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,
|
||||
lastNEndpoints, endpointsEnteringLastN, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
console.debug("Data channel JSON-formatted message: ", obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dataChannel.onclose = function ()
|
||||
{
|
||||
dataChannel.onclose = function () {
|
||||
console.info("The Data Channel closed", dataChannel);
|
||||
var idx = _dataChannels.indexOf(dataChannel);
|
||||
if (idx > -1)
|
||||
@@ -165,19 +147,15 @@ var DataChannels =
|
||||
},
|
||||
handleSelectedEndpointEvent: onSelectedEndpointChanged,
|
||||
handlePinnedEndpointEvent: onPinnedEndpointChanged
|
||||
|
||||
};
|
||||
|
||||
function onSelectedEndpointChanged(userResource)
|
||||
{
|
||||
function onSelectedEndpointChanged(userResource) {
|
||||
console.log('selected endpoint changed: ', userResource);
|
||||
if (_dataChannels && _dataChannels.length != 0)
|
||||
{
|
||||
if (_dataChannels && _dataChannels.length != 0) {
|
||||
_dataChannels.some(function (dataChannel) {
|
||||
if (dataChannel.readyState == 'open')
|
||||
{
|
||||
console.log('sending selected endpoint changed '
|
||||
+ 'notification to the bridge: ', userResource);
|
||||
if (dataChannel.readyState == 'open') {
|
||||
console.log('sending selected endpoint changed ' +
|
||||
'notification to the bridge: ', userResource);
|
||||
dataChannel.send(JSON.stringify({
|
||||
'colibriClass': 'SelectedEndpointChangedEvent',
|
||||
'selectedEndpoint':
|
||||
@@ -191,14 +169,11 @@ function onSelectedEndpointChanged(userResource)
|
||||
}
|
||||
}
|
||||
|
||||
function onPinnedEndpointChanged(userResource)
|
||||
{
|
||||
function onPinnedEndpointChanged(userResource) {
|
||||
console.log('pinned endpoint changed: ', userResource);
|
||||
if (_dataChannels && _dataChannels.length != 0)
|
||||
{
|
||||
if (_dataChannels && _dataChannels.length != 0) {
|
||||
_dataChannels.some(function (dataChannel) {
|
||||
if (dataChannel.readyState == 'open')
|
||||
{
|
||||
if (dataChannel.readyState == 'open') {
|
||||
dataChannel.send(JSON.stringify({
|
||||
'colibriClass': 'PinnedEndpointChangedEvent',
|
||||
'pinnedEndpoint':
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
/* global APP */
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
|
||||
var RTCEvents = require("../../service/RTC/RTCEvents");
|
||||
var RTCBrowserType = require("./RTCBrowserType");
|
||||
|
||||
/**
|
||||
* This implements 'onended' callback normally fired by WebRTC after the stream
|
||||
* is stopped. There is no such behaviour yet in FF, so we have to add it.
|
||||
* @param stream original WebRTC stream object to which 'onended' handling
|
||||
* will be added.
|
||||
*/
|
||||
function implementOnEndedHandling(stream) {
|
||||
var originalStop = stream.stop;
|
||||
stream.stop = function () {
|
||||
originalStop.apply(stream);
|
||||
if (!stream.ended) {
|
||||
stream.ended = true;
|
||||
stream.onended();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
|
||||
{
|
||||
function LocalStream(stream, type, eventEmitter, videoType, isGUMStream) {
|
||||
this.stream = stream;
|
||||
this.eventEmitter = eventEmitter;
|
||||
this.type = type;
|
||||
@@ -12,84 +29,71 @@ function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
|
||||
if(isGUMStream === false)
|
||||
this.isGUMStream = isGUMStream;
|
||||
var self = this;
|
||||
if(type == "audio")
|
||||
{
|
||||
if(type == "audio") {
|
||||
this.getTracks = function () {
|
||||
return self.stream.getAudioTracks();
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.getTracks = function () {
|
||||
return self.stream.getVideoTracks();
|
||||
};
|
||||
}
|
||||
|
||||
this.stream.onended = function()
|
||||
{
|
||||
this.stream.onended = function () {
|
||||
self.streamEnded();
|
||||
};
|
||||
if (RTCBrowserType.isFirefox()) {
|
||||
implementOnEndedHandling(this.stream);
|
||||
}
|
||||
}
|
||||
|
||||
LocalStream.prototype.streamEnded = function () {
|
||||
this.eventEmitter.emit(StreamEventTypes.EVENT_TYPE_LOCAL_ENDED, this);
|
||||
}
|
||||
};
|
||||
|
||||
LocalStream.prototype.getOriginalStream = function()
|
||||
{
|
||||
return this.stream;
|
||||
}
|
||||
};
|
||||
|
||||
LocalStream.prototype.isAudioStream = function () {
|
||||
return this.type === "audio";
|
||||
};
|
||||
|
||||
LocalStream.prototype.setMute = function(mute)
|
||||
LocalStream.prototype.setMute = function (mute)
|
||||
{
|
||||
var isAudio = this.isAudioStream();
|
||||
var eventType = isAudio ? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE;
|
||||
|
||||
if ((window.location.protocol != "https:" && this.isGUMStream) ||
|
||||
(isAudio && this.isGUMStream) || this.videoType === "screen" ||
|
||||
// FIXME FF does not support 'removeStream' method used to mute
|
||||
RTCBrowserType.isFirefox()) {
|
||||
|
||||
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;
|
||||
tracks[idx].enabled = !mute;
|
||||
}
|
||||
this.eventEmitter.emit(
|
||||
(this.type == "audio"? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE),
|
||||
!mute);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mute === false) {
|
||||
this.eventEmitter.emit(eventType, mute);
|
||||
} else {
|
||||
if (mute) {
|
||||
APP.xmpp.removeStream(this.stream);
|
||||
this.stream.stop();
|
||||
this.eventEmitter.emit(
|
||||
(this.type == "audio"? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE),
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.eventEmitter.emit(eventType, true);
|
||||
} else {
|
||||
var self = this;
|
||||
APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(
|
||||
(this.isAudioStream() ? ["audio"] : ["video"]),
|
||||
function (stream) {
|
||||
if(self.isAudioStream())
|
||||
{
|
||||
if (isAudio) {
|
||||
APP.RTC.changeLocalAudio(stream,
|
||||
function () {
|
||||
self.eventEmitter.emit(
|
||||
(self.type == "audio"? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE),
|
||||
true);
|
||||
self.eventEmitter.emit(eventType, false);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
APP.RTC.changeLocalVideo(stream, false,
|
||||
function () {
|
||||
self.eventEmitter.emit(
|
||||
(self.type == "audio"? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE),
|
||||
true);
|
||||
self.eventEmitter.emit(eventType, false);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -99,13 +103,10 @@ LocalStream.prototype.setMute = function(mute)
|
||||
|
||||
LocalStream.prototype.isMuted = function () {
|
||||
var tracks = [];
|
||||
if(this.type == "audio")
|
||||
{
|
||||
if (this.isAudioStream()) {
|
||||
tracks = this.stream.getAudioTracks();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this.stream.ended)
|
||||
} else {
|
||||
if (this.stream.ended)
|
||||
return true;
|
||||
tracks = this.stream.getVideoTracks();
|
||||
}
|
||||
@@ -114,10 +115,10 @@ LocalStream.prototype.isMuted = function () {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LocalStream.prototype.getId = function () {
|
||||
return this.stream.getTracks()[0].id;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LocalStream;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
////These lines should be uncommented when require works in app.js
|
||||
var MediaStreamType = require("../../service/RTC/MediaStreamTypes");
|
||||
var StreamEventType = require("../../service/RTC/StreamEventTypes");
|
||||
|
||||
/**
|
||||
* Creates a MediaStream object for the given data, session id and ssrc.
|
||||
@@ -37,13 +35,11 @@ function MediaStream(data, sid, ssrc, browser, eventEmitter) {
|
||||
}
|
||||
|
||||
|
||||
MediaStream.prototype.getOriginalStream = function()
|
||||
{
|
||||
MediaStream.prototype.getOriginalStream = function() {
|
||||
return this.stream;
|
||||
};
|
||||
|
||||
MediaStream.prototype.setMute = function (value)
|
||||
{
|
||||
MediaStream.prototype.setMute = function (value) {
|
||||
this.stream.muted = value;
|
||||
this.muted = value;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global APP */
|
||||
var EventEmitter = require("events");
|
||||
var RTCBrowserType = require("./RTCBrowserType");
|
||||
var RTCUtils = require("./RTCUtils.js");
|
||||
@@ -72,14 +73,11 @@ var RTC = {
|
||||
this.localStreams[0].getOriginalStream() != stream)
|
||||
this.localStreams.push(localStream);
|
||||
if(isMuted === true)
|
||||
localStream.setMute(false);
|
||||
localStream.setMute(true);
|
||||
|
||||
if(type == "audio")
|
||||
{
|
||||
if(type == "audio") {
|
||||
this.localAudio = localStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.localVideo = localStream;
|
||||
}
|
||||
var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
|
||||
@@ -90,8 +88,7 @@ var RTC = {
|
||||
return localStream;
|
||||
},
|
||||
removeLocalStream: function (stream) {
|
||||
for(var i = 0; i < this.localStreams.length; i++)
|
||||
{
|
||||
for(var i = 0; i < this.localStreams.length; i++) {
|
||||
if(this.localStreams[i].getOriginalStream() === stream) {
|
||||
delete this.localStreams[i];
|
||||
return;
|
||||
@@ -176,8 +173,7 @@ var RTC = {
|
||||
var stream;
|
||||
|
||||
if(this.remoteStreams[jid] &&
|
||||
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE])
|
||||
{
|
||||
this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
||||
stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
||||
}
|
||||
|
||||
@@ -202,12 +198,14 @@ var RTC = {
|
||||
},
|
||||
changeLocalVideo: function (stream, isUsingScreenStream, callback) {
|
||||
var oldStream = this.localVideo.getOriginalStream();
|
||||
var type = (isUsingScreenStream? "screen" : "video");
|
||||
var type = (isUsingScreenStream ? "screen" : "camera");
|
||||
var localCallback = callback;
|
||||
if(this.localVideo.isMuted() && this.localVideo.videoType !== type)
|
||||
{
|
||||
if(this.localVideo.isMuted() && this.localVideo.videoType !== type) {
|
||||
localCallback = function() {
|
||||
APP.xmpp.setVideoMute(false, APP.UI.setVideoMuteButtonsState);
|
||||
APP.xmpp.setVideoMute(false, function(mute) {
|
||||
eventEmitter.emit(RTCEvents.VIDEO_MUTE, mute);
|
||||
});
|
||||
|
||||
callback();
|
||||
};
|
||||
}
|
||||
@@ -236,54 +234,27 @@ var RTC = {
|
||||
if (jid === APP.xmpp.myJid()) {
|
||||
var localVideo = APP.RTC.localVideo;
|
||||
return (!localVideo || localVideo.isMuted());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
||||
} else {
|
||||
if (!APP.RTC.remoteStreams[jid] ||
|
||||
!APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
|
||||
return null;
|
||||
}
|
||||
return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if video identified by given src is desktop stream.
|
||||
* @param videoSrc eg.
|
||||
* blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isVideoSrcDesktop: function (jid) {
|
||||
if(!jid)
|
||||
return false;
|
||||
var isDesktop = false;
|
||||
var stream = null;
|
||||
if (APP.xmpp.myJid() === jid) {
|
||||
// local video
|
||||
stream = this.localVideo;
|
||||
} else {
|
||||
var peerStreams = this.remoteStreams[jid];
|
||||
if(!peerStreams)
|
||||
return false;
|
||||
stream = peerStreams[MediaStreamType.VIDEO_TYPE];
|
||||
}
|
||||
|
||||
if(stream)
|
||||
isDesktop = (stream.videoType === "screen");
|
||||
|
||||
return isDesktop;
|
||||
},
|
||||
setVideoMute: function(mute, callback, options) {
|
||||
if(!this.localVideo)
|
||||
setVideoMute: function (mute, callback, options) {
|
||||
if (!this.localVideo)
|
||||
return;
|
||||
|
||||
if (mute == APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
APP.xmpp.sendVideoInfoPresence(mute);
|
||||
if(callback)
|
||||
if (callback)
|
||||
callback(mute);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP.RTC.localVideo.setMute(!mute);
|
||||
APP.RTC.localVideo.setMute(mute);
|
||||
APP.xmpp.setVideoMute(
|
||||
mute,
|
||||
callback,
|
||||
|
||||
@@ -3,6 +3,8 @@ var currentBrowser;
|
||||
|
||||
var browserVersion;
|
||||
|
||||
var isAndroid;
|
||||
|
||||
var RTCBrowserType = {
|
||||
|
||||
RTC_BROWSER_CHROME: "rtc_browser.chrome",
|
||||
@@ -46,6 +48,22 @@ var RTCBrowserType = {
|
||||
|
||||
getChromeVersion: function () {
|
||||
return RTCBrowserType.isChrome() ? browserVersion : null;
|
||||
},
|
||||
|
||||
usesPlanB: function() {
|
||||
return RTCBrowserType.isChrome() || RTCBrowserType.isOpera() ||
|
||||
RTCBrowserType.isTemasysPluginUsed();
|
||||
},
|
||||
|
||||
usesUnifiedPlan: function() {
|
||||
return RTCBrowserType.isFirefox();
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the browser is running on an android device.
|
||||
*/
|
||||
isAndroid: function() {
|
||||
return isAndroid;
|
||||
}
|
||||
|
||||
// Add version getters for other browsers when needed
|
||||
@@ -148,5 +166,6 @@ function detectBrowser() {
|
||||
}
|
||||
|
||||
browserVersion = detectBrowser();
|
||||
isAndroid = navigator.userAgent.indexOf('Android') != -1;
|
||||
|
||||
module.exports = RTCBrowserType;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global config, require, attachMediaStream, getUserMedia */
|
||||
var RTCBrowserType = require("./RTCBrowserType");
|
||||
var Resolutions = require("../../service/RTC/Resolutions");
|
||||
var AdapterJS = require("./adapter.screenshare");
|
||||
@@ -11,11 +12,9 @@ function getPreviousResolution(resolution) {
|
||||
var order = Resolutions[resolution].order;
|
||||
var res = null;
|
||||
var resName = null;
|
||||
for(var i in Resolutions)
|
||||
{
|
||||
for(var i in Resolutions) {
|
||||
var tmp = Resolutions[i];
|
||||
if(res == null || (res.order < tmp.order && tmp.order < order))
|
||||
{
|
||||
if(res == null || (res.order < tmp.order && tmp.order < order)) {
|
||||
resName = i;
|
||||
res = tmp;
|
||||
}
|
||||
@@ -23,41 +22,57 @@ function getPreviousResolution(resolution) {
|
||||
return resName;
|
||||
}
|
||||
|
||||
function setResolutionConstraints(constraints, resolution, isAndroid)
|
||||
{
|
||||
if (resolution && !constraints.video || isAndroid) {
|
||||
constraints.video = { mandatory: {}, optional: [] };// same behaviour as true
|
||||
}
|
||||
function setResolutionConstraints(constraints, resolution) {
|
||||
var isAndroid = RTCBrowserType.isAndroid();
|
||||
|
||||
if(Resolutions[resolution])
|
||||
{
|
||||
if (Resolutions[resolution]) {
|
||||
constraints.video.mandatory.minWidth = Resolutions[resolution].width;
|
||||
constraints.video.mandatory.minHeight = Resolutions[resolution].height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isAndroid) {
|
||||
constraints.video.mandatory.minWidth = 320;
|
||||
constraints.video.mandatory.minHeight = 240;
|
||||
constraints.video.mandatory.maxFrameRate = 15;
|
||||
}
|
||||
else if (isAndroid) {
|
||||
// FIXME can't remember if the purpose of this was to always request
|
||||
// low resolution on Android ? if yes it should be moved up front
|
||||
constraints.video.mandatory.minWidth = 320;
|
||||
constraints.video.mandatory.minHeight = 240;
|
||||
constraints.video.mandatory.maxFrameRate = 15;
|
||||
}
|
||||
|
||||
if (constraints.video.mandatory.minWidth)
|
||||
constraints.video.mandatory.maxWidth = constraints.video.mandatory.minWidth;
|
||||
constraints.video.mandatory.maxWidth =
|
||||
constraints.video.mandatory.minWidth;
|
||||
if (constraints.video.mandatory.minHeight)
|
||||
constraints.video.mandatory.maxHeight = constraints.video.mandatory.minHeight;
|
||||
constraints.video.mandatory.maxHeight =
|
||||
constraints.video.mandatory.minHeight;
|
||||
}
|
||||
|
||||
function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid)
|
||||
{
|
||||
function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
|
||||
var constraints = {audio: false, video: false};
|
||||
|
||||
if (um.indexOf('video') >= 0) {
|
||||
constraints.video = { mandatory: {}, optional: [] };// same behaviour as true
|
||||
// same behaviour as true
|
||||
constraints.video = { mandatory: {}, optional: [] };
|
||||
|
||||
constraints.video.optional.push({ googLeakyBucket: true });
|
||||
|
||||
setResolutionConstraints(constraints, resolution);
|
||||
}
|
||||
if (um.indexOf('audio') >= 0) {
|
||||
constraints.audio = { mandatory: {}, optional: []};// same behaviour as true
|
||||
if (!RTCBrowserType.isFirefox()) {
|
||||
// same behaviour as true
|
||||
constraints.audio = { mandatory: {}, optional: []};
|
||||
// if it is good enough for hangouts...
|
||||
constraints.audio.optional.push(
|
||||
{googEchoCancellation: true},
|
||||
{googAutoGainControl: true},
|
||||
{googNoiseSupression: true},
|
||||
{googHighpassFilter: true},
|
||||
{googNoisesuppression2: true},
|
||||
{googEchoCancellation2: true},
|
||||
{googAutoGainControl2: true}
|
||||
);
|
||||
} else {
|
||||
constraints.audio = true;
|
||||
}
|
||||
}
|
||||
if (um.indexOf('screen') >= 0) {
|
||||
if (RTCBrowserType.isChrome()) {
|
||||
@@ -99,43 +114,32 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid
|
||||
};
|
||||
}
|
||||
|
||||
if (constraints.audio) {
|
||||
// if it is good enough for hangouts...
|
||||
constraints.audio.optional.push(
|
||||
{googEchoCancellation: true},
|
||||
{googAutoGainControl: true},
|
||||
{googNoiseSupression: true},
|
||||
{googHighpassFilter: true},
|
||||
{googNoisesuppression2: true},
|
||||
{googEchoCancellation2: true},
|
||||
{googAutoGainControl2: true}
|
||||
);
|
||||
}
|
||||
if (constraints.video) {
|
||||
constraints.video.optional.push(
|
||||
{googNoiseReduction: false} // chrome 37 workaround for issue 3807, reenable in M38
|
||||
);
|
||||
if (um.indexOf('video') >= 0) {
|
||||
constraints.video.optional.push(
|
||||
{googLeakyBucket: true}
|
||||
);
|
||||
if (bandwidth) {
|
||||
if (!constraints.video) {
|
||||
//same behaviour as true
|
||||
constraints.video = {mandatory: {}, optional: []};
|
||||
}
|
||||
}
|
||||
|
||||
if (um.indexOf('video') >= 0) {
|
||||
setResolutionConstraints(constraints, resolution, isAndroid);
|
||||
}
|
||||
|
||||
if (bandwidth) { // doesn't work currently, see webrtc issue 1846
|
||||
if (!constraints.video) constraints.video = {mandatory: {}, optional: []};//same behaviour as true
|
||||
constraints.video.optional.push({bandwidth: bandwidth});
|
||||
}
|
||||
if (fps) { // for some cameras it might be necessary to request 30fps
|
||||
if (fps) {
|
||||
// for some cameras it might be necessary to request 30fps
|
||||
// so they choose 30fps mjpg over 10fps yuy2
|
||||
if (!constraints.video) constraints.video = {mandatory: {}, optional: []};// same behaviour as true;
|
||||
if (!constraints.video) {
|
||||
// same behaviour as true;
|
||||
constraints.video = {mandatory: {}, optional: []};
|
||||
}
|
||||
constraints.video.mandatory.minFrameRate = fps;
|
||||
}
|
||||
|
||||
// we turn audio for both audio and video tracks, the fake audio & video seems to work
|
||||
// only when enabled in one getUserMedia call, we cannot get fake audio separate by fake video
|
||||
// this later can be a problem with some of the tests
|
||||
if(RTCBrowserType.isFirefox() && config.firefox_fake_device)
|
||||
{
|
||||
constraints.audio = true;
|
||||
constraints.fake = true;
|
||||
}
|
||||
|
||||
return constraints;
|
||||
}
|
||||
|
||||
@@ -146,7 +150,7 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
this.service = RTCService;
|
||||
if (RTCBrowserType.isFirefox()) {
|
||||
var FFversion = RTCBrowserType.getFirefoxVersion();
|
||||
if (FFversion >= 40 && config.useBundle && config.useRtcpMux) {
|
||||
if (FFversion >= 40) {
|
||||
this.peerconnection = mozRTCPeerConnection;
|
||||
this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
|
||||
this.pc_constraints = {};
|
||||
@@ -164,12 +168,15 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
element[0].play();
|
||||
};
|
||||
this.getStreamID = function (stream) {
|
||||
var tracks = stream.getVideoTracks();
|
||||
if(!tracks || tracks.length == 0)
|
||||
{
|
||||
tracks = stream.getAudioTracks();
|
||||
var id = stream.id;
|
||||
if (!id) {
|
||||
var tracks = stream.getVideoTracks();
|
||||
if (!tracks || tracks.length === 0) {
|
||||
tracks = stream.getAudioTracks();
|
||||
}
|
||||
id = tracks[0].id;
|
||||
}
|
||||
return SDPUtil.filter_special_chars(tracks[0].id);
|
||||
return SDPUtil.filter_special_chars(id);
|
||||
};
|
||||
this.getVideoSrc = function (element) {
|
||||
if(!element)
|
||||
@@ -184,9 +191,7 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
RTCIceCandidate = mozRTCIceCandidate;
|
||||
} else {
|
||||
console.error(
|
||||
"Firefox requirements not met, ver: " + FFversion +
|
||||
", bundle: " + config.useBundle +
|
||||
", rtcp-mux: " + config.useRtcpMux);
|
||||
"Firefox version too old: " + FFversion + ". Required >= 40.");
|
||||
window.location.href = 'unsupported_browser.html';
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +218,7 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
};
|
||||
// DTLS should now be enabled by default but..
|
||||
this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
|
||||
if (navigator.userAgent.indexOf('Android') != -1) {
|
||||
if (RTCBrowserType.isAndroid()) {
|
||||
this.pc_constraints = {}; // disable DTLS on Android
|
||||
}
|
||||
if (!webkitMediaStream.prototype.getVideoTracks) {
|
||||
@@ -230,8 +235,8 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
// Detect IE/Safari
|
||||
else if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||
|
||||
AdapterJS.WebRTCPlugin.setLogLevel(
|
||||
AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS.VERBOSE);
|
||||
//AdapterJS.WebRTCPlugin.setLogLevel(
|
||||
// AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS.VERBOSE);
|
||||
|
||||
AdapterJS.webRTCReady(function (isPlugin) {
|
||||
|
||||
@@ -254,7 +259,6 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
console.warn("Attempt to get video SRC of null element");
|
||||
return null;
|
||||
}
|
||||
var src = null;
|
||||
var children = element.children;
|
||||
for (var i = 0; i !== children.length; ++i) {
|
||||
if (children[i].name === 'streamId') {
|
||||
@@ -289,14 +293,11 @@ function RTCUtils(RTCService, onTemasysPluginReady)
|
||||
|
||||
RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
um, success_callback, failure_callback, resolution,bandwidth, fps,
|
||||
desktopStream)
|
||||
{
|
||||
desktopStream) {
|
||||
currentResolution = resolution;
|
||||
// Check if we are running on Android device
|
||||
var isAndroid = navigator.userAgent.indexOf('Android') != -1;
|
||||
|
||||
var constraints = getConstraints(
|
||||
um, resolution, bandwidth, fps, desktopStream, isAndroid);
|
||||
um, resolution, bandwidth, fps, desktopStream);
|
||||
|
||||
console.info("Get media constraints", constraints);
|
||||
|
||||
@@ -327,16 +328,14 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
|
||||
|
||||
RTCUtils.prototype.setAvailableDevices = function (um, available) {
|
||||
var devices = {};
|
||||
if(um.indexOf("video") != -1)
|
||||
{
|
||||
if(um.indexOf("video") != -1) {
|
||||
devices.video = available;
|
||||
}
|
||||
if(um.indexOf("audio") != -1)
|
||||
{
|
||||
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
|
||||
@@ -362,8 +361,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||
|
||||
|
||||
if(usageOptions)
|
||||
for(var i = 0; i < devices.length; i++)
|
||||
{
|
||||
for(var i = 0; i < devices.length; i++) {
|
||||
var device = devices[i];
|
||||
if(usageOptions[device] === true)
|
||||
newDevices.push(device);
|
||||
@@ -371,8 +369,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||
else
|
||||
newDevices = devices;
|
||||
|
||||
if(newDevices.length === 0)
|
||||
{
|
||||
if(newDevices.length === 0) {
|
||||
successCallback();
|
||||
return;
|
||||
}
|
||||
@@ -433,7 +430,6 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||
},
|
||||
config.resolution || '360');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
RTCUtils.prototype.successCallback = function (stream, usageOptions) {
|
||||
@@ -463,8 +459,7 @@ RTCUtils.prototype.errorCallback = function (error) {
|
||||
return self.errorCallback(error);
|
||||
}, resolution);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
self.getUserMediaWithConstraints(
|
||||
['audio'],
|
||||
function (stream) {
|
||||
@@ -477,11 +472,9 @@ RTCUtils.prototype.errorCallback = function (error) {
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
||||
{
|
||||
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;
|
||||
@@ -526,7 +519,7 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
||||
this.service.createLocalStream(audioStream, "audio", null, null,
|
||||
audioMuted, audioGUM);
|
||||
|
||||
this.service.createLocalStream(videoStream, "video", null, null,
|
||||
this.service.createLocalStream(videoStream, "video", null, 'camera',
|
||||
videoMuted, videoGUM);
|
||||
};
|
||||
|
||||
@@ -534,8 +527,8 @@ function DummyMediaStream(id) {
|
||||
this.id = id;
|
||||
this.label = id;
|
||||
this.stop = function() { };
|
||||
this.getAudioTracks = function() { return []; }
|
||||
this.getVideoTracks = function() { return []; }
|
||||
this.getAudioTracks = function() { return []; };
|
||||
this.getVideoTracks = function() { return []; };
|
||||
}
|
||||
|
||||
RTCUtils.prototype.createStream = function(stream, isVideo) {
|
||||
@@ -545,7 +538,7 @@ RTCUtils.prototype.createStream = function(stream, isVideo) {
|
||||
if (newStream) {
|
||||
var tracks = (isVideo ? stream.getVideoTracks() : stream.getAudioTracks());
|
||||
|
||||
for (i = 0; i < tracks.length; i++) {
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
newStream.addTrack(tracks[i]);
|
||||
}
|
||||
}
|
||||
@@ -556,7 +549,8 @@ RTCUtils.prototype.createStream = function(stream, isVideo) {
|
||||
if (stream) {
|
||||
newStream = stream;
|
||||
} else {
|
||||
newStream = new DummyMediaStream(isVideo ? "dummyVideo" : "dummyAudio");
|
||||
newStream =
|
||||
new DummyMediaStream(isVideo ? "dummyVideo" : "dummyAudio");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*! adapterjs - v0.11.0 - 2015-06-08 */
|
||||
/*! adapterjs - custom version from - 2015-08-19 */
|
||||
|
||||
// Adapter's interface.
|
||||
var AdapterJS = AdapterJS || {};
|
||||
@@ -17,7 +17,7 @@ AdapterJS.options = AdapterJS.options || {};
|
||||
// AdapterJS.options.hidePluginInstallPrompt = true;
|
||||
|
||||
// AdapterJS version
|
||||
AdapterJS.VERSION = '0.11.0';
|
||||
AdapterJS.VERSION = '0.12.0';
|
||||
|
||||
// This function will be called when the WebRTC API is ready to be used
|
||||
// Whether it is the native implementation (Chrome, Firefox, Opera) or
|
||||
@@ -72,6 +72,12 @@ else if(!!navigator.platform.match(/^Win/i)) {
|
||||
AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN';
|
||||
}
|
||||
|
||||
AdapterJS.WebRTCPlugin.TAGS = {
|
||||
NONE : 'none',
|
||||
AUDIO : 'audio',
|
||||
VIDEO : 'video'
|
||||
};
|
||||
|
||||
// Unique identifier of each opened page
|
||||
AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2);
|
||||
|
||||
@@ -340,9 +346,24 @@ AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNe
|
||||
try {
|
||||
event.cancelBubble = true;
|
||||
} catch(error) { }
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
var pluginInstallInterval = setInterval(function(){
|
||||
if(! isIE) {
|
||||
navigator.plugins.refresh(false);
|
||||
}
|
||||
AdapterJS.WebRTCPlugin.isPluginInstalled(
|
||||
AdapterJS.WebRTCPlugin.pluginInfo.prefix,
|
||||
AdapterJS.WebRTCPlugin.pluginInfo.plugName,
|
||||
function() { // plugin now installed
|
||||
clearInterval(pluginInstallInterval);
|
||||
AdapterJS.WebRTCPlugin.defineWebRTCInterface();
|
||||
},
|
||||
function() {
|
||||
// still no plugin detected, nothing to do
|
||||
});
|
||||
} , 500);
|
||||
});
|
||||
} else {
|
||||
c.document.close();
|
||||
}
|
||||
AdapterJS.addEvent(c.document, 'click', function() {
|
||||
@@ -711,6 +732,28 @@ if (navigator.mozGetUserMedia) {
|
||||
return to;
|
||||
};
|
||||
|
||||
AdapterJS.maybeThroughWebRTCReady();
|
||||
} else if (navigator.mediaDevices && navigator.userAgent.match(
|
||||
/Edge\/(\d+).(\d+)$/)) {
|
||||
webrtcDetectedBrowser = 'edge';
|
||||
|
||||
webrtcDetectedVersion =
|
||||
parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10);
|
||||
|
||||
// the minimum version still supported by adapter.
|
||||
webrtcMinimumVersion = 12;
|
||||
|
||||
getUserMedia = navigator.getUserMedia;
|
||||
|
||||
attachMediaStream = function(element, stream) {
|
||||
element.srcObject = stream;
|
||||
return element;
|
||||
};
|
||||
reattachMediaStream = function(to, from) {
|
||||
to.srcObject = from.srcObject;
|
||||
return to;
|
||||
};
|
||||
|
||||
AdapterJS.maybeThroughWebRTCReady();
|
||||
} else { // TRY TO USE PLUGIN
|
||||
// IE 9 is not offering an implementation of console.log until you open a console
|
||||
@@ -794,8 +837,8 @@ if (navigator.mozGetUserMedia) {
|
||||
AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '" /> ' +
|
||||
'<param name="windowless" value="false" /> ' +
|
||||
'<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
|
||||
'<param name="onload" value="' + AdapterJS.WebRTCPlugin.pluginInfo.onload +
|
||||
'" />' +
|
||||
'<param name="onload" value="' + AdapterJS.WebRTCPlugin.pluginInfo.onload + '" />' +
|
||||
'<param name="tag" value="' + AdapterJS.WebRTCPlugin.TAGS.NONE + '" />' +
|
||||
// uncomment to be able to use virtual cams
|
||||
(AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
|
||||
|
||||
@@ -829,7 +872,8 @@ if (navigator.mozGetUserMedia) {
|
||||
AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '">' +
|
||||
'<param name="windowless" value="false" /> ' +
|
||||
(AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
|
||||
'<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '">';
|
||||
'<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '">' +
|
||||
'<param name="tag" value="' + AdapterJS.WebRTCPlugin.TAGS.NONE + '" />';
|
||||
document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);
|
||||
}
|
||||
|
||||
@@ -860,6 +904,12 @@ if (navigator.mozGetUserMedia) {
|
||||
};
|
||||
|
||||
AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
|
||||
if (AdapterJS.WebRTCPlugin.pluginState ===
|
||||
AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
|
||||
console.error("AdapterJS - WebRTC interface has already been defined");
|
||||
return;
|
||||
}
|
||||
|
||||
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING;
|
||||
|
||||
AdapterJS.isDefined = function (variable) {
|
||||
@@ -951,78 +1001,89 @@ if (navigator.mozGetUserMedia) {
|
||||
streamId = '';
|
||||
}
|
||||
else {
|
||||
stream.enableSoundTracks(true);
|
||||
stream.enableSoundTracks(true); // TODO: remove on 0.12.0
|
||||
streamId = stream.id;
|
||||
}
|
||||
|
||||
if (element.nodeName.toLowerCase() !== 'audio') {
|
||||
var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
|
||||
if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) {
|
||||
var frag = document.createDocumentFragment();
|
||||
var temp = document.createElement('div');
|
||||
var classHTML = '';
|
||||
if (element.className) {
|
||||
classHTML = 'class="' + element.className + '" ';
|
||||
} else if (element.attributes && element.attributes['class']) {
|
||||
classHTML = 'class="' + element.attributes['class'].value + '" ';
|
||||
var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
|
||||
var nodeName = element.nodeName.toLowerCase();
|
||||
if (nodeName !== 'object') { // not a plugin <object> tag yet
|
||||
var tag;
|
||||
switch(nodeName) {
|
||||
case 'audio':
|
||||
tag = AdapterJS.WebRTCPlugin.TAGS.AUDIO;
|
||||
break;
|
||||
case 'video':
|
||||
tag = AdapterJS.WebRTCPlugin.TAGS.VIDEO;
|
||||
break;
|
||||
default:
|
||||
tag = AdapterJS.WebRTCPlugin.TAGS.NONE;
|
||||
}
|
||||
|
||||
temp.innerHTML = '<object id="' + elementId + '" ' + classHTML +
|
||||
'type="' + AdapterJS.WebRTCPlugin.pluginInfo.type + '">' +
|
||||
'<param name="pluginId" value="' + elementId + '" /> ' +
|
||||
'<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
|
||||
'<param name="windowless" value="true" /> ' +
|
||||
'<param name="streamId" value="' + streamId + '" /> ' +
|
||||
'</object>';
|
||||
while (temp.firstChild) {
|
||||
frag.appendChild(temp.firstChild);
|
||||
}
|
||||
var frag = document.createDocumentFragment();
|
||||
var temp = document.createElement('div');
|
||||
var classHTML = '';
|
||||
if (element.className) {
|
||||
classHTML = 'class="' + element.className + '" ';
|
||||
} else if (element.attributes && element.attributes['class']) {
|
||||
classHTML = 'class="' + element.attributes['class'].value + '" ';
|
||||
}
|
||||
|
||||
var height = '';
|
||||
var width = '';
|
||||
if (element.getBoundingClientRect) {
|
||||
var rectObject = element.getBoundingClientRect();
|
||||
width = rectObject.width + 'px';
|
||||
height = rectObject.height + 'px';
|
||||
}
|
||||
else if (element.width) {
|
||||
width = element.width;
|
||||
height = element.height;
|
||||
} else {
|
||||
// TODO: What scenario could bring us here?
|
||||
}
|
||||
temp.innerHTML = '<object id="' + elementId + '" ' + classHTML +
|
||||
'type="' + AdapterJS.WebRTCPlugin.pluginInfo.type + '">' +
|
||||
'<param name="pluginId" value="' + elementId + '" /> ' +
|
||||
'<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
|
||||
'<param name="windowless" value="true" /> ' +
|
||||
'<param name="streamId" value="' + streamId + '" /> ' +
|
||||
'<param name="tag" value="' + tag + '" /> ' +
|
||||
'</object>';
|
||||
while (temp.firstChild) {
|
||||
frag.appendChild(temp.firstChild);
|
||||
}
|
||||
|
||||
element.parentNode.insertBefore(frag, element);
|
||||
frag = document.getElementById(elementId);
|
||||
frag.width = width;
|
||||
frag.height = height;
|
||||
element.parentNode.removeChild(element);
|
||||
var height = '';
|
||||
var width = '';
|
||||
if (element.getBoundingClientRect) {
|
||||
var rectObject = element.getBoundingClientRect();
|
||||
width = rectObject.width + 'px';
|
||||
height = rectObject.height + 'px';
|
||||
}
|
||||
else if (element.width) {
|
||||
width = element.width;
|
||||
height = element.height;
|
||||
} else {
|
||||
var children = element.children;
|
||||
for (var i = 0; i !== children.length; ++i) {
|
||||
if (children[i].name === 'streamId') {
|
||||
children[i].value = streamId;
|
||||
break;
|
||||
}
|
||||
// TODO: What scenario could bring us here?
|
||||
}
|
||||
|
||||
element.parentNode.insertBefore(frag, element);
|
||||
frag = document.getElementById(elementId);
|
||||
frag.width = width;
|
||||
frag.height = height;
|
||||
element.parentNode.removeChild(element);
|
||||
} else { // already an <object> tag, just change the stream id
|
||||
var children = element.children;
|
||||
for (var i = 0; i !== children.length; ++i) {
|
||||
if (children[i].name === 'streamId') {
|
||||
children[i].value = streamId;
|
||||
break;
|
||||
}
|
||||
element.setStreamId(streamId);
|
||||
}
|
||||
var newElement = document.getElementById(elementId);
|
||||
newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
|
||||
if (isIE) { // on IE the event needs to be plugged manually
|
||||
newElement.attachEvent('onplaying', newElement.onplaying);
|
||||
newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
|
||||
newElement._TemOnClick = function (id) {
|
||||
var arg = {
|
||||
srcElement : document.getElementById(id)
|
||||
};
|
||||
newElement.onclick(arg);
|
||||
};
|
||||
}
|
||||
return newElement;
|
||||
} else {
|
||||
return element;
|
||||
element.setStreamId(streamId);
|
||||
}
|
||||
var newElement = document.getElementById(elementId);
|
||||
newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
|
||||
newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
|
||||
if (isIE) { // on IE the event needs to be plugged manually
|
||||
newElement.attachEvent('onplaying', newElement.onplaying);
|
||||
newElement._TemOnClick = function (id) {
|
||||
var arg = {
|
||||
srcElement : document.getElementById(id)
|
||||
};
|
||||
newElement.onclick(arg);
|
||||
};
|
||||
}
|
||||
|
||||
return newElement;
|
||||
};
|
||||
|
||||
reattachMediaStream = function (to, from) {
|
||||
@@ -1100,211 +1161,3 @@ if (navigator.mozGetUserMedia) {
|
||||
AdapterJS.WebRTCPlugin.defineWebRTCInterface,
|
||||
AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
var baseGetUserMedia = null;
|
||||
|
||||
AdapterJS.TEXT.EXTENSION = {
|
||||
REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.',
|
||||
REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.',
|
||||
REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.',
|
||||
BUTTON_FF: 'Install Now',
|
||||
BUTTON_CHROME: 'Go to Chrome Web Store'
|
||||
};
|
||||
|
||||
var clone = function(obj) {
|
||||
if (null == obj || "object" != typeof obj) return obj;
|
||||
var copy = obj.constructor();
|
||||
for (var attr in obj) {
|
||||
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
|
||||
}
|
||||
return copy;
|
||||
};
|
||||
|
||||
if (window.navigator.mozGetUserMedia) {
|
||||
baseGetUserMedia = window.navigator.getUserMedia;
|
||||
|
||||
navigator.getUserMedia = function (constraints, successCb, failureCb) {
|
||||
|
||||
if (constraints && constraints.video && !!constraints.video.mediaSource) {
|
||||
// intercepting screensharing requests
|
||||
|
||||
if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') {
|
||||
throw new Error('Only "screen" and "window" option is available as mediaSource');
|
||||
}
|
||||
|
||||
var updatedConstraints = clone(constraints);
|
||||
|
||||
//constraints.video.mediaSource = constraints.video.mediaSource;
|
||||
updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource;
|
||||
|
||||
// so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked.
|
||||
// strange but this works anyway
|
||||
var checkIfReady = setInterval(function () {
|
||||
if (document.readyState === 'complete') {
|
||||
clearInterval(checkIfReady);
|
||||
|
||||
baseGetUserMedia(updatedConstraints, successCb, function (error) {
|
||||
if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
|
||||
AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
|
||||
AdapterJS.TEXT.EXTENSION.BUTTON_FF,
|
||||
'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
|
||||
//window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
|
||||
} else {
|
||||
failureCb(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1);
|
||||
|
||||
} else { // regular GetUserMediaRequest
|
||||
baseGetUserMedia(constraints, successCb, failureCb);
|
||||
}
|
||||
};
|
||||
|
||||
getUserMedia = navigator.getUserMedia;
|
||||
|
||||
} else if (window.navigator.webkitGetUserMedia) {
|
||||
baseGetUserMedia = window.navigator.getUserMedia;
|
||||
|
||||
navigator.getUserMedia = function (constraints, successCb, failureCb) {
|
||||
|
||||
if (constraints && constraints.video && !!constraints.video.mediaSource) {
|
||||
if (window.webrtcDetectedBrowser !== 'chrome') {
|
||||
throw new Error('Current browser does not support screensharing');
|
||||
}
|
||||
|
||||
// would be fine since no methods
|
||||
var updatedConstraints = clone(constraints);
|
||||
|
||||
var chromeCallback = function(error, sourceId) {
|
||||
if(!error) {
|
||||
updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {};
|
||||
updatedConstraints.video.mandatory.chromeMediaSource = 'desktop';
|
||||
updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920;
|
||||
updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080;
|
||||
|
||||
if (sourceId) {
|
||||
updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
|
||||
delete updatedConstraints.video.mediaSource;
|
||||
|
||||
baseGetUserMedia(updatedConstraints, successCb, failureCb);
|
||||
|
||||
} else {
|
||||
if (error === 'permission-denied') {
|
||||
throw new Error('Permission denied for screen retrieval');
|
||||
} else {
|
||||
throw new Error('Failed retrieving selected screen');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var onIFrameCallback = function (event) {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data.chromeMediaSourceId) {
|
||||
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
|
||||
chromeCallback('permission-denied');
|
||||
} else {
|
||||
chromeCallback(null, event.data.chromeMediaSourceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
if (event.data.chromeExtensionStatus === 'not-installed') {
|
||||
AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,
|
||||
AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,
|
||||
event.data.data, true, true);
|
||||
} else {
|
||||
chromeCallback(event.data.chromeExtensionStatus, null);
|
||||
}
|
||||
}
|
||||
|
||||
// this event listener is no more needed
|
||||
window.removeEventListener('message', onIFrameCallback);
|
||||
};
|
||||
|
||||
window.addEventListener('message', onIFrameCallback);
|
||||
|
||||
postFrameMessage({
|
||||
captureSourceId: true
|
||||
});
|
||||
|
||||
} else {
|
||||
baseGetUserMedia(constraints, successCb, failureCb);
|
||||
}
|
||||
};
|
||||
|
||||
getUserMedia = navigator.getUserMedia;
|
||||
|
||||
} else {
|
||||
baseGetUserMedia = window.navigator.getUserMedia;
|
||||
|
||||
navigator.getUserMedia = function (constraints, successCb, failureCb) {
|
||||
if (constraints && constraints.video && !!constraints.video.mediaSource) {
|
||||
// would be fine since no methods
|
||||
var updatedConstraints = clone(constraints);
|
||||
|
||||
// wait for plugin to be ready
|
||||
AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
|
||||
// check if screensharing feature is available
|
||||
if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature &&
|
||||
!!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
|
||||
|
||||
|
||||
// set the constraints
|
||||
updatedConstraints.video.optional = updatedConstraints.video.optional || [];
|
||||
updatedConstraints.video.optional.push({
|
||||
sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing'
|
||||
});
|
||||
|
||||
delete updatedConstraints.video.mediaSource;
|
||||
} else {
|
||||
throw new Error('Your WebRTC plugin does not support screensharing');
|
||||
}
|
||||
baseGetUserMedia(updatedConstraints, successCb, failureCb);
|
||||
});
|
||||
} else {
|
||||
baseGetUserMedia(constraints, successCb, failureCb);
|
||||
}
|
||||
};
|
||||
|
||||
getUserMedia = window.navigator.getUserMedia;
|
||||
}
|
||||
|
||||
if (window.webrtcDetectedBrowser === 'chrome') {
|
||||
var iframe = document.createElement('iframe');
|
||||
|
||||
iframe.onload = function() {
|
||||
iframe.isLoaded = true;
|
||||
};
|
||||
|
||||
iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html';
|
||||
//'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html';
|
||||
iframe.style.display = 'none';
|
||||
|
||||
(document.body || document.documentElement).appendChild(iframe);
|
||||
|
||||
var postFrameMessage = function (object) {
|
||||
object = object || {};
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(function () {
|
||||
iframe.contentWindow.postMessage(object, '*');
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
iframe.contentWindow.postMessage(object, '*');
|
||||
};
|
||||
}
|
||||
})();
|
||||
449
modules/UI/UI.js
449
modules/UI/UI.js
@@ -1,3 +1,4 @@
|
||||
/* global Strophe, APP, $, config, interfaceConfig, toastr */
|
||||
var UI = {};
|
||||
|
||||
var VideoLayout = require("./videolayout/VideoLayout.js");
|
||||
@@ -20,6 +21,7 @@ var messageHandler = UI.messageHandler;
|
||||
var Authentication = require("./authentication/Authentication");
|
||||
var UIUtil = require("./util/UIUtil");
|
||||
var NicknameHandler = require("./util/NicknameHandler");
|
||||
var JitsiPopover = require("./util/JitsiPopover");
|
||||
var CQEvents = require("../../service/connectionquality/CQEvents");
|
||||
var DesktopSharingEventTypes
|
||||
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
||||
@@ -27,9 +29,11 @@ var RTCEvents = require("../../service/RTC/RTCEvents");
|
||||
var RTCBrowserType = require("../RTC/RTCBrowserType");
|
||||
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var UIEvents = require("../../service/UI/UIEvents");
|
||||
var MemberEvents = require("../../service/members/Events");
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
var roomNode = null;
|
||||
var roomName = null;
|
||||
|
||||
|
||||
@@ -78,23 +82,18 @@ function promptDisplayName() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function notifyForInitialMute()
|
||||
{
|
||||
function notifyForInitialMute() {
|
||||
messageHandler.notify(null, "notify.mutedTitle", "connected",
|
||||
"notify.muted", null, {timeOut: 120000});
|
||||
}
|
||||
|
||||
function setupPrezi()
|
||||
{
|
||||
$("#reloadPresentationLink").click(function()
|
||||
{
|
||||
function setupPrezi() {
|
||||
$("#reloadPresentationLink").click(function() {
|
||||
Prezi.reloadPresentation();
|
||||
});
|
||||
}
|
||||
|
||||
function setupChat()
|
||||
{
|
||||
function setupChat() {
|
||||
Chat.init();
|
||||
$("#toggle_smileys").click(function() {
|
||||
Chat.toggleSmileys();
|
||||
@@ -108,8 +107,7 @@ function setupToolbars() {
|
||||
}
|
||||
|
||||
function streamHandler(stream, isMuted) {
|
||||
switch (stream.type)
|
||||
{
|
||||
switch (stream.type) {
|
||||
case "audio":
|
||||
VideoLayout.changeLocalAudio(stream, isMuted);
|
||||
break;
|
||||
@@ -151,17 +149,16 @@ function onDisplayNameChanged(jid, displayName) {
|
||||
}
|
||||
|
||||
function registerListeners() {
|
||||
APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
|
||||
|
||||
APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
|
||||
APP.RTC.addStreamListener(streamHandler,
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
|
||||
APP.RTC.addStreamListener(streamHandler,
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
|
||||
APP.RTC.addStreamListener(function (stream) {
|
||||
VideoLayout.onRemoteStreamAdded(stream);
|
||||
}, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
|
||||
APP.RTC.addStreamListener(function (jid) {
|
||||
VideoLayout.onVideoTypeChanged(jid);
|
||||
}, StreamEventTypes.EVENT_TYPE_REMOTE_CHANGED);
|
||||
APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged);
|
||||
APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (resourceJid) {
|
||||
APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED,
|
||||
function (resourceJid) {
|
||||
VideoLayout.onDominantSpeakerChanged(resourceJid);
|
||||
});
|
||||
APP.RTC.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
|
||||
@@ -172,25 +169,29 @@ function registerListeners() {
|
||||
APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED,
|
||||
function (devices) {
|
||||
VideoLayout.setDeviceAvailabilityIcons(null, devices);
|
||||
})
|
||||
APP.statistics.addAudioLevelListener(function(jid, audioLevel)
|
||||
{
|
||||
});
|
||||
APP.RTC.addListener(RTCEvents.VIDEO_MUTE, UI.setVideoMuteButtonsState);
|
||||
APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function () {
|
||||
// when the data channel becomes available, tell the bridge about video
|
||||
// selections so that it can do adaptive simulcast,
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
var userResource = APP.UI.getLargeVideoResource();
|
||||
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, userResource);
|
||||
});
|
||||
APP.statistics.addAudioLevelListener(function(jid, audioLevel) {
|
||||
var resourceJid;
|
||||
if(jid === APP.statistics.LOCAL_JID)
|
||||
{
|
||||
if(jid === APP.statistics.LOCAL_JID) {
|
||||
resourceJid = AudioLevels.LOCAL_LEVEL;
|
||||
if(APP.RTC.localAudio.isMuted())
|
||||
{
|
||||
if(APP.RTC.localAudio.isMuted()) {
|
||||
audioLevel = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
resourceJid = Strophe.getResourceFromJid(jid);
|
||||
}
|
||||
|
||||
AudioLevels.updateAudioLevel(resourceJid, audioLevel,
|
||||
UI.getLargeVideoJid());
|
||||
UI.getLargeVideoResource());
|
||||
});
|
||||
APP.desktopsharing.addListener(function () {
|
||||
ToolbarToggler.showDesktopSharingButton();
|
||||
@@ -221,8 +222,7 @@ function registerListeners() {
|
||||
title,
|
||||
message,
|
||||
true, {},
|
||||
function (event, value, message, formVals)
|
||||
{
|
||||
function (event, value, message, formVals) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
@@ -237,8 +237,7 @@ function registerListeners() {
|
||||
var title = APP.translation.generateTranslationHTML("dialog.sessTerminated");
|
||||
messageHandler.openDialog(
|
||||
title, reason, true, {},
|
||||
function (event, value, message, formVals)
|
||||
{
|
||||
function (event, value, message, formVals) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
@@ -257,25 +256,93 @@ function registerListeners() {
|
||||
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_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.PARTICIPANT_VIDEO_TYPE_CHANGED,
|
||||
onPeerVideoTypeChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE,
|
||||
function (resource, devices) {
|
||||
VideoLayout.setDeviceAvailabilityIcons(resource, devices);
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED, VideoLayout.onAudioMute);
|
||||
APP.xmpp.addListener(XMPPEvents.VIDEO_MUTED, VideoLayout.onVideoMute);
|
||||
APP.xmpp.addListener(XMPPEvents.PARTICIPANT_AUDIO_MUTED,
|
||||
VideoLayout.onAudioMute);
|
||||
APP.xmpp.addListener(XMPPEvents.PARTICIPANT_VIDEO_MUTED,
|
||||
VideoLayout.onVideoMute);
|
||||
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function (doMuteAudio) {
|
||||
UI.setAudioMuted(doMuteAudio);
|
||||
});
|
||||
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
||||
onDtmfSupportChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
|
||||
onDtmfSupportChanged);
|
||||
APP.xmpp.addListener(XMPPEvents.START_MUTED_SETTING_CHANGED, function (audio, video) {
|
||||
SettingsMenu.setStartMuted(audio, video);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.START_MUTED_FROM_FOCUS, function (audio, video) {
|
||||
UI.setInitialMuteFromFocus(audio, video);
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function (session, error) {
|
||||
UI.messageHandler.showError("dialog.sorry",
|
||||
"dialog.internalError");
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function () {
|
||||
messageHandler.showError("dialog.error",
|
||||
"dialog.SLDFailure");
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function () {
|
||||
messageHandler.showError("dialog.error",
|
||||
"dialog.SRDFailure");
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function () {
|
||||
messageHandler.showError();
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function () {
|
||||
// FIXME: re-use LoginDialog which supports retries
|
||||
UI.showLoginPopup(connect);
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focusComponent, retrySec) {
|
||||
UI.messageHandler.notify(
|
||||
null, "notify.focus",
|
||||
'disconnected', "notify.focusFail",
|
||||
{component: focusComponent, ms: retrySec});
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
|
||||
UI.messageHandler.openReportDialog(null,
|
||||
"dialog.joinError", pres);
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
|
||||
UI.messageHandler.openReportDialog(null,
|
||||
"dialog.connectError", pres);
|
||||
});
|
||||
|
||||
APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function () {
|
||||
var roomName = UI.generateRoomName();
|
||||
APP.xmpp.allocateConferenceFocus(roomName, UI.checkForNicknameAndJoin);
|
||||
});
|
||||
|
||||
//NicknameHandler emits this event
|
||||
UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
|
||||
APP.xmpp.addToPresence("displayName", nickname);
|
||||
});
|
||||
|
||||
UI.addListener(UIEvents.LARGEVIDEO_INIT, function () {
|
||||
AudioLevels.init();
|
||||
});
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
|
||||
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
|
||||
// Listens for video interruption events.
|
||||
APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted);
|
||||
// Listens for video restores events.
|
||||
APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,14 +361,12 @@ function setVideoMute(mute, options) {
|
||||
options);
|
||||
}
|
||||
|
||||
function onResize()
|
||||
{
|
||||
function onResize() {
|
||||
Chat.resizeChat();
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
}
|
||||
|
||||
function bindEvents()
|
||||
{
|
||||
function bindEvents() {
|
||||
/**
|
||||
* Resizes and repositions videos in full screen mode.
|
||||
*/
|
||||
@@ -313,85 +378,64 @@ function bindEvents()
|
||||
|
||||
UI.start = function (init) {
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
var setupWelcomePage = null;
|
||||
if(config.enableWelcomePage && window.location.pathname == "/" &&
|
||||
(!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
|
||||
{
|
||||
(!window.localStorage.welcomePageDisabled ||
|
||||
window.localStorage.welcomePageDisabled == "false")) {
|
||||
$("#videoconference_page").hide();
|
||||
var setupWelcomePage = require("./welcome_page/WelcomePage");
|
||||
if (!setupWelcomePage)
|
||||
setupWelcomePage = require("./welcome_page/WelcomePage");
|
||||
setupWelcomePage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
||||
var leftWatermarkDiv
|
||||
= $("#largeVideoContainer div[class='watermark leftwatermark']");
|
||||
|
||||
leftWatermarkDiv.css({display: 'block'});
|
||||
leftWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.JITSI_WATERMARK_LINK;
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
var rightWatermarkDiv
|
||||
= $("#largeVideoContainer div[class='watermark rightwatermark']");
|
||||
|
||||
rightWatermarkDiv.css({display: 'block'});
|
||||
rightWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.BRAND_WATERMARK_LINK;
|
||||
rightWatermarkDiv.get(0).style.backgroundImage
|
||||
= "url(images/rightwatermark.png)";
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_POWERED_BY) {
|
||||
$("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
|
||||
}
|
||||
|
||||
$("#welcome_page").hide();
|
||||
|
||||
$("#videospace").mousemove(function () {
|
||||
return ToolbarToggler.showToolbar();
|
||||
});
|
||||
// Set the defaults for prompt dialogs.
|
||||
jQuery.prompt.setDefaults({persistent: false});
|
||||
$.prompt.setDefaults({persistent: false});
|
||||
|
||||
|
||||
registerListeners();
|
||||
|
||||
VideoLayout.init(eventEmitter);
|
||||
AudioLevels.init();
|
||||
NicknameHandler.init(eventEmitter);
|
||||
registerListeners();
|
||||
|
||||
bindEvents();
|
||||
setupPrezi();
|
||||
setupToolbars();
|
||||
setupChat();
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
$("#videospace").mousemove(function () {
|
||||
return ToolbarToggler.showToolbar();
|
||||
});
|
||||
setupToolbars();
|
||||
setupChat();
|
||||
// Display notice message at the top of the toolbar
|
||||
if (config.noticeMessage) {
|
||||
$('#noticeText').text(config.noticeMessage);
|
||||
$('#notice').css({display: 'block'});
|
||||
}
|
||||
$("#downloadlog").click(function (event) {
|
||||
dump(event.target);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#header").css("display", "none");
|
||||
$("#bottomToolbar").css("display", "none");
|
||||
$("#downloadlog").css("display", "none");
|
||||
$("#remoteVideos").css("padding", "0px 0px 18px 0px");
|
||||
$("#remoteVideos").css("right", "0px");
|
||||
messageHandler.disableNotifications();
|
||||
$('body').popover("disable");
|
||||
// $("[data-toggle=popover]").popover("disable");
|
||||
JitsiPopover.enabled = false;
|
||||
}
|
||||
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
|
||||
$("#downloadlog").click(function (event) {
|
||||
dump(event.target);
|
||||
});
|
||||
|
||||
if(config.enableWelcomePage && window.location.pathname == "/" &&
|
||||
(!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
|
||||
{
|
||||
$("#videoconference_page").hide();
|
||||
var setupWelcomePage = require("./welcome_page/WelcomePage");
|
||||
setupWelcomePage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$("#welcome_page").hide();
|
||||
|
||||
// Display notice message at the top of the toolbar
|
||||
if (config.noticeMessage) {
|
||||
$('#noticeText').text(config.noticeMessage);
|
||||
$('#notice').css({display: 'block'});
|
||||
}
|
||||
|
||||
if (!RTCBrowserType.isIExplorer()) {
|
||||
document.getElementById('largeVideo').volume = 0;
|
||||
}
|
||||
|
||||
if(config.requireDisplayName) {
|
||||
var currentSettings = Settings.getSettings();
|
||||
@@ -402,42 +446,43 @@ UI.start = function (init) {
|
||||
|
||||
init();
|
||||
|
||||
toastr.options = {
|
||||
"closeButton": true,
|
||||
"debug": false,
|
||||
"positionClass": "notification-bottom-right",
|
||||
"onclick": null,
|
||||
"showDuration": "300",
|
||||
"hideDuration": "1000",
|
||||
"timeOut": "2000",
|
||||
"extendedTimeOut": "1000",
|
||||
"showEasing": "swing",
|
||||
"hideEasing": "linear",
|
||||
"showMethod": "fadeIn",
|
||||
"hideMethod": "fadeOut",
|
||||
"reposition": function() {
|
||||
if(PanelToggler.isVisible()) {
|
||||
$("#toast-container").addClass("notification-bottom-right-center");
|
||||
} else {
|
||||
$("#toast-container").removeClass("notification-bottom-right-center");
|
||||
}
|
||||
},
|
||||
"newestOnTop": false
|
||||
};
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
toastr.options = {
|
||||
"closeButton": true,
|
||||
"debug": false,
|
||||
"positionClass": "notification-bottom-right",
|
||||
"onclick": null,
|
||||
"showDuration": "300",
|
||||
"hideDuration": "1000",
|
||||
"timeOut": "2000",
|
||||
"extendedTimeOut": "1000",
|
||||
"showEasing": "swing",
|
||||
"hideEasing": "linear",
|
||||
"showMethod": "fadeIn",
|
||||
"hideMethod": "fadeOut",
|
||||
"reposition": function () {
|
||||
if (PanelToggler.isVisible()) {
|
||||
$("#toast-container").addClass("notification-bottom-right-center");
|
||||
} else {
|
||||
$("#toast-container").removeClass("notification-bottom-right-center");
|
||||
}
|
||||
},
|
||||
"newestOnTop": false
|
||||
};
|
||||
|
||||
SettingsMenu.init();
|
||||
|
||||
SettingsMenu.init();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function chatAddError(errorMessage, originalText)
|
||||
{
|
||||
function chatAddError(errorMessage, originalText) {
|
||||
return Chat.chatAddError(errorMessage, originalText);
|
||||
};
|
||||
}
|
||||
|
||||
function chatSetSubject(text)
|
||||
{
|
||||
function chatSetSubject(text) {
|
||||
return Chat.chatSetSubject(text);
|
||||
};
|
||||
}
|
||||
|
||||
function updateChatConversation(from, displayName, message, myjid, stamp) {
|
||||
return Chat.updateChatConversation(from, displayName, message, myjid, stamp);
|
||||
@@ -449,14 +494,18 @@ function onMucJoined(jid, info) {
|
||||
$("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")");
|
||||
|
||||
var settings = Settings.getSettings();
|
||||
|
||||
// Make sure we configure our avatar id, before creating avatar for us
|
||||
Avatar.setUserAvatar(jid, settings.email || settings.uid);
|
||||
|
||||
// Add myself to the contact list.
|
||||
ContactList.addContact(jid, settings.email || settings.uid);
|
||||
ContactList.addContact(jid);
|
||||
|
||||
// Once we've joined the muc show the toolbar
|
||||
ToolbarToggler.showToolbar();
|
||||
|
||||
var displayName = !config.displayJids
|
||||
? info.displayName : Strophe.getResourceFromJid(jid);
|
||||
var displayName =
|
||||
config.displayJids ? Strophe.getResourceFromJid(jid) : info.displayName;
|
||||
|
||||
if (displayName)
|
||||
onDisplayNameChanged('localVideoContainer', displayName);
|
||||
@@ -476,32 +525,17 @@ function onMucMemberLeft(jid) {
|
||||
messageHandler.notify(displayName,'notify.somebody',
|
||||
'disconnected',
|
||||
'notify.disconnected');
|
||||
if(!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
if (!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size()) {
|
||||
UIUtil.playSoundNotification('userLeft');
|
||||
// Need to call this with a slight delay, otherwise the element couldn't be
|
||||
// found for some reason.
|
||||
// XXX(gp) it works fine without the timeout for me (with Chrome 38).
|
||||
window.setTimeout(function () {
|
||||
var container = document.getElementById(
|
||||
'participant_' + Strophe.getResourceFromJid(jid));
|
||||
if (container) {
|
||||
ContactList.removeContact(jid);
|
||||
VideoLayout.removeConnectionIndicator(jid);
|
||||
// hide here, wait for video to close before removing
|
||||
$(container).hide();
|
||||
VideoLayout.resizeThumbnails();
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
ContactList.removeContact(jid);
|
||||
|
||||
VideoLayout.participantLeft(jid);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
function onLocalRoleChanged(jid, info, pres, isModerator)
|
||||
{
|
||||
|
||||
function onLocalRoleChanged(jid, info, pres, isModerator) {
|
||||
console.info("My role changed, new role: " + info.role);
|
||||
onModeratorStatusChanged(isModerator);
|
||||
VideoLayout.showModeratorIndicator();
|
||||
@@ -511,11 +545,12 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
|
||||
Authentication.closeAuthenticationWindow();
|
||||
messageHandler.notify(null, "notify.me",
|
||||
'connected', "notify.moderator");
|
||||
|
||||
Toolbar.checkAutoRecord();
|
||||
}
|
||||
}
|
||||
|
||||
function onModeratorStatusChanged(isModerator) {
|
||||
|
||||
Toolbar.showSipCallButton(isModerator);
|
||||
Toolbar.showRecordingButton(
|
||||
isModerator); //&&
|
||||
@@ -569,17 +604,25 @@ function onMucMemberJoined(jid, id, displayName) {
|
||||
'connected',
|
||||
'notify.connected');
|
||||
|
||||
if(!config.startAudioMuted ||
|
||||
if (!config.startAudioMuted ||
|
||||
config.startAudioMuted > APP.members.size())
|
||||
UIUtil.playSoundNotification('userJoined');
|
||||
|
||||
// Configure avatar
|
||||
Avatar.setUserAvatar(jid, id);
|
||||
|
||||
// Add Peer's container
|
||||
VideoLayout.ensurePeerContainerExists(jid,id);
|
||||
VideoLayout.ensurePeerContainerExists(jid);
|
||||
}
|
||||
|
||||
function onMucPresenceStatus(jid, info) {
|
||||
VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status);
|
||||
}
|
||||
|
||||
function onPeerVideoTypeChanged(resourceJid, newVideoType) {
|
||||
VideoLayout.onVideoTypeChanged(resourceJid, newVideoType);
|
||||
}
|
||||
|
||||
function onMucRoleChanged(role, displayName) {
|
||||
VideoLayout.showModeratorIndicator();
|
||||
|
||||
@@ -588,8 +631,7 @@ function onMucRoleChanged(role, displayName) {
|
||||
if (!displayName) {
|
||||
messageKey = "notify.grantedToUnknown";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
messageKey = "notify.grantedTo";
|
||||
messageOptions = {to: displayName};
|
||||
}
|
||||
@@ -605,7 +647,7 @@ function onAuthenticationRequired(intervalCallback) {
|
||||
roomName, intervalCallback, function () {
|
||||
Toolbar.authenticateClicked();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function onLastNChanged(oldValue, newValue) {
|
||||
@@ -639,23 +681,20 @@ UI.inputDisplayNameHandler = function (value) {
|
||||
VideoLayout.inputDisplayNameHandler(value);
|
||||
};
|
||||
|
||||
|
||||
UI.getLargeVideoJid = function()
|
||||
{
|
||||
return VideoLayout.getLargeVideoJid();
|
||||
UI.getLargeVideoResource = function () {
|
||||
return VideoLayout.getLargeVideoResource();
|
||||
};
|
||||
|
||||
UI.generateRoomName = function() {
|
||||
if(roomName)
|
||||
return roomName;
|
||||
var roomnode = null;
|
||||
UI.getRoomNode = function () {
|
||||
if (roomNode)
|
||||
return roomNode;
|
||||
var path = window.location.pathname;
|
||||
|
||||
// determinde the room node from the url
|
||||
// TODO: just the roomnode or the whole bare jid?
|
||||
if (config.getroomnode && typeof config.getroomnode === 'function') {
|
||||
// custom function might be responsible for doing the pushstate
|
||||
roomnode = config.getroomnode(path);
|
||||
roomNode = config.getroomnode(path);
|
||||
} else {
|
||||
/* fall back to default strategy
|
||||
* this is making assumptions about how the URL->room mapping happens.
|
||||
@@ -666,28 +705,31 @@ UI.generateRoomName = function() {
|
||||
}
|
||||
*/
|
||||
if (path.length > 1) {
|
||||
roomnode = path.substr(1).toLowerCase();
|
||||
roomNode = path.substr(1).toLowerCase();
|
||||
} else {
|
||||
var word = RoomNameGenerator.generateRoomWithoutSeparator();
|
||||
roomnode = word.toLowerCase();
|
||||
|
||||
roomNode = word.toLowerCase();
|
||||
window.history.pushState('VideoChat',
|
||||
'Room: ' + word, window.location.pathname + word);
|
||||
'Room: ' + word, window.location.pathname + word);
|
||||
}
|
||||
}
|
||||
return roomNode;
|
||||
};
|
||||
|
||||
roomName = roomnode + '@' + config.hosts.muc;
|
||||
UI.generateRoomName = function () {
|
||||
if (roomName)
|
||||
return roomName;
|
||||
var roomNode = UI.getRoomNode();
|
||||
roomName = roomNode + '@' + config.hosts.muc;
|
||||
return roomName;
|
||||
};
|
||||
|
||||
|
||||
UI.connectionIndicatorShowMore = function(jid)
|
||||
{
|
||||
UI.connectionIndicatorShowMore = function(jid) {
|
||||
return VideoLayout.showMore(jid);
|
||||
};
|
||||
|
||||
UI.showLoginPopup = function(callback)
|
||||
{
|
||||
UI.showLoginPopup = function(callback) {
|
||||
console.log('password is required');
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
message += APP.translation.translateString(
|
||||
@@ -711,7 +753,7 @@ UI.showLoginPopup = function(callback)
|
||||
null, null, ':input:first'
|
||||
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UI.checkForNicknameAndJoin = function () {
|
||||
|
||||
@@ -730,12 +772,12 @@ function dump(elem, filename) {
|
||||
elem = elem.parentNode;
|
||||
elem.download = filename || 'meetlog.json';
|
||||
elem.href = 'data:application/json;charset=utf-8,\n';
|
||||
var data = APP.xmpp.populateData();
|
||||
var data = APP.xmpp.getJingleLog();
|
||||
var metadata = {};
|
||||
metadata.time = new Date();
|
||||
metadata.url = window.location.href;
|
||||
metadata.ua = navigator.userAgent;
|
||||
var log = APP.xmpp.getLogger();
|
||||
var log = APP.xmpp.getXmppLog();
|
||||
if (log) {
|
||||
metadata.xmpp = log;
|
||||
}
|
||||
@@ -749,10 +791,13 @@ UI.getRoomName = function () {
|
||||
};
|
||||
|
||||
UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
|
||||
if(muteAudio || muteVideo) notifyForInitialMute();
|
||||
if(muteAudio) UI.setAudioMuted(true);
|
||||
if(muteVideo) UI.setVideoMute(true);
|
||||
}
|
||||
if (muteAudio || muteVideo)
|
||||
notifyForInitialMute();
|
||||
if (muteAudio)
|
||||
UI.setAudioMuted(true);
|
||||
if (muteVideo)
|
||||
UI.setVideoMute(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutes/unmutes the local video.
|
||||
@@ -773,50 +818,48 @@ UI.toggleAudio = function() {
|
||||
*/
|
||||
UI.setAudioMuted = function (mute, earlyMute) {
|
||||
var audioMute = null;
|
||||
if(earlyMute)
|
||||
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);
|
||||
};
|
||||
if (!audioMute(mute, function () {
|
||||
VideoLayout.showLocalAudioIndicator(mute);
|
||||
|
||||
UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
}))
|
||||
{
|
||||
UIUtil.buttonClick("#toolbar_button_mute", "icon-microphone icon-mic-disabled");
|
||||
})) {
|
||||
// We still click the button.
|
||||
UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
|
||||
UIUtil.buttonClick("#toolbar_button_mute", "icon-microphone icon-mic-disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
UI.addListener = function (type, listener) {
|
||||
eventEmitter.on(type, listener);
|
||||
}
|
||||
};
|
||||
|
||||
UI.clickOnVideo = function (videoNumber) {
|
||||
var remoteVideos = $(".videocontainer:not(#mixedstream)");
|
||||
if (remoteVideos.length > videoNumber) {
|
||||
remoteVideos[videoNumber].click();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Used by torture
|
||||
UI.showToolbar = function () {
|
||||
return ToolbarToggler.showToolbar();
|
||||
}
|
||||
};
|
||||
|
||||
//Used by torture
|
||||
UI.dockToolbar = function (isDock) {
|
||||
return ToolbarToggler.dockToolbar(isDock);
|
||||
}
|
||||
};
|
||||
|
||||
UI.setVideoMuteButtonsState = function (mute) {
|
||||
var video = $('#video');
|
||||
var video = $('#toolbar_button_camera');
|
||||
var communicativeClass = "icon-camera";
|
||||
var muteClass = "icon-camera icon-camera-disabled";
|
||||
|
||||
@@ -827,14 +870,14 @@ UI.setVideoMuteButtonsState = function (mute) {
|
||||
video.removeClass(muteClass);
|
||||
video.addClass(communicativeClass);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UI.userAvatarChanged = function (resourceJid, thumbUrl, contactListUrl) {
|
||||
VideoLayout.userAvatarChanged(resourceJid, thumbUrl);
|
||||
ContactList.userAvatarChanged(resourceJid, contactListUrl);
|
||||
if(resourceJid === APP.xmpp.myResource())
|
||||
SettingsMenu.changeAvatar(thumbUrl);
|
||||
}
|
||||
};
|
||||
|
||||
UI.setVideoMute = setVideoMute;
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/* global APP, interfaceConfig, $, Strophe */
|
||||
var CanvasUtil = require("./CanvasUtils");
|
||||
|
||||
var ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
|
||||
var ASDrawContext = null;
|
||||
|
||||
function initActiveSpeakerAudioLevels() {
|
||||
var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
|
||||
var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2;
|
||||
|
||||
// Draw a circle.
|
||||
// Draw a circle.
|
||||
ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
|
||||
|
||||
// Add a shadow around the circle
|
||||
// Add a shadow around the circle
|
||||
ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
|
||||
ASDrawContext.shadowOffsetX = 0;
|
||||
ASDrawContext.shadowOffsetY = 0;
|
||||
@@ -24,8 +25,9 @@ var AudioLevels = (function(my) {
|
||||
my.LOCAL_LEVEL = 'local';
|
||||
|
||||
my.init = function () {
|
||||
ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
|
||||
initActiveSpeakerAudioLevels();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the audio level canvas for the given peerJid. If the canvas
|
||||
@@ -112,7 +114,7 @@ var AudioLevels = (function(my) {
|
||||
resourceJid = APP.xmpp.myResource();
|
||||
}
|
||||
|
||||
if(resourceJid === largeVideoResourceJid) {
|
||||
if(resourceJid === largeVideoResourceJid) {
|
||||
window.requestAnimationFrame(function () {
|
||||
AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
|
||||
});
|
||||
@@ -120,10 +122,9 @@ var AudioLevels = (function(my) {
|
||||
};
|
||||
|
||||
my.updateActiveSpeakerAudioLevel = function(audioLevel) {
|
||||
if($("#activeSpeaker").css("visibility") == "hidden")
|
||||
if($("#activeSpeaker").css("visibility") == "hidden" || ASDrawContext === null)
|
||||
return;
|
||||
|
||||
|
||||
ASDrawContext.clearRect(0, 0, 300, 300);
|
||||
if(audioLevel == 0)
|
||||
return;
|
||||
@@ -165,10 +166,9 @@ var AudioLevels = (function(my) {
|
||||
* error. Since audio levels are frequently updated, the errors have
|
||||
* been observed to pile into the console, strain the CPU.
|
||||
*/
|
||||
if (audioLevelCanvasOrig)
|
||||
{
|
||||
audioLevelCanvasCache[resourceJid]
|
||||
= CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
|
||||
if (audioLevelCanvasOrig) {
|
||||
audioLevelCanvasCache[resourceJid] =
|
||||
CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,15 +183,16 @@ var AudioLevels = (function(my) {
|
||||
|
||||
var shadowLevel = getShadowLevel(audioLevel);
|
||||
|
||||
if (shadowLevel > 0)
|
||||
if (shadowLevel > 0) {
|
||||
// drawContext, x, y, w, h, r, shadowColor, shadowLevel
|
||||
CanvasUtil.drawRoundRectGlow( drawContext,
|
||||
interfaceConfig.CANVAS_EXTRA/2, interfaceConfig.CANVAS_EXTRA/2,
|
||||
CanvasUtil.drawRoundRectGlow(drawContext,
|
||||
interfaceConfig.CANVAS_EXTRA / 2, interfaceConfig.CANVAS_EXTRA / 2,
|
||||
canvas.width - interfaceConfig.CANVAS_EXTRA,
|
||||
canvas.height - interfaceConfig.CANVAS_EXTRA,
|
||||
interfaceConfig.CANVAS_RADIUS,
|
||||
interfaceConfig.SHADOW_COLOR,
|
||||
shadowLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,9 +222,8 @@ var AudioLevels = (function(my) {
|
||||
*/
|
||||
function getVideoSpanId(resourceJid) {
|
||||
var videoSpanId = null;
|
||||
if (resourceJid === AudioLevels.LOCAL_LEVEL
|
||||
|| (APP.xmpp.myResource() && resourceJid
|
||||
=== APP.xmpp.myResource()))
|
||||
if (resourceJid === AudioLevels.LOCAL_LEVEL ||
|
||||
(APP.xmpp.myResource() && resourceJid === APP.xmpp.myResource()))
|
||||
videoSpanId = 'localVideoContainer';
|
||||
else
|
||||
videoSpanId = 'participant_' + resourceJid;
|
||||
@@ -251,10 +251,10 @@ var AudioLevels = (function(my) {
|
||||
|
||||
if (resized)
|
||||
Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) {
|
||||
audioLevelCanvasCache[resourceJid].width
|
||||
= width + interfaceConfig.CANVAS_EXTRA;
|
||||
audioLevelCanvasCache[resourceJid].height
|
||||
= height + interfaceConfig.CANVAS_EXTRA;
|
||||
audioLevelCanvasCache[resourceJid].width =
|
||||
width + interfaceConfig.CANVAS_EXTRA;
|
||||
audioLevelCanvasCache[resourceJid].height =
|
||||
height + interfaceConfig.CANVAS_EXTRA;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ var Authentication = {
|
||||
Moderator.allocateConferenceFocus(roomName, function () {
|
||||
// If it's not "on the fly" authentication now join
|
||||
// the conference room
|
||||
if (!APP.xmpp.getMUCJoined()) {
|
||||
if (!APP.xmpp.isMUCJoined()) {
|
||||
APP.UI.checkForNicknameAndJoin();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,15 +17,44 @@ var Avatar = {
|
||||
}
|
||||
users[jid] = id;
|
||||
}
|
||||
var thumbUrl = this.getGravatarUrl(users[jid] || jid, 100);
|
||||
var contactListUrl = this.getGravatarUrl(users[jid] || jid);
|
||||
var thumbUrl = this.getThumbUrl(jid);
|
||||
var contactListUrl = this.getContactListUrl(jid);
|
||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||
|
||||
APP.UI.userAvatarChanged(resourceJid, thumbUrl, contactListUrl);
|
||||
},
|
||||
getGravatarUrl: function (id, size) {
|
||||
if(id === APP.xmpp.myJid() || !id) {
|
||||
id = Settings.getSettings().uid;
|
||||
/**
|
||||
* Returns image URL for the avatar to be displayed on large video area
|
||||
* where current active speaker is presented.
|
||||
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
|
||||
*/
|
||||
getActiveSpeakerUrl: function (jid) {
|
||||
return this.getGravatarUrl(jid, 100);
|
||||
},
|
||||
/**
|
||||
* Returns image URL for the avatar to be displayed on small video thumbnail
|
||||
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
|
||||
*/
|
||||
getThumbUrl: function (jid) {
|
||||
return this.getGravatarUrl(jid, 100);
|
||||
},
|
||||
/**
|
||||
* Returns the URL for the avatar to be displayed as contactlist item
|
||||
* @param jid full MUC jid of the user for whom we want to obtain avatar URL
|
||||
*/
|
||||
getContactListUrl: function (jid) {
|
||||
return this.getGravatarUrl(jid, 30);
|
||||
},
|
||||
getGravatarUrl: function (jid, size) {
|
||||
if (!jid) {
|
||||
console.error("Get gravatar - jid is undefined");
|
||||
return null;
|
||||
}
|
||||
var id = users[jid];
|
||||
if (!id) {
|
||||
console.warn(
|
||||
"No avatar stored yet for " + jid + " - using JID as ID");
|
||||
id = jid;
|
||||
}
|
||||
return 'https://www.gravatar.com/avatar/' +
|
||||
MD5.hexdigest(id.trim().toLowerCase()) +
|
||||
|
||||
@@ -30,39 +30,30 @@ function resize() {
|
||||
* Creates the Etherpad button and adds it to the toolbar.
|
||||
*/
|
||||
function enableEtherpadButton() {
|
||||
if (!$('#etherpadButton').is(":visible"))
|
||||
$('#etherpadButton').css({display: 'inline-block'});
|
||||
if (!$('#toolbar_button_etherpad').is(":visible"))
|
||||
$('#toolbar_button_etherpad').css({display: 'inline-block'});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the IFrame for the etherpad.
|
||||
*/
|
||||
function createIFrame() {
|
||||
etherpadIFrame = document.createElement('iframe');
|
||||
etherpadIFrame.src = domain + etherpadName + options;
|
||||
etherpadIFrame.frameBorder = 0;
|
||||
etherpadIFrame.scrolling = "no";
|
||||
etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
|
||||
etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
|
||||
etherpadIFrame.setAttribute('style', 'visibility: hidden;');
|
||||
etherpadIFrame = VideoLayout.createEtherpadIframe(
|
||||
domain + etherpadName + options, function() {
|
||||
|
||||
document.getElementById('etherpad').appendChild(etherpadIFrame);
|
||||
|
||||
etherpadIFrame.onload = function() {
|
||||
|
||||
document.domain = document.domain;
|
||||
bubbleIframeMouseMove(etherpadIFrame);
|
||||
setTimeout(function() {
|
||||
// the iframes inside of the etherpad are
|
||||
// not yet loaded when the etherpad iframe is loaded
|
||||
var outer = etherpadIFrame.
|
||||
contentDocument.getElementsByName("ace_outer")[0];
|
||||
bubbleIframeMouseMove(outer);
|
||||
var inner = outer.
|
||||
contentDocument.getElementsByName("ace_inner")[0];
|
||||
bubbleIframeMouseMove(inner);
|
||||
}, 2000);
|
||||
};
|
||||
document.domain = document.domain;
|
||||
bubbleIframeMouseMove(etherpadIFrame);
|
||||
setTimeout(function() {
|
||||
// the iframes inside of the etherpad are
|
||||
// not yet loaded when the etherpad iframe is loaded
|
||||
var outer = etherpadIFrame.
|
||||
contentDocument.getElementsByName("ace_outer")[0];
|
||||
bubbleIframeMouseMove(outer);
|
||||
var inner = outer.
|
||||
contentDocument.getElementsByName("ace_inner")[0];
|
||||
bubbleIframeMouseMove(inner);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function bubbleIframeMouseMove(iframe){
|
||||
@@ -93,15 +84,6 @@ function bubbleIframeMouseMove(iframe){
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On video selected event.
|
||||
*/
|
||||
$(document).bind('video.selected', function (event, isPresentation) {
|
||||
if (config.etherpad_base && etherpadIFrame && etherpadIFrame.style.visibility !== 'hidden')
|
||||
Etherpad.toggleEtherpad(isPresentation);
|
||||
});
|
||||
|
||||
|
||||
var Etherpad = {
|
||||
/**
|
||||
* Initializes the etherpad.
|
||||
@@ -132,49 +114,17 @@ var Etherpad = {
|
||||
if (!etherpadIFrame)
|
||||
createIFrame();
|
||||
|
||||
var largeVideo = null;
|
||||
if (Prezi.isPresentationVisible())
|
||||
largeVideo = $('#presentation>iframe');
|
||||
else
|
||||
largeVideo = $('#largeVideo');
|
||||
|
||||
if ($('#etherpad>iframe').css('visibility') === 'hidden') {
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
largeVideo.fadeOut(300, function () {
|
||||
if (Prezi.isPresentationVisible()) {
|
||||
largeVideo.css({opacity: '0'});
|
||||
} else {
|
||||
VideoLayout.setLargeVideoVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#etherpad>iframe').fadeIn(300, function () {
|
||||
document.body.style.background = '#eeeeee';
|
||||
$('#etherpad>iframe').css({visibility: 'visible'});
|
||||
$('#etherpad').css({zIndex: 2});
|
||||
});
|
||||
if(VideoLayout.getLargeVideoState() === "etherpad")
|
||||
{
|
||||
VideoLayout.setLargeVideoState("video");
|
||||
}
|
||||
else if ($('#etherpad>iframe')) {
|
||||
$('#etherpad>iframe').fadeOut(300, function () {
|
||||
$('#etherpad>iframe').css({visibility: 'hidden'});
|
||||
$('#etherpad').css({zIndex: 0});
|
||||
document.body.style.background = 'black';
|
||||
});
|
||||
|
||||
if (!isPresentation) {
|
||||
$('#largeVideo').fadeIn(300, function () {
|
||||
VideoLayout.setLargeVideoVisible(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
VideoLayout.setLargeVideoState("etherpad");
|
||||
}
|
||||
resize();
|
||||
},
|
||||
|
||||
isVisible: function() {
|
||||
var etherpadIframe = $('#etherpad>iframe');
|
||||
return etherpadIframe && etherpadIframe.is(':visible');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = Etherpad;
|
||||
|
||||
@@ -6,6 +6,20 @@ var PreziPlayer = require("./PreziPlayer");
|
||||
|
||||
var preziPlayer = null;
|
||||
|
||||
|
||||
/**
|
||||
* Shows/hides a presentation.
|
||||
*/
|
||||
function setPresentationVisible(visible) {
|
||||
|
||||
if (visible) {
|
||||
VideoLayout.setLargeVideoState("prezi");
|
||||
}
|
||||
else {
|
||||
VideoLayout.setLargeVideoState("video");
|
||||
}
|
||||
}
|
||||
|
||||
var Prezi = {
|
||||
|
||||
|
||||
@@ -165,18 +179,14 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
|
||||
+ Strophe.getResourceFromJid(jid)
|
||||
+ '_' + presId;
|
||||
|
||||
|
||||
|
||||
|
||||
VideoLayout.addPreziContainer(elementId);
|
||||
VideoLayout.resizeThumbnails();
|
||||
|
||||
var controlsEnabled = false;
|
||||
if (jid === APP.xmpp.myJid())
|
||||
controlsEnabled = true;
|
||||
|
||||
setPresentationVisible(true);
|
||||
$('#largeVideoContainer').hover(
|
||||
VideoLayout.setLargeVideoHover(
|
||||
function (event) {
|
||||
if (Prezi.isPresentationVisible()) {
|
||||
var reloadButtonRight = window.innerWidth
|
||||
@@ -302,38 +312,6 @@ function resize() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides a presentation.
|
||||
*/
|
||||
function setPresentationVisible(visible) {
|
||||
var prezi = $('#presentation>iframe');
|
||||
if (visible) {
|
||||
// Trigger the video.selected event to indicate a change in the
|
||||
// large video.
|
||||
$(document).trigger("video.selected", [true]);
|
||||
|
||||
$('#largeVideo').fadeOut(300);
|
||||
prezi.fadeIn(300, function() {
|
||||
prezi.css({opacity:'1'});
|
||||
ToolbarToggler.dockToolbar(true);
|
||||
VideoLayout.setLargeVideoVisible(false);
|
||||
});
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
}
|
||||
else {
|
||||
if (prezi.css('opacity') == '1') {
|
||||
prezi.fadeOut(300, function () {
|
||||
prezi.css({opacity:'0'});
|
||||
$('#reloadPresentation').css({display:'none'});
|
||||
$('#largeVideo').fadeIn(300, function() {
|
||||
VideoLayout.setLargeVideoVisible(true);
|
||||
ToolbarToggler.dockToolbar(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Presentation has been removed.
|
||||
*/
|
||||
@@ -358,15 +336,6 @@ $(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* On video selected event.
|
||||
*/
|
||||
$(document).bind('video.selected', function (event, isPresentation) {
|
||||
if (!isPresentation && $('#presentation>iframe')) {
|
||||
setPresentationVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
$(window).resize(function () {
|
||||
resize();
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global require, $ */
|
||||
var Chat = require("./chat/Chat");
|
||||
var ContactList = require("./contactlist/ContactList");
|
||||
var Settings = require("./../../settings/Settings");
|
||||
@@ -16,7 +17,7 @@ var PanelToggler = (function(my) {
|
||||
var buttons = {
|
||||
'#chatspace': '#chatBottomButton',
|
||||
'#contactlist': '#contactListButton',
|
||||
'#settingsmenu': '#settingsButton'
|
||||
'#settingsmenu': '#toolbar_button_settings'
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, Util, nickname:true */
|
||||
/* global APP, $, Util, nickname:true */
|
||||
var Replacement = require("./Replacement");
|
||||
var CommandsProcessor = require("./Commands");
|
||||
var ToolbarToggler = require("../../toolbars/ToolbarToggler");
|
||||
@@ -19,7 +19,7 @@ function setVisualNotification(show) {
|
||||
var unreadMsgBottomElement
|
||||
= document.getElementById('bottomUnreadMessages');
|
||||
|
||||
var glower = $('#chatButton');
|
||||
var glower = $('#toolbar_button_chat');
|
||||
var bottomGlower = $('#chatBottomButton');
|
||||
|
||||
if (unreadMessages) {
|
||||
@@ -29,7 +29,7 @@ function setVisualNotification(show) {
|
||||
ToolbarToggler.dockToolbar(true);
|
||||
|
||||
var chatButtonElement
|
||||
= document.getElementById('chatButton').parentNode;
|
||||
= document.getElementById('toolbar_button_chat');
|
||||
var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
|
||||
UIUtil.getTextWidth(unreadMsgElement)) / 2;
|
||||
var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
|
||||
@@ -102,8 +102,7 @@ function getCurrentTime(stamp) {
|
||||
return hour+':'+minute+':'+second;
|
||||
}
|
||||
|
||||
function toggleSmileys()
|
||||
{
|
||||
function toggleSmileys() {
|
||||
var smileys = $('#smileysContainer');
|
||||
if(!smileys.is(':visible')) {
|
||||
smileys.show("slide", { direction: "down", duration: 300});
|
||||
@@ -191,19 +190,18 @@ var Chat = (function (my) {
|
||||
}
|
||||
});
|
||||
|
||||
$('#usermsg').keydown(function (event) {
|
||||
var usermsg = $('#usermsg');
|
||||
usermsg.keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
var value = this.value;
|
||||
$('#usermsg').val('').trigger('autosize.resize');
|
||||
usermsg.val('').trigger('autosize.resize');
|
||||
this.focus();
|
||||
var command = new CommandsProcessor(value);
|
||||
if(command.isCommand())
|
||||
{
|
||||
if(command.isCommand()) {
|
||||
command.processCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
var message = UIUtil.escapeHtml(value);
|
||||
APP.xmpp.sendChatMessage(message, NicknameHandler.getNickname());
|
||||
}
|
||||
@@ -214,7 +212,7 @@ var Chat = (function (my) {
|
||||
resizeChatConversation();
|
||||
Chat.scrollChatToBottom();
|
||||
};
|
||||
$('#usermsg').autosize({callback: onTextAreaResize});
|
||||
usermsg.autosize({callback: onTextAreaResize});
|
||||
|
||||
$("#chatspace").bind("shown",
|
||||
function () {
|
||||
@@ -228,7 +226,8 @@ var Chat = (function (my) {
|
||||
/**
|
||||
* Appends the given message to the chat conversation.
|
||||
*/
|
||||
my.updateChatConversation = function (from, displayName, message, myjid, stamp) {
|
||||
my.updateChatConversation =
|
||||
function (from, displayName, message, myjid, stamp) {
|
||||
var divClassName = '';
|
||||
|
||||
if (APP.xmpp.myJid() === from) {
|
||||
@@ -270,8 +269,7 @@ var Chat = (function (my) {
|
||||
* @param errorMessage the received error message.
|
||||
* @param originalText the original message.
|
||||
*/
|
||||
my.chatAddError = function(errorMessage, originalText)
|
||||
{
|
||||
my.chatAddError = function(errorMessage, originalText) {
|
||||
errorMessage = UIUtil.escapeHtml(errorMessage);
|
||||
originalText = UIUtil.escapeHtml(originalText);
|
||||
|
||||
@@ -288,23 +286,18 @@ var Chat = (function (my) {
|
||||
* Sets the subject to the UI
|
||||
* @param subject the subject
|
||||
*/
|
||||
my.chatSetSubject = function(subject)
|
||||
{
|
||||
if(subject)
|
||||
my.chatSetSubject = function(subject) {
|
||||
if (subject)
|
||||
subject = subject.trim();
|
||||
$('#subject').html(Replacement.linkify(UIUtil.escapeHtml(subject)));
|
||||
if(subject === "")
|
||||
{
|
||||
if(subject === "") {
|
||||
$("#subject").css({display: "none"});
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$("#subject").css({display: "block"});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the chat conversation mode.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global APP, require */
|
||||
var UIUtil = require("../../util/UIUtil");
|
||||
|
||||
/**
|
||||
@@ -14,47 +15,39 @@ var commands = {
|
||||
* @param message the received message
|
||||
* @returns {string} the command
|
||||
*/
|
||||
function getCommand(message)
|
||||
{
|
||||
if(message)
|
||||
{
|
||||
for(var command in commands)
|
||||
{
|
||||
function getCommand(message) {
|
||||
if(message) {
|
||||
for(var command in commands) {
|
||||
if(message.indexOf("/" + command) == 0)
|
||||
return command;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the data for topic command.
|
||||
* @param commandArguments the arguments of the topic command.
|
||||
*/
|
||||
function processTopic(commandArguments)
|
||||
{
|
||||
function processTopic(commandArguments) {
|
||||
var topic = UIUtil.escapeHtml(commandArguments);
|
||||
APP.xmpp.setSubject(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new CommandProccessor instance from a message that
|
||||
* Constructs a new CommandProccessor instance from a message that
|
||||
* handles commands received via chat messages.
|
||||
* @param message the message
|
||||
* @constructor
|
||||
*/
|
||||
function CommandsProcessor(message)
|
||||
{
|
||||
|
||||
|
||||
function CommandsProcessor(message) {
|
||||
var command = getCommand(message);
|
||||
|
||||
/**
|
||||
* Returns the name of the command.
|
||||
* @returns {String} the command
|
||||
*/
|
||||
this.getCommand = function()
|
||||
{
|
||||
this.getCommand = function() {
|
||||
return command;
|
||||
};
|
||||
|
||||
@@ -65,8 +58,7 @@ function CommandsProcessor(message)
|
||||
* Returns the arguments of the command.
|
||||
* @returns {string}
|
||||
*/
|
||||
this.getArgument = function()
|
||||
{
|
||||
this.getArgument = function() {
|
||||
return messageArgument;
|
||||
};
|
||||
}
|
||||
@@ -75,9 +67,8 @@ function CommandsProcessor(message)
|
||||
* Checks whether this instance is valid command or not.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
CommandsProcessor.prototype.isCommand = function()
|
||||
{
|
||||
if(this.getCommand())
|
||||
CommandsProcessor.prototype.isCommand = function() {
|
||||
if (this.getCommand())
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
@@ -85,13 +76,11 @@ CommandsProcessor.prototype.isCommand = function()
|
||||
/**
|
||||
* Processes the command.
|
||||
*/
|
||||
CommandsProcessor.prototype.processCommand = function()
|
||||
{
|
||||
CommandsProcessor.prototype.processCommand = function() {
|
||||
if(!this.isCommand())
|
||||
return;
|
||||
|
||||
commands[this.getCommand()](this.getArgument());
|
||||
|
||||
};
|
||||
|
||||
module.exports = CommandsProcessor;
|
||||
@@ -1,3 +1,5 @@
|
||||
/* global $, APP, Strophe */
|
||||
var Avatar = require('../../avatar/Avatar');
|
||||
|
||||
var numberOfContacts = 0;
|
||||
var notificationInterval;
|
||||
@@ -25,12 +27,12 @@ function updateNumberOfParticipants(delta) {
|
||||
/**
|
||||
* Creates the avatar element.
|
||||
*
|
||||
* @return the newly created avatar element
|
||||
* @return {object} the newly created avatar element
|
||||
*/
|
||||
function createAvatar(id) {
|
||||
function createAvatar(jid) {
|
||||
var avatar = document.createElement('img');
|
||||
avatar.className = "icon-avatar avatar";
|
||||
avatar.src = "https://www.gravatar.com/avatar/" + id + "?d=wavatar&size=30";
|
||||
avatar.src = Avatar.getContactListUrl(jid);
|
||||
|
||||
return avatar;
|
||||
}
|
||||
@@ -44,8 +46,7 @@ function createDisplayNameParagraph(key, displayName) {
|
||||
var p = document.createElement('p');
|
||||
if(displayName)
|
||||
p.innerText = displayName;
|
||||
else if(key)
|
||||
{
|
||||
else if(key) {
|
||||
p.setAttribute("data-i18n",key);
|
||||
p.innerText = APP.translation.translateString(key);
|
||||
}
|
||||
@@ -63,7 +64,6 @@ function stopGlowing(glower) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contact list.
|
||||
*/
|
||||
@@ -82,24 +82,22 @@ var ContactList = {
|
||||
* Adds a contact for the given peerJid if such doesn't yet exist.
|
||||
*
|
||||
* @param peerJid the peerJid corresponding to the contact
|
||||
* @param id the user's email or userId used to get the user's avatar
|
||||
*/
|
||||
ensureAddContact: function (peerJid, id) {
|
||||
ensureAddContact: function (peerJid) {
|
||||
var resourceJid = Strophe.getResourceFromJid(peerJid);
|
||||
|
||||
var contact = $('#contacts>li[id="' + resourceJid + '"]');
|
||||
|
||||
if (!contact || contact.length <= 0)
|
||||
ContactList.addContact(peerJid, id);
|
||||
ContactList.addContact(peerJid);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a contact for the given peer jid.
|
||||
*
|
||||
* @param peerJid the jid of the contact to add
|
||||
* @param id the email or userId of the user
|
||||
*/
|
||||
addContact: function (peerJid, id) {
|
||||
addContact: function (peerJid) {
|
||||
var resourceJid = Strophe.getResourceFromJid(peerJid);
|
||||
|
||||
var contactlist = $('#contacts');
|
||||
@@ -113,7 +111,7 @@ var ContactList = {
|
||||
}
|
||||
};
|
||||
|
||||
newContact.appendChild(createAvatar(id));
|
||||
newContact.appendChild(createAvatar(peerJid));
|
||||
newContact.appendChild(createDisplayNameParagraph("participant"));
|
||||
|
||||
if (resourceJid === APP.xmpp.myResource()) {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
/* global APP, $ */
|
||||
var Avatar = require("../../avatar/Avatar");
|
||||
var Settings = require("./../../../settings/Settings");
|
||||
var UIUtil = require("../../util/UIUtil");
|
||||
var languages = require("../../../../service/translation/languages");
|
||||
|
||||
function generateLanguagesSelectBox()
|
||||
{
|
||||
function generateLanguagesSelectBox() {
|
||||
var currentLang = APP.translation.getCurrentLanguage();
|
||||
var html = "<select id=\"languages_selectbox\">";
|
||||
var langArray = languages.getLanguages();
|
||||
for(var i = 0; i < langArray.length; i++)
|
||||
{
|
||||
for(var i = 0; i < langArray.length; i++) {
|
||||
var lang = langArray[i];
|
||||
html += "<option ";
|
||||
if(lang === currentLang)
|
||||
@@ -26,7 +25,8 @@ function generateLanguagesSelectBox()
|
||||
var SettingsMenu = {
|
||||
|
||||
init: function () {
|
||||
$("#startMutedOptions").before(generateLanguagesSelectBox());
|
||||
var startMutedSelector = $("#startMutedOptions");
|
||||
startMutedSelector.before(generateLanguagesSelectBox());
|
||||
APP.translation.translateElement($("#languages_selectbox"));
|
||||
$('#settingsmenu>input').keyup(function(event){
|
||||
if(event.keyCode === 13) {//enter
|
||||
@@ -34,13 +34,11 @@ var SettingsMenu = {
|
||||
}
|
||||
});
|
||||
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
if (APP.xmpp.isModerator()) {
|
||||
startMutedSelector.css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
else {
|
||||
startMutedSelector.css("display", "none");
|
||||
}
|
||||
|
||||
$("#updateSettings").click(function () {
|
||||
@@ -49,12 +47,10 @@ var SettingsMenu = {
|
||||
},
|
||||
|
||||
onRoleChanged: function () {
|
||||
if(APP.xmpp.isModerator())
|
||||
{
|
||||
if(APP.xmpp.isModerator()) {
|
||||
$("#startMutedOptions").css("display", "block");
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$("#startMutedOptions").css("display", "none");
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global $ */
|
||||
var PanelToggler = require("../side_pannels/SidePanelToggler");
|
||||
|
||||
var buttonHandlers = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* global APP,$, buttonClick, config, lockRoom,
|
||||
setSharedKey, Util */
|
||||
/* global APP, $, buttonClick, config, lockRoom, interfaceConfig, setSharedKey,
|
||||
Util */
|
||||
var messageHandler = require("../util/MessageHandler");
|
||||
var BottomToolbar = require("./BottomToolbar");
|
||||
var Prezi = require("../prezi/Prezi");
|
||||
@@ -13,9 +13,9 @@ var AuthenticationEvents
|
||||
var roomUrl = null;
|
||||
var sharedKey = '';
|
||||
var UI = null;
|
||||
var recordingToaster = null;
|
||||
|
||||
var buttonHandlers =
|
||||
{
|
||||
var buttonHandlers = {
|
||||
"toolbar_button_mute": function () {
|
||||
return APP.UI.toggleAudio();
|
||||
},
|
||||
@@ -46,9 +46,8 @@ var buttonHandlers =
|
||||
"toolbar_button_desktopsharing": function () {
|
||||
return APP.desktopsharing.toggleScreenSharing();
|
||||
},
|
||||
"toolbar_button_fullScreen": function()
|
||||
{
|
||||
UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
|
||||
"toolbar_button_fullScreen": function() {
|
||||
UIUtil.buttonClick("#toolbar_button_fullScreen", "icon-full-screen icon-exit-full-screen");
|
||||
return Toolbar.toggleFullScreen();
|
||||
},
|
||||
"toolbar_button_sip": function () {
|
||||
@@ -91,10 +90,8 @@ var buttonHandlers =
|
||||
|
||||
function hangup() {
|
||||
APP.xmpp.disposeConference();
|
||||
if(config.enableWelcomePage)
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
if(config.enableWelcomePage) {
|
||||
setTimeout(function() {
|
||||
window.localStorage.welcomePageDisabled = false;
|
||||
window.location.pathname = "/";
|
||||
}, 10000);
|
||||
@@ -115,8 +112,7 @@ function hangup() {
|
||||
msg,
|
||||
true,
|
||||
buttons,
|
||||
function(event, value, message, formVals)
|
||||
{
|
||||
function(event, value, message, formVals) {
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
@@ -127,8 +123,13 @@ function hangup() {
|
||||
* Starts or stops the recording for the conference.
|
||||
*/
|
||||
|
||||
function toggleRecording() {
|
||||
function toggleRecording(predefinedToken) {
|
||||
APP.xmpp.toggleRecording(function (callback) {
|
||||
if (predefinedToken) {
|
||||
callback(UIUtil.escapeHtml(predefinedToken));
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = APP.translation.generateTranslationHTML(
|
||||
"dialog.recordingToken");
|
||||
var token = APP.translation.translateString("dialog.token");
|
||||
@@ -152,7 +153,7 @@ function toggleRecording() {
|
||||
function () { },
|
||||
':input:first'
|
||||
);
|
||||
}, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
|
||||
}, Toolbar.setRecordingButtonState);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,13 +166,11 @@ function lockRoom(lock) {
|
||||
|
||||
APP.xmpp.lockRoom(currentSharedKey, function (res) {
|
||||
// password is required
|
||||
if (sharedKey)
|
||||
{
|
||||
if (sharedKey) {
|
||||
console.log('set room password');
|
||||
Toolbar.lockLockButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
console.log('removed room password');
|
||||
Toolbar.unlockLockButton();
|
||||
}
|
||||
@@ -186,7 +185,7 @@ function lockRoom(lock) {
|
||||
"dialog.passwordNotSupported");
|
||||
Toolbar.setSharedKey('');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Invite participants to conference.
|
||||
@@ -224,13 +223,11 @@ function inviteParticipants() {
|
||||
window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
|
||||
}
|
||||
|
||||
function dialpadButtonClicked()
|
||||
{
|
||||
//TODO show the dialpad window
|
||||
function dialpadButtonClicked() {
|
||||
//TODO show the dialpad box
|
||||
}
|
||||
|
||||
function callSipButtonClicked()
|
||||
{
|
||||
function callSipButtonClicked() {
|
||||
var defaultNumber
|
||||
= config.defaultSipNumber ? config.defaultSipNumber : '';
|
||||
|
||||
@@ -281,7 +278,7 @@ var Toolbar = (function (my) {
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets shared key
|
||||
@@ -298,7 +295,7 @@ var Toolbar = (function (my) {
|
||||
return;
|
||||
}
|
||||
// Get authentication URL
|
||||
if (!APP.xmpp.getMUCJoined()) {
|
||||
if (!APP.xmpp.isMUCJoined()) {
|
||||
APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {
|
||||
// If conference has not been started yet - redirect to login page
|
||||
window.location.href = url;
|
||||
@@ -342,9 +339,8 @@ var Toolbar = (function (my) {
|
||||
* Disables and enables some of the buttons.
|
||||
*/
|
||||
my.setupButtonsFromConfig = function () {
|
||||
if (config.disablePrezi)
|
||||
{
|
||||
$("#prezi_button").css({display: "none"});
|
||||
if (config.disablePrezi) {
|
||||
$("#toolbar_button_prezi").css({display: "none"});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -519,15 +515,15 @@ var Toolbar = (function (my) {
|
||||
* Unlocks the lock button state.
|
||||
*/
|
||||
my.unlockLockButton = function () {
|
||||
if ($("#lockIcon").hasClass("icon-security-locked"))
|
||||
UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
|
||||
if ($("#toolbar_button_security").hasClass("icon-security-locked"))
|
||||
UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked");
|
||||
};
|
||||
/**
|
||||
* Updates the lock button state to locked.
|
||||
*/
|
||||
my.lockLockButton = function () {
|
||||
if ($("#lockIcon").hasClass("icon-security"))
|
||||
UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
|
||||
if ($("#toolbar_button_security").hasClass("icon-security"))
|
||||
UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked");
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -550,40 +546,77 @@ var Toolbar = (function (my) {
|
||||
}
|
||||
|
||||
if (show) {
|
||||
$('#recording').css({display: "inline"});
|
||||
$('#toolbar_button_record').css({display: "inline-block"});
|
||||
}
|
||||
else {
|
||||
$('#recording').css({display: "none"});
|
||||
$('#toolbar_button_record').css({display: "none"});
|
||||
}
|
||||
};
|
||||
|
||||
// Sets the state of the recording button
|
||||
my.setRecordingButtonState = function (isRecording) {
|
||||
var selector = $('#recordButton');
|
||||
if (isRecording) {
|
||||
my.setRecordingButtonState = function (recordingState) {
|
||||
var selector = $('#toolbar_button_record');
|
||||
|
||||
if (recordingState === 'on') {
|
||||
selector.removeClass("icon-recEnable");
|
||||
selector.addClass("icon-recEnable active");
|
||||
} else {
|
||||
|
||||
$("#largeVideo").toggleClass("videoMessageFilter", true);
|
||||
var recordOnKey = "recording.on";
|
||||
$('#videoConnectionMessage').attr("data-i18n", recordOnKey);
|
||||
$('#videoConnectionMessage').text(APP.translation.translateString(recordOnKey));
|
||||
|
||||
setTimeout(function(){
|
||||
$("#largeVideo").toggleClass("videoMessageFilter", false);
|
||||
$('#videoConnectionMessage').css({display: "none"});
|
||||
}, 1500);
|
||||
|
||||
recordingToaster = messageHandler.notify(null, "recording.toaster", null,
|
||||
null, null, {timeOut: 0, closeButton: null, tapToDismiss: false});
|
||||
} else if (recordingState === 'off') {
|
||||
selector.removeClass("icon-recEnable active");
|
||||
selector.addClass("icon-recEnable");
|
||||
|
||||
$("#largeVideo").toggleClass("videoMessageFilter", false);
|
||||
$('#videoConnectionMessage').css({display: "none"});
|
||||
|
||||
if (recordingToaster)
|
||||
messageHandler.remove(recordingToaster);
|
||||
|
||||
} else if (recordingState === 'pending') {
|
||||
selector.removeClass("icon-recEnable active");
|
||||
selector.addClass("icon-recEnable");
|
||||
|
||||
$("#largeVideo").toggleClass("videoMessageFilter", true);
|
||||
var recordPendingKey = "recording.pending";
|
||||
$('#videoConnectionMessage').attr("data-i18n", recordPendingKey);
|
||||
$('#videoConnectionMessage').text(APP.translation.translateString(recordPendingKey));
|
||||
$('#videoConnectionMessage').css({display: "block"});
|
||||
}
|
||||
};
|
||||
|
||||
// checks whether recording is enabled and whether we have params to start automatically recording
|
||||
my.checkAutoRecord = function () {
|
||||
if (config.enableRecording && config.autoRecord) {
|
||||
toggleRecording(config.autoRecordToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Shows or hides SIP calls button
|
||||
my.showSipCallButton = function (show) {
|
||||
if (APP.xmpp.isSipGatewayEnabled() && show) {
|
||||
$('#sipCallButton').css({display: "inline-block"});
|
||||
$('#toolbar_button_sip').css({display: "inline-block"});
|
||||
} else {
|
||||
$('#sipCallButton').css({display: "none"});
|
||||
$('#toolbar_button_sip').css({display: "none"});
|
||||
}
|
||||
};
|
||||
|
||||
// Shows or hides the dialpad button
|
||||
my.showDialPadButton = function (show) {
|
||||
if (show) {
|
||||
$('#dialPadButton').css({display: "inline-block"});
|
||||
$('#toolbar_button_dialpad').css({display: "inline-block"});
|
||||
} else {
|
||||
$('#dialPadButton').css({display: "none"});
|
||||
$('#toolbar_button_dialpad').css({display: "none"});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -631,13 +664,10 @@ var Toolbar = (function (my) {
|
||||
* @param active the state of the desktop streaming.
|
||||
*/
|
||||
my.changeDesktopSharingButtonState = function (active) {
|
||||
var button = $("#desktopsharing > a");
|
||||
if (active)
|
||||
{
|
||||
var button = $("#toolbar_button_desktopsharing");
|
||||
if (active) {
|
||||
button.addClass("glow");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
button.removeClass("glow");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
|
||||
/* global APP, config, $, interfaceConfig, Moderator,
|
||||
DesktopStreaming.showDesktopSharingButton */
|
||||
|
||||
var toolbarTimeoutObject,
|
||||
toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
|
||||
|
||||
function showDesktopSharingButton() {
|
||||
if (APP.desktopsharing.isDesktopSharingEnabled()) {
|
||||
$('#desktopsharing').css({display: "inline"});
|
||||
$('#toolbar_button_desktopsharing').css({display: "inline-block"});
|
||||
} else {
|
||||
$('#desktopsharing').css({display: "none"});
|
||||
$('#toolbar_button_desktopsharing').css({display: "none"});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +53,8 @@ var ToolbarToggler = {
|
||||
* Shows the main toolbar.
|
||||
*/
|
||||
showToolbar: function () {
|
||||
if (interfaceConfig.filmStripOnly)
|
||||
return;
|
||||
var header = $("#header"),
|
||||
bottomToolbar = $("#bottomToolbar");
|
||||
if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
|
||||
@@ -81,13 +84,15 @@ var ToolbarToggler = {
|
||||
showDesktopSharingButton();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Docks/undocks the toolbar.
|
||||
*
|
||||
* @param isDock indicates what operation to perform
|
||||
*/
|
||||
dockToolbar: function (isDock) {
|
||||
if (interfaceConfig.filmStripOnly)
|
||||
return;
|
||||
|
||||
if (isDock) {
|
||||
// First make sure the toolbar is shown.
|
||||
if (!$('#header').is(':visible')) {
|
||||
@@ -111,7 +116,6 @@ var ToolbarToggler = {
|
||||
},
|
||||
|
||||
showDesktopSharingButton: showDesktopSharingButton
|
||||
|
||||
};
|
||||
|
||||
module.exports = ToolbarToggler;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global $ */
|
||||
var JitsiPopover = (function () {
|
||||
/**
|
||||
* Constructs new JitsiPopover and attaches it to the element
|
||||
@@ -45,23 +46,24 @@ var JitsiPopover = (function () {
|
||||
* Shows the popover
|
||||
*/
|
||||
JitsiPopover.prototype.show = function () {
|
||||
if(!JitsiPopover.enabled)
|
||||
return;
|
||||
this.createPopover();
|
||||
this.popoverShown = true;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the popover
|
||||
*/
|
||||
JitsiPopover.prototype.hide = function () {
|
||||
if(!this.elementIsHovered && !this.popoverIsHovered && this.popoverShown)
|
||||
{
|
||||
if(!this.elementIsHovered && !this.popoverIsHovered &&
|
||||
this.popoverShown) {
|
||||
this.forceHide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the popover
|
||||
* Hides the popover.
|
||||
*/
|
||||
JitsiPopover.prototype.forceHide = function () {
|
||||
$(".jitsipopover").remove();
|
||||
@@ -69,7 +71,7 @@ var JitsiPopover = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the popover html
|
||||
* Creates the popover html.
|
||||
*/
|
||||
JitsiPopover.prototype.createPopover = function () {
|
||||
$("body").append(this.template);
|
||||
@@ -86,7 +88,7 @@ var JitsiPopover = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Refreshes the position of the popover
|
||||
* Refreshes the position of the popover.
|
||||
*/
|
||||
JitsiPopover.prototype.refreshPosition = function () {
|
||||
$(".jitsipopover").position({
|
||||
@@ -95,10 +97,13 @@ var JitsiPopover = (function () {
|
||||
collision: "fit",
|
||||
of: this.element,
|
||||
using: function (position, elements) {
|
||||
var calcLeft = elements.target.left - elements.element.left + elements.target.width/2;
|
||||
$(".jitsipopover").css({top: position.top, left: position.left, display: "table"});
|
||||
var calcLeft = elements.target.left - elements.element.left +
|
||||
elements.target.width/2;
|
||||
$(".jitsipopover").css(
|
||||
{top: position.top, left: position.left, display: "table"});
|
||||
$(".jitsipopover > .arrow").css({left: calcLeft});
|
||||
$(".jitsipopover > .jitsiPopupmenuPadding").css({left: calcLeft - 50});
|
||||
$(".jitsipopover > .jitsiPopupmenuPadding").css(
|
||||
{left: calcLeft - 50});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -115,9 +120,9 @@ var JitsiPopover = (function () {
|
||||
this.createPopover();
|
||||
};
|
||||
|
||||
JitsiPopover.enabled = true;
|
||||
|
||||
return JitsiPopover;
|
||||
|
||||
|
||||
})();
|
||||
|
||||
module.exports = JitsiPopover;
|
||||
@@ -1,36 +1,42 @@
|
||||
/* global $, APP, jQuery, toastr */
|
||||
|
||||
/**
|
||||
* Flag for enable/disable of the notifications.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var notificationsEnabled = true;
|
||||
|
||||
var messageHandler = (function(my) {
|
||||
|
||||
/**
|
||||
* Shows a message to the user.
|
||||
*
|
||||
* @param titleString the title of the message
|
||||
* @param messageString the text of the message
|
||||
* @param titleKey the title of the message
|
||||
* @param messageKey the text of the message
|
||||
*/
|
||||
my.openMessageDialog = function(titleKey, messageKey) {
|
||||
var title = null;
|
||||
if(titleKey)
|
||||
{
|
||||
if(titleKey) {
|
||||
title = APP.translation.generateTranslationHTML(titleKey);
|
||||
}
|
||||
var message = APP.translation.generateTranslationHTML(messageKey);
|
||||
$.prompt(message,
|
||||
{
|
||||
title: title,
|
||||
persistent: false
|
||||
}
|
||||
{title: title, persistent: false}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
|
||||
* Shows a message to the user with two buttons: first is given as a
|
||||
* parameter and the second is Cancel.
|
||||
*
|
||||
* @param titleString the title of the message
|
||||
* @param msgString the text of the message
|
||||
* @param persistent boolean value which determines whether the message is persistent or not
|
||||
* @param persistent boolean value which determines whether the message is
|
||||
* persistent or not
|
||||
* @param leftButton the fist button's text
|
||||
* @param submitFunction function to be called on submit
|
||||
* @param loadedFunction function to be called after the prompt is fully loaded
|
||||
* @param loadedFunction function to be called after the prompt is fully
|
||||
* loaded
|
||||
* @param closeFunction function to be called after the prompt is closed
|
||||
* @param focus optional focus selector or button index to be focused after
|
||||
* the dialog is opened
|
||||
@@ -39,8 +45,7 @@ var messageHandler = (function(my) {
|
||||
*/
|
||||
my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString,
|
||||
persistent, leftButtonKey, submitFunction, loadedFunction,
|
||||
closeFunction, focus, defaultButton)
|
||||
{
|
||||
closeFunction, focus, defaultButton) {
|
||||
var buttons = [];
|
||||
|
||||
var leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
|
||||
@@ -51,8 +56,7 @@ var messageHandler = (function(my) {
|
||||
buttons.push({title: cancelButton, value: false});
|
||||
|
||||
var message = msgString, title = titleString;
|
||||
if (titleKey)
|
||||
{
|
||||
if (titleKey) {
|
||||
title = APP.translation.generateTranslationHTML(titleKey);
|
||||
}
|
||||
if (msgKey) {
|
||||
@@ -75,11 +79,14 @@ var messageHandler = (function(my) {
|
||||
*
|
||||
* @param titleString the title of the message
|
||||
* @param msgString the text of the message
|
||||
* @param persistent boolean value which determines whether the message is persistent or not
|
||||
* @param buttons object with the buttons. The keys must be the name of the button and value is the value
|
||||
* that will be passed to submitFunction
|
||||
* @param persistent boolean value which determines whether the message is
|
||||
* persistent or not
|
||||
* @param buttons object with the buttons. The keys must be the name of the
|
||||
* button and value is the value that will be passed to
|
||||
* submitFunction
|
||||
* @param submitFunction function to be called on submit
|
||||
* @param loadedFunction function to be called after the prompt is fully loaded
|
||||
* @param loadedFunction function to be called after the prompt is fully
|
||||
* loaded
|
||||
*/
|
||||
my.openDialog = function (titleString, msgString, persistent, buttons,
|
||||
submitFunction, loadedFunction) {
|
||||
@@ -107,10 +114,9 @@ var messageHandler = (function(my) {
|
||||
/**
|
||||
* Shows a dialog with different states to the user.
|
||||
*
|
||||
* @param statesObject object containing all the states of the dialog
|
||||
* @param statesObject object containing all the states of the dialog.
|
||||
*/
|
||||
my.openDialogWithStates = function (statesObject, options) {
|
||||
|
||||
return new Impromptu(statesObject, options);
|
||||
};
|
||||
|
||||
@@ -124,7 +130,7 @@ var messageHandler = (function(my) {
|
||||
* @param onPopupClosed optional callback function called when popup window
|
||||
* has been closed.
|
||||
*
|
||||
* @returns popup window object if opened successfully or undefined
|
||||
* @returns {object} popup window object if opened successfully or undefined
|
||||
* in case we failed to open it(popup blocked)
|
||||
*/
|
||||
my.openCenteredPopup = function (url, w, h, onPopupClosed) {
|
||||
@@ -147,8 +153,8 @@ var messageHandler = (function(my) {
|
||||
/**
|
||||
* Shows a dialog prompting the user to send an error report.
|
||||
*
|
||||
* @param titleString the title of the message
|
||||
* @param msgString the text of the message
|
||||
* @param titleKey the title of the message
|
||||
* @param msgKey the text of the message
|
||||
* @param error the error that is being reported
|
||||
*/
|
||||
my.openReportDialog = function(titleKey, msgKey, error) {
|
||||
@@ -159,35 +165,42 @@ var messageHandler = (function(my) {
|
||||
|
||||
/**
|
||||
* Shows an error dialog to the user.
|
||||
* @param title the title of the message
|
||||
* @param message the text of the messafe
|
||||
* @param titleKey the title of the message.
|
||||
* @param msgKey the text of the message.
|
||||
*/
|
||||
my.showError = function(titleKey, msgKey) {
|
||||
|
||||
if(!titleKey) {
|
||||
if (!titleKey) {
|
||||
titleKey = "dialog.oops";
|
||||
}
|
||||
if(!msgKey)
|
||||
{
|
||||
if (!msgKey) {
|
||||
msgKey = "dialog.defaultError";
|
||||
}
|
||||
messageHandler.openMessageDialog(titleKey, msgKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Displayes notification.
|
||||
* @param displayName display name of the participant that is associated with the notification.
|
||||
* @param displayNameKey the key from the language file for the display name.
|
||||
* @param cls css class for the notification
|
||||
* @param messageKey the key from the language file for the text of the message.
|
||||
* @param messageArguments object with the arguments for the message.
|
||||
* @param options object with language options.
|
||||
*/
|
||||
my.notify = function(displayName, displayNameKey,
|
||||
cls, messageKey, messageArguments, options) {
|
||||
if(!notificationsEnabled)
|
||||
return;
|
||||
var displayNameSpan = '<span class="nickname" ';
|
||||
if(displayName)
|
||||
{
|
||||
if (displayName) {
|
||||
displayNameSpan += ">" + displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
displayNameSpan += "data-i18n='" + displayNameKey +
|
||||
"'>" + APP.translation.translateString(displayNameKey);
|
||||
}
|
||||
displayNameSpan += "</span>";
|
||||
toastr.info(
|
||||
return toastr.info(
|
||||
displayNameSpan + '<br>' +
|
||||
'<span class=' + cls + ' data-i18n="' + messageKey + '"' +
|
||||
(messageArguments?
|
||||
@@ -198,6 +211,28 @@ var messageHandler = (function(my) {
|
||||
'</span>', null, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the toaster.
|
||||
* @param toasterElement
|
||||
*/
|
||||
my.remove = function(toasterElement) {
|
||||
toasterElement.remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables notifications.
|
||||
*/
|
||||
my.disableNotifications = function () {
|
||||
notificationsEnabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables notifications.
|
||||
*/
|
||||
my.enableNotifications = function () {
|
||||
notificationsEnabled = true;
|
||||
};
|
||||
|
||||
return my;
|
||||
}(messageHandler || {}));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ var UIEvents = require("../../../service/UI/UIEvents");
|
||||
var nickname = null;
|
||||
var eventEmitter = null;
|
||||
|
||||
var NickanameHandler = {
|
||||
var NicknameHandler = {
|
||||
init: function (emitter) {
|
||||
eventEmitter = emitter;
|
||||
var storedDisplayName = window.localStorage.displayname;
|
||||
@@ -27,4 +27,4 @@ var NickanameHandler = {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = NickanameHandler;
|
||||
module.exports = NicknameHandler;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global $ */
|
||||
/**
|
||||
* Created by hristo on 12/22/14.
|
||||
*/
|
||||
@@ -61,7 +62,7 @@ module.exports = {
|
||||
|
||||
for (var i = 0, n = pixels.length; i < n; i += 4) {
|
||||
var grayscale
|
||||
= pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
|
||||
= pixels[i] * 0.3 + pixels[i+1] * 0.59 + pixels[i+2] * 0.11;
|
||||
pixels[i ] = grayscale; // red
|
||||
pixels[i+1] = grayscale; // green
|
||||
pixels[i+2] = grayscale; // blue
|
||||
@@ -77,7 +78,19 @@ module.exports = {
|
||||
element.setAttribute("data-placement", position);
|
||||
element.setAttribute("data-html", true);
|
||||
element.setAttribute("data-container", "body");
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts given child element as the first one into the container.
|
||||
* @param container the container to which new child element will be added
|
||||
* @param newChild the new element that will be inserted into the container
|
||||
*/
|
||||
prependChild: function (container, newChild) {
|
||||
var firstChild = container.childNodes[0];
|
||||
if (firstChild) {
|
||||
container.insertBefore(newChild, firstChild);
|
||||
} else {
|
||||
container.appendChild(newChild);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global APP, $ */
|
||||
var JitsiPopover = require("../util/JitsiPopover");
|
||||
|
||||
/**
|
||||
@@ -5,8 +6,7 @@ var JitsiPopover = require("../util/JitsiPopover");
|
||||
* @param videoContainer the video container associated with the indicator.
|
||||
* @constructor
|
||||
*/
|
||||
function ConnectionIndicator(videoContainer, jid)
|
||||
{
|
||||
function ConnectionIndicator(videoContainer, jid) {
|
||||
this.videoContainer = videoContainer;
|
||||
this.bandwidth = null;
|
||||
this.packetLoss = null;
|
||||
@@ -37,20 +37,17 @@ ConnectionIndicator.connectionQualityValues = {
|
||||
0: "0px"//empty
|
||||
};
|
||||
|
||||
ConnectionIndicator.getIP = function(value)
|
||||
{
|
||||
ConnectionIndicator.getIP = function(value) {
|
||||
return value.substring(0, value.lastIndexOf(":"));
|
||||
};
|
||||
|
||||
ConnectionIndicator.getPort = function(value)
|
||||
{
|
||||
ConnectionIndicator.getPort = function(value) {
|
||||
return value.substring(value.lastIndexOf(":") + 1, value.length);
|
||||
};
|
||||
|
||||
ConnectionIndicator.getStringFromArray = function (array) {
|
||||
var res = "";
|
||||
for(var i = 0; i < array.length; i++)
|
||||
{
|
||||
for(var i = 0; i < array.length; i++) {
|
||||
res += (i === 0? "" : ", ") + array[i];
|
||||
}
|
||||
return res;
|
||||
@@ -65,74 +62,60 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||
|
||||
var translate = APP.translation.translateString;
|
||||
|
||||
if(this.bitrate === null)
|
||||
{
|
||||
if(this.bitrate === null) {
|
||||
downloadBitrate = "N/A";
|
||||
uploadBitrate = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
downloadBitrate =
|
||||
this.bitrate.download? this.bitrate.download + " Kbps" : "N/A";
|
||||
uploadBitrate =
|
||||
this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A";
|
||||
}
|
||||
|
||||
if(this.packetLoss === null)
|
||||
{
|
||||
if(this.packetLoss === null) {
|
||||
packetLoss = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
|
||||
packetLoss = "<span class='jitsipopover_green'>↓</span>" +
|
||||
(this.packetLoss.download !== null? this.packetLoss.download : "N/A") +
|
||||
(this.packetLoss.download !== null ?
|
||||
this.packetLoss.download : "N/A") +
|
||||
"% <span class='jitsipopover_orange'>↑</span>" +
|
||||
(this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") + "%";
|
||||
(this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") +
|
||||
"%";
|
||||
}
|
||||
|
||||
var resolutionValue = null;
|
||||
if(this.resolution && this.jid != null)
|
||||
{
|
||||
if(this.resolution && this.jid != null) {
|
||||
var keys = Object.keys(this.resolution);
|
||||
for(var ssrc in this.resolution)
|
||||
{
|
||||
for(var ssrc in this.resolution) {
|
||||
resolutionValue = this.resolution[ssrc];
|
||||
}
|
||||
}
|
||||
|
||||
if(this.jid === null)
|
||||
{
|
||||
if(this.jid === null) {
|
||||
resolution = "";
|
||||
if(this.resolution === null || !Object.keys(this.resolution) ||
|
||||
Object.keys(this.resolution).length === 0)
|
||||
{
|
||||
Object.keys(this.resolution).length === 0) {
|
||||
resolution = "N/A";
|
||||
}
|
||||
else
|
||||
for(i in this.resolution)
|
||||
{
|
||||
} else {
|
||||
for (i in this.resolution) {
|
||||
resolutionValue = this.resolution[i];
|
||||
if(resolutionValue)
|
||||
{
|
||||
if(resolutionValue.height &&
|
||||
resolutionValue.width)
|
||||
{
|
||||
resolution += (resolution === ""? "" : ", ") +
|
||||
resolutionValue.width + "x" +
|
||||
resolutionValue.height;
|
||||
if (resolutionValue) {
|
||||
if (resolutionValue.height &&
|
||||
resolutionValue.width) {
|
||||
resolution += (resolution === "" ? "" : ", ") +
|
||||
resolutionValue.width + "x" +
|
||||
resolutionValue.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!resolutionValue ||
|
||||
}
|
||||
} else if(!resolutionValue ||
|
||||
!resolutionValue.height ||
|
||||
!resolutionValue.width)
|
||||
{
|
||||
!resolutionValue.width) {
|
||||
resolution = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
resolution = resolutionValue.width + "x" + resolutionValue.height;
|
||||
}
|
||||
|
||||
@@ -155,22 +138,20 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||
if(this.videoContainer.videoSpanId == "localVideoContainer") {
|
||||
result += "<div class=\"jitsipopover_showmore\" " +
|
||||
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
|
||||
this.jid + "')\" data-i18n='connectionindicator." +
|
||||
// FIXME: we do not know local jid when this text is generated
|
||||
//this.jid + "')\" data-i18n='connectionindicator." +
|
||||
"local')\" data-i18n='connectionindicator." +
|
||||
(this.showMoreValue ? "less" : "more") + "'>" +
|
||||
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
|
||||
"</div><br />";
|
||||
}
|
||||
|
||||
if(this.showMoreValue)
|
||||
{
|
||||
if (this.showMoreValue) {
|
||||
var downloadBandwidth, uploadBandwidth, transport;
|
||||
if(this.bandwidth === null)
|
||||
{
|
||||
if (this.bandwidth === null) {
|
||||
downloadBandwidth = "N/A";
|
||||
uploadBandwidth = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
downloadBandwidth = this.bandwidth.download?
|
||||
this.bandwidth.download + " Kbps" :
|
||||
"N/A";
|
||||
@@ -179,45 +160,36 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||
"N/A";
|
||||
}
|
||||
|
||||
if(!this.transport || this.transport.length === 0)
|
||||
{
|
||||
if (!this.transport || this.transport.length === 0) {
|
||||
transport = "<tr>" +
|
||||
"<td><span class='jitsipopover_blue' " +
|
||||
"data-i18n='connectionindicator.address'>" +
|
||||
translate("connectionindicator.address") + "</span></td>" +
|
||||
"<td> N/A</td></tr>";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
|
||||
for(i = 0; i < this.transport.length; i++)
|
||||
{
|
||||
for(i = 0; i < this.transport.length; i++) {
|
||||
var ip = ConnectionIndicator.getIP(this.transport[i].ip);
|
||||
var port = ConnectionIndicator.getPort(this.transport[i].ip);
|
||||
var localIP =
|
||||
ConnectionIndicator.getIP(this.transport[i].localip);
|
||||
var localPort =
|
||||
ConnectionIndicator.getPort(this.transport[i].localip);
|
||||
if(data.remoteIP.indexOf(ip) == -1)
|
||||
{
|
||||
if(data.remoteIP.indexOf(ip) == -1) {
|
||||
data.remoteIP.push(ip);
|
||||
}
|
||||
|
||||
if(data.remotePort.indexOf(port) == -1)
|
||||
{
|
||||
if(data.remotePort.indexOf(port) == -1) {
|
||||
data.remotePort.push(port);
|
||||
}
|
||||
|
||||
if(data.localIP.indexOf(localIP) == -1)
|
||||
{
|
||||
if(data.localIP.indexOf(localIP) == -1) {
|
||||
data.localIP.push(localIP);
|
||||
}
|
||||
|
||||
if(data.localPort.indexOf(localPort) == -1)
|
||||
{
|
||||
if(data.localPort.indexOf(localPort) == -1) {
|
||||
data.localPort.push(localPort);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var local_address_key = "connectionindicator.localaddress";
|
||||
@@ -283,7 +255,6 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||
uploadBandwidth + "</td></tr>";
|
||||
|
||||
result += transport + "</table>";
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -298,11 +269,9 @@ ConnectionIndicator.prototype.showMore = function () {
|
||||
};
|
||||
|
||||
|
||||
function createIcon(classes)
|
||||
{
|
||||
function createIcon(classes) {
|
||||
var icon = document.createElement("span");
|
||||
for(var i in classes)
|
||||
{
|
||||
for(var i in classes) {
|
||||
icon.classList.add(classes[i]);
|
||||
}
|
||||
icon.appendChild(
|
||||
@@ -317,7 +286,8 @@ ConnectionIndicator.prototype.create = function () {
|
||||
this.connectionIndicatorContainer = document.createElement("div");
|
||||
this.connectionIndicatorContainer.className = "connectionindicator";
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
this.videoContainer.container.appendChild(this.connectionIndicatorContainer);
|
||||
this.videoContainer.container.appendChild(
|
||||
this.connectionIndicatorContainer);
|
||||
this.popover = new JitsiPopover(
|
||||
$("#" + this.videoContainer.videoSpanId + " > .connectionindicator"),
|
||||
{content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
|
||||
@@ -328,20 +298,17 @@ ConnectionIndicator.prototype.create = function () {
|
||||
createIcon(["connection", "connection_empty"]));
|
||||
this.fullIcon = this.connectionIndicatorContainer.appendChild(
|
||||
createIcon(["connection", "connection_full"]));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the indicator
|
||||
*/
|
||||
ConnectionIndicator.prototype.remove = function()
|
||||
{
|
||||
ConnectionIndicator.prototype.remove = function() {
|
||||
if (this.connectionIndicatorContainer.parentNode) {
|
||||
this.connectionIndicatorContainer.parentNode.removeChild(
|
||||
this.connectionIndicatorContainer);
|
||||
}
|
||||
this.popover.forceHide();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -350,16 +317,13 @@ ConnectionIndicator.prototype.remove = function()
|
||||
* @param object the statistics data.
|
||||
*/
|
||||
ConnectionIndicator.prototype.updateConnectionQuality =
|
||||
function (percent, object) {
|
||||
function (percent, object) {
|
||||
|
||||
if(percent === null)
|
||||
{
|
||||
if (percent === null) {
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
this.popover.forceHide();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(this.connectionIndicatorContainer.style.display == "none") {
|
||||
this.connectionIndicatorContainer.style.display = "block";
|
||||
this.videoContainer.updateIconPositions();
|
||||
@@ -369,14 +333,11 @@ function (percent, object) {
|
||||
this.bitrate = object.bitrate;
|
||||
this.packetLoss = object.packetLoss;
|
||||
this.transport = object.transport;
|
||||
if(object.resolution)
|
||||
{
|
||||
if (object.resolution) {
|
||||
this.resolution = object.resolution;
|
||||
}
|
||||
for(var quality in ConnectionIndicator.connectionQualityValues)
|
||||
{
|
||||
if(percent >= quality)
|
||||
{
|
||||
for (var quality in ConnectionIndicator.connectionQualityValues) {
|
||||
if (percent >= quality) {
|
||||
this.fullIcon.style.width =
|
||||
ConnectionIndicator.connectionQualityValues[quality];
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* global $, APP, Strophe, interfaceConfig */
|
||||
var Avatar = require("../avatar/Avatar");
|
||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
var UIEvents = require("../../../service/UI/UIEvents");
|
||||
var xmpp = require("../../xmpp/xmpp");
|
||||
var ToolbarToggler = require("../toolbars/ToolbarToggler");
|
||||
|
||||
// FIXME: With Temasys we have to re-select everytime
|
||||
//var video = $('#largeVideo');
|
||||
@@ -12,10 +14,45 @@ var currentVideoHeight = null;
|
||||
// By default we use camera
|
||||
var getVideoSize = getCameraVideoSize;
|
||||
var getVideoPosition = getCameraVideoPosition;
|
||||
/**
|
||||
* The small video instance that is displayed in the large video
|
||||
* @type {SmallVideo}
|
||||
*/
|
||||
var currentSmallVideo = null;
|
||||
var oldSmallVideo = null;
|
||||
|
||||
/**
|
||||
* Indicates whether the large video is enabled.
|
||||
* @type {boolean}
|
||||
*/
|
||||
var isEnabled = true;
|
||||
/**
|
||||
* Current large video state.
|
||||
* Possible values - video, prezi or etherpad.
|
||||
* @type {string}
|
||||
*/
|
||||
var state = "video";
|
||||
|
||||
/**
|
||||
* Returns the html element associated with the passed state of large video
|
||||
* @param state the state.
|
||||
* @returns {JQuery|*|jQuery|HTMLElement} the container.
|
||||
*/
|
||||
function getContainerByState(state)
|
||||
{
|
||||
var selector = null;
|
||||
switch (state)
|
||||
{
|
||||
case "video":
|
||||
selector = "#largeVideoWrapper";
|
||||
break;
|
||||
case "etherpad":
|
||||
selector = "#etherpad>iframe";
|
||||
break;
|
||||
case "prezi":
|
||||
selector = "#presentation>iframe";
|
||||
break;
|
||||
}
|
||||
return (selector !== null)? $(selector) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size and position of the given video element.
|
||||
@@ -32,8 +69,7 @@ function positionVideo(video,
|
||||
horizontalIndent,
|
||||
verticalIndent,
|
||||
animate) {
|
||||
if(animate)
|
||||
{
|
||||
if (animate) {
|
||||
video.animate({
|
||||
width: width,
|
||||
height: height,
|
||||
@@ -46,9 +82,7 @@ function positionVideo(video,
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
video.width(width);
|
||||
video.height(height);
|
||||
video.css({ top: verticalIndent + 'px',
|
||||
@@ -59,7 +93,6 @@ function positionVideo(video,
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of the video dimensions, so that it keeps it's aspect
|
||||
* ratio and fits available area with it's larger dimension. This method
|
||||
@@ -185,27 +218,26 @@ function getCameraVideoSize(videoWidth,
|
||||
function updateActiveSpeakerAvatarSrc() {
|
||||
var avatar = $("#activeSpeakerAvatar")[0];
|
||||
var jid = currentSmallVideo.peerJid;
|
||||
var url = Avatar.getGravatarUrl(jid);
|
||||
var url = Avatar.getActiveSpeakerUrl(jid);
|
||||
if (avatar.src === url)
|
||||
return;
|
||||
var isMuted = null;
|
||||
if (!currentSmallVideo.isLocal &&
|
||||
!LargeVideo.VideoLayout.isInLastN(currentSmallVideo.resourceJid)) {
|
||||
isMuted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isMuted = APP.RTC.isVideoMuted(jid);
|
||||
}
|
||||
|
||||
if (jid && isMuted !== null) {
|
||||
if (jid) {
|
||||
avatar.src = url;
|
||||
$("#largeVideo").css("visibility", isMuted ? "hidden" : "visible");
|
||||
currentSmallVideo.showAvatar(isMuted);
|
||||
currentSmallVideo.showAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the video source of the large video.
|
||||
* @param isVisible
|
||||
*/
|
||||
function changeVideo(isVisible) {
|
||||
|
||||
if (!currentSmallVideo) {
|
||||
console.error("Unable to change large video - no 'currentSmallVideo'");
|
||||
return;
|
||||
}
|
||||
|
||||
updateActiveSpeakerAvatarSrc();
|
||||
|
||||
APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
|
||||
@@ -216,49 +248,92 @@ function changeVideo(isVisible) {
|
||||
var flipX = currentSmallVideo.flipX;
|
||||
|
||||
if (flipX && videoTransform !== 'scaleX(-1)') {
|
||||
document.getElementById('largeVideo').style.webkitTransform
|
||||
= "scaleX(-1)";
|
||||
}
|
||||
else if (!flipX && videoTransform === 'scaleX(-1)') {
|
||||
document.getElementById('largeVideo').style.webkitTransform
|
||||
= "none";
|
||||
document.getElementById('largeVideo').style.webkitTransform =
|
||||
"scaleX(-1)";
|
||||
} else if (!flipX && videoTransform === 'scaleX(-1)') {
|
||||
document.getElementById('largeVideo').style.webkitTransform =
|
||||
"none";
|
||||
}
|
||||
|
||||
var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
|
||||
var isDesktop = currentSmallVideo.getVideoType() === 'screen';
|
||||
// Change the way we'll be measuring and positioning large video
|
||||
|
||||
getVideoSize = isDesktop
|
||||
? getDesktopVideoSize
|
||||
: getCameraVideoSize;
|
||||
getVideoPosition = isDesktop
|
||||
? getDesktopVideoPosition
|
||||
: getCameraVideoPosition;
|
||||
getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize;
|
||||
getVideoPosition = isDesktop ? getDesktopVideoPosition :
|
||||
getCameraVideoPosition;
|
||||
|
||||
|
||||
// Only if the large video is currently visible.
|
||||
// Disable previous dominant speaker video.
|
||||
if (oldSmallVideo) {
|
||||
oldSmallVideo.enableDominantSpeaker(false);
|
||||
}
|
||||
|
||||
// Enable new dominant speaker in the remote videos section.
|
||||
if (currentSmallVideo) {
|
||||
currentSmallVideo.enableDominantSpeaker(true);
|
||||
}
|
||||
|
||||
if (isVisible) {
|
||||
$('#largeVideo').fadeIn(300);
|
||||
LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo);
|
||||
|
||||
$('#largeVideoWrapper').fadeTo(300, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the html elements for the large video.
|
||||
*/
|
||||
function createLargeVideoHTML()
|
||||
{
|
||||
var html = '<div id="largeVideoContainer" class="videocontainer">';
|
||||
html += '<div id="presentation"></div>' +
|
||||
'<div id="etherpad"></div>' +
|
||||
'<a target="_new"><div class="watermark leftwatermark"></div></a>' +
|
||||
'<a target="_new"><div class="watermark rightwatermark"></div></a>' +
|
||||
'<a class="poweredby" href="http://jitsi.org" target="_new" >' +
|
||||
'<span data-i18n="poweredby"></span> jitsi.org' +
|
||||
'</a>'+
|
||||
'<div id="activeSpeaker">' +
|
||||
'<img id="activeSpeakerAvatar" src=""/>' +
|
||||
'<canvas id="activeSpeakerAudioLevel"></canvas>' +
|
||||
'</div>' +
|
||||
'<div id="largeVideoWrapper">' +
|
||||
'<video id="largeVideo" autoplay oncontextmenu="return false;"></video>' +
|
||||
'</div id="largeVideoWrapper">' +
|
||||
'<span id="videoConnectionMessage"></span>';
|
||||
html += '</div>';
|
||||
$(html).prependTo("#videospace");
|
||||
|
||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
||||
var leftWatermarkDiv
|
||||
= $("#largeVideoContainer div[class='watermark leftwatermark']");
|
||||
|
||||
leftWatermarkDiv.css({display: 'block'});
|
||||
leftWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.JITSI_WATERMARK_LINK;
|
||||
}
|
||||
|
||||
if(oldSmallVideo)
|
||||
oldSmallVideo.showAvatar();
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
var rightWatermarkDiv
|
||||
= $("#largeVideoContainer div[class='watermark rightwatermark']");
|
||||
|
||||
rightWatermarkDiv.css({display: 'block'});
|
||||
rightWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.BRAND_WATERMARK_LINK;
|
||||
rightWatermarkDiv.get(0).style.backgroundImage
|
||||
= "url(images/rightwatermark.png)";
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_POWERED_BY) {
|
||||
$("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
|
||||
}
|
||||
|
||||
if (!RTCBrowserType.isIExplorer()) {
|
||||
$('#largeVideo').volume = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var LargeVideo = {
|
||||
|
||||
init: function (VideoLayout, emitter) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
createLargeVideoHTML();
|
||||
|
||||
this.VideoLayout = VideoLayout;
|
||||
this.eventEmitter = emitter;
|
||||
this.eventEmitter.emit(UIEvents.LARGEVIDEO_INIT);
|
||||
var self = this;
|
||||
// Listen for large video size updates
|
||||
var largeVideo = $('#largeVideo')[0];
|
||||
@@ -278,40 +353,59 @@ var LargeVideo = {
|
||||
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
|
||||
*/
|
||||
isLargeVideoVisible: function() {
|
||||
return $('#largeVideo').is(':visible');
|
||||
return $('#largeVideoWrapper').is(':visible');
|
||||
},
|
||||
/**
|
||||
* Returns <tt>true</tt> if the user is currently displayed on large video.
|
||||
*/
|
||||
isCurrentlyOnLarge: function (resourceJid) {
|
||||
return currentSmallVideo && resourceJid &&
|
||||
currentSmallVideo.getResourceJid() === resourceJid;
|
||||
},
|
||||
/**
|
||||
* Updates the large video with the given new video source.
|
||||
*/
|
||||
updateLargeVideo: function(resourceJid, forceUpdate) {
|
||||
updateLargeVideo: function (resourceJid, forceUpdate) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
|
||||
console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
|
||||
|
||||
if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
|
||||
|| forceUpdate) {
|
||||
if (!newSmallVideo) {
|
||||
console.error("Small video not found for: " + resourceJid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) {
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
|
||||
if(currentSmallVideo) {
|
||||
var oldSmallVideo = null;
|
||||
if (currentSmallVideo) {
|
||||
oldSmallVideo = currentSmallVideo;
|
||||
} else {
|
||||
oldSmallVideo = null;
|
||||
}
|
||||
|
||||
currentSmallVideo = newSmallVideo;
|
||||
var oldJid = null;
|
||||
if(oldSmallVideo)
|
||||
oldJid = oldSmallVideo.peerJid;
|
||||
|
||||
var oldJid = null;
|
||||
if (oldSmallVideo)
|
||||
oldJid = oldSmallVideo.peerJid;
|
||||
if (oldJid !== resourceJid) {
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
|
||||
resourceJid);
|
||||
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, resourceJid);
|
||||
}
|
||||
$('#largeVideo').fadeOut(300,
|
||||
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
||||
// largeVideo, because when Temasys plugin is in use it replaces
|
||||
// <video> elements with plugin <object> tag. In Safari jQuery is
|
||||
// unable to store values on this plugin object which breaks all
|
||||
// animation effects performed on it directly.
|
||||
//
|
||||
// If for any reason large video was hidden before calling fadeOut
|
||||
// changeVideo will never be called, so we call show() in chain just
|
||||
// to be sure
|
||||
$('#largeVideoWrapper').show().fadeTo(300, 0,
|
||||
changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
|
||||
} else {
|
||||
if(currentSmallVideo) {
|
||||
if (currentSmallVideo) {
|
||||
currentSmallVideo.showAvatar();
|
||||
}
|
||||
}
|
||||
@@ -322,31 +416,32 @@ var LargeVideo = {
|
||||
* Shows/hides the large video.
|
||||
*/
|
||||
setLargeVideoVisible: function(isVisible) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
if (isVisible) {
|
||||
$('#largeVideo').css({visibility: 'visible'});
|
||||
$('#largeVideoWrapper').css({visibility: 'visible'});
|
||||
$('.watermark').css({visibility: 'visible'});
|
||||
if(currentSmallVideo)
|
||||
currentSmallVideo.enableDominantSpeaker(true);
|
||||
}
|
||||
else {
|
||||
$('#largeVideo').css({visibility: 'hidden'});
|
||||
$('#largeVideoWrapper').css({visibility: 'hidden'});
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
$('.watermark').css({visibility: 'hidden'});
|
||||
if(currentSmallVideo)
|
||||
currentSmallVideo.enableDominantSpeaker(false);
|
||||
}
|
||||
},
|
||||
onVideoTypeChanged: function (jid) {
|
||||
if(jid && currentSmallVideo && jid === currentSmallVideo.peerJid)
|
||||
onVideoTypeChanged: function (resourceJid, newVideoType) {
|
||||
if (!isEnabled)
|
||||
return;
|
||||
if (LargeVideo.isCurrentlyOnLarge(resourceJid))
|
||||
{
|
||||
var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
|
||||
getVideoSize = isDesktop
|
||||
? getDesktopVideoSize
|
||||
: getCameraVideoSize;
|
||||
getVideoPosition = isDesktop
|
||||
? getDesktopVideoPosition
|
||||
var isDesktop = newVideoType === 'screen';
|
||||
getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize;
|
||||
getVideoPosition = isDesktop ? getDesktopVideoPosition
|
||||
: getCameraVideoPosition;
|
||||
this.position(null, null);
|
||||
this.position(null, null, null, null, true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -357,6 +452,8 @@ var LargeVideo = {
|
||||
*/
|
||||
position: function (videoWidth, videoHeight,
|
||||
videoSpaceWidth, videoSpaceHeight, animate) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
if(!videoSpaceWidth)
|
||||
videoSpaceWidth = $('#videospace').width();
|
||||
if(!videoSpaceHeight)
|
||||
@@ -378,18 +475,22 @@ var LargeVideo = {
|
||||
var horizontalIndent = videoPosition[0];
|
||||
var verticalIndent = videoPosition[1];
|
||||
|
||||
positionVideo($('#largeVideo'),
|
||||
positionVideo($('#largeVideoWrapper'),
|
||||
largeVideoWidth,
|
||||
largeVideoHeight,
|
||||
horizontalIndent, verticalIndent, animate);
|
||||
},
|
||||
|
||||
isLargeVideoOnTop: function () {
|
||||
var Etherpad = require("../etherpad/Etherpad");
|
||||
var Prezi = require("../prezi/Prezi");
|
||||
return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
|
||||
},
|
||||
/**
|
||||
* Resizes the large html elements.
|
||||
* @param animate boolean property that indicates whether the resize should be animated or not.
|
||||
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
|
||||
* If that parameter is null the method will check the chat pannel visibility.
|
||||
* @param completeFunction a function to be called when the video space is resized
|
||||
* @returns {*[]} array with the current width and height values of the largeVideo html element.
|
||||
*/
|
||||
resize: function (animate, isVisible, completeFunction) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
var availableHeight = window.innerHeight;
|
||||
var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
|
||||
|
||||
@@ -399,19 +500,8 @@ var LargeVideo = {
|
||||
var top = availableHeight / 2 - avatarSize / 4 * 3;
|
||||
$('#activeSpeaker').css('top', top);
|
||||
|
||||
if(animate)
|
||||
{
|
||||
$('#videospace').animate({
|
||||
right: window.innerWidth - availableWidth,
|
||||
width: availableWidth,
|
||||
height: availableHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500,
|
||||
complete: completeFunction
|
||||
});
|
||||
|
||||
this.VideoLayout.resizeVideoSpace(animate, isVisible, completeFunction);
|
||||
if(animate) {
|
||||
$('#largeVideoContainer').animate({
|
||||
width: availableWidth,
|
||||
height: availableHeight
|
||||
@@ -420,45 +510,175 @@ var LargeVideo = {
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#videospace').width(availableWidth);
|
||||
$('#videospace').height(availableHeight);
|
||||
} else {
|
||||
$('#largeVideoContainer').width(availableWidth);
|
||||
$('#largeVideoContainer').height(availableHeight);
|
||||
}
|
||||
return [availableWidth, availableHeight];
|
||||
|
||||
},
|
||||
resizeVideoAreaAnimated: function (isVisible, completeFunction) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
var size = this.resize(true, isVisible, completeFunction);
|
||||
this.position(null, null, size[0], size[1], true);
|
||||
},
|
||||
getResourceJid: function () {
|
||||
if(!currentSmallVideo)
|
||||
return null;
|
||||
return currentSmallVideo.resourceJid;
|
||||
return currentSmallVideo ? currentSmallVideo.getResourceJid() : null;
|
||||
},
|
||||
updateAvatar: function (resourceJid) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
if (resourceJid === this.getResourceJid()) {
|
||||
updateActiveSpeakerAvatarSrc();
|
||||
}
|
||||
},
|
||||
showAvatar: function (resourceJid, show) {
|
||||
if(this.getResourceJid() === resourceJid
|
||||
&& LargeVideo.isLargeVideoOnTop())
|
||||
{
|
||||
$("#largeVideo").css("visibility", show ? "hidden" : "visible");
|
||||
if (!isEnabled)
|
||||
return;
|
||||
if (this.getResourceJid() === resourceJid && state === "video") {
|
||||
$("#largeVideoWrapper")
|
||||
.css("visibility", show ? "hidden" : "visible");
|
||||
$('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* Disables the large video
|
||||
*/
|
||||
disable: function () {
|
||||
isEnabled = false;
|
||||
},
|
||||
/**
|
||||
* Enables the large video
|
||||
*/
|
||||
enable: function () {
|
||||
isEnabled = true;
|
||||
},
|
||||
/**
|
||||
* Returns true if the video is enabled.
|
||||
*/
|
||||
isEnabled: function () {
|
||||
return isEnabled;
|
||||
},
|
||||
/**
|
||||
* Creates the iframe used by the etherpad
|
||||
* @param src the value for src attribute
|
||||
* @param onloadHandler handler executed when the iframe loads it content
|
||||
* @returns {HTMLElement} the iframe
|
||||
*/
|
||||
createEtherpadIframe: function (src, onloadHandler) {
|
||||
if(!isEnabled)
|
||||
return;
|
||||
|
||||
var etherpadIFrame = document.createElement('iframe');
|
||||
etherpadIFrame.src = src;
|
||||
etherpadIFrame.frameBorder = 0;
|
||||
etherpadIFrame.scrolling = "no";
|
||||
etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
|
||||
etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
|
||||
etherpadIFrame.setAttribute('style', 'visibility: hidden;');
|
||||
|
||||
document.getElementById('etherpad').appendChild(etherpadIFrame);
|
||||
|
||||
etherpadIFrame.onload = onloadHandler;
|
||||
|
||||
return etherpadIFrame;
|
||||
},
|
||||
/**
|
||||
* Changes the state of the large video.
|
||||
* Possible values - video, prezi, etherpad.
|
||||
* @param newState - the new state
|
||||
*/
|
||||
setState: function (newState) {
|
||||
if(state === newState)
|
||||
return;
|
||||
var currentContainer = getContainerByState(state);
|
||||
if(!currentContainer)
|
||||
return;
|
||||
|
||||
var self = this;
|
||||
var oldState = state;
|
||||
|
||||
switch (newState)
|
||||
{
|
||||
case "etherpad":
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
currentContainer.fadeOut(300, function () {
|
||||
if (oldState === "prezi") {
|
||||
currentContainer.css({opacity: '0'});
|
||||
$('#reloadPresentation').css({display: 'none'});
|
||||
}
|
||||
else {
|
||||
self.setLargeVideoVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#etherpad>iframe').fadeIn(300, function () {
|
||||
document.body.style.background = '#eeeeee';
|
||||
$('#etherpad>iframe').css({visibility: 'visible'});
|
||||
$('#etherpad').css({zIndex: 2});
|
||||
});
|
||||
break;
|
||||
case "prezi":
|
||||
var prezi = $('#presentation>iframe');
|
||||
currentContainer.fadeOut(300, function() {
|
||||
document.body.style.background = 'black';
|
||||
});
|
||||
prezi.fadeIn(300, function() {
|
||||
prezi.css({opacity:'1'});
|
||||
ToolbarToggler.dockToolbar(true);//fix that
|
||||
self.setLargeVideoVisible(false);
|
||||
$('#etherpad>iframe').css({visibility: 'hidden'});
|
||||
$('#etherpad').css({zIndex: 0});
|
||||
});
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
break;
|
||||
|
||||
case "video":
|
||||
currentContainer.fadeOut(300, function () {
|
||||
$('#presentation>iframe').css({opacity:'0'});
|
||||
$('#reloadPresentation').css({display:'none'});
|
||||
$('#etherpad>iframe').css({visibility: 'hidden'});
|
||||
$('#etherpad').css({zIndex: 0});
|
||||
document.body.style.background = 'black';
|
||||
ToolbarToggler.dockToolbar(false);//fix that
|
||||
});
|
||||
$('#largeVideoWrapper').fadeIn(300, function () {
|
||||
self.setLargeVideoVisible(true);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
state = newState;
|
||||
|
||||
},
|
||||
/**
|
||||
* Returns the current state of the large video.
|
||||
* @returns {string} the current state - video, prezi or etherpad.
|
||||
*/
|
||||
getState: function () {
|
||||
return state;
|
||||
},
|
||||
/**
|
||||
* Sets hover handlers for the large video container div.
|
||||
*
|
||||
* @param inHandler
|
||||
* @param outHandler
|
||||
*/
|
||||
setHover: function(inHandler, outHandler)
|
||||
{
|
||||
$('#largeVideoContainer').hover(inHandler, outHandler);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables/disables the filter indicating a video problem to the user.
|
||||
*
|
||||
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
|
||||
*/
|
||||
enableVideoProblemFilter: function (enable) {
|
||||
$("#largeVideo").toggleClass("videoProblemFilter", enable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = LargeVideo;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global $, interfaceConfig, APP */
|
||||
var SmallVideo = require("./SmallVideo");
|
||||
var ConnectionIndicator = require("./ConnectionIndicator");
|
||||
var NicknameHandler = require("../util/NicknameHandler");
|
||||
@@ -5,15 +6,14 @@ var UIUtil = require("../util/UIUtil");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||
|
||||
function LocalVideo(VideoLayout)
|
||||
{
|
||||
function LocalVideo(VideoLayout) {
|
||||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.bindHoverHandler();
|
||||
this.VideoLayout = VideoLayout;
|
||||
this.flipX = true;
|
||||
this.isLocal = true;
|
||||
this.peerJid = null;
|
||||
this.resourceJid = null;
|
||||
}
|
||||
|
||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
@@ -22,7 +22,7 @@ LocalVideo.prototype.constructor = LocalVideo;
|
||||
/**
|
||||
* Creates the edit display name button.
|
||||
*
|
||||
* @returns the edit button
|
||||
* @returns {object} the edit button
|
||||
*/
|
||||
function createEditDisplayNameButton() {
|
||||
var editButton = document.createElement('a');
|
||||
@@ -35,15 +35,14 @@ function createEditDisplayNameButton() {
|
||||
return editButton;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
*/
|
||||
LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
if (!this.container) {
|
||||
console.warn(
|
||||
"Unable to set displayName - " + this.videoSpanId + " does not exist");
|
||||
"Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,17 +50,16 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
var defaultLocalDisplayName = APP.translation.generateTranslationHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
|
||||
var meHTML;
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
|
||||
if (nameSpan.text() !== displayName) {
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
var meHTML = APP.translation.generateTranslationHTML("me");
|
||||
if (displayName && displayName.length > 0) {
|
||||
meHTML = APP.translation.generateTranslationHTML("me");
|
||||
$('#localDisplayName').html(displayName + ' (' + meHTML + ')');
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$('#localDisplayName').html(defaultLocalDisplayName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var editButton = createEditDisplayNameButton();
|
||||
@@ -72,7 +70,7 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
var meHTML = APP.translation.generateTranslationHTML("me");
|
||||
meHTML = APP.translation.generateTranslationHTML("me");
|
||||
nameSpan.innerHTML = displayName + meHTML;
|
||||
}
|
||||
else {
|
||||
@@ -109,18 +107,19 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
$('#localVideoContainer .displayname')
|
||||
.bind("click", function (e) {
|
||||
|
||||
var editDisplayName = $('#editDisplayName');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$('#localDisplayName').hide();
|
||||
$('#editDisplayName').show();
|
||||
$('#editDisplayName').focus();
|
||||
$('#editDisplayName').select();
|
||||
editDisplayName.show();
|
||||
editDisplayName.focus();
|
||||
editDisplayName.select();
|
||||
|
||||
$('#editDisplayName').one("focusout", function (e) {
|
||||
editDisplayName.one("focusout", function (e) {
|
||||
self.VideoLayout.inputDisplayNameHandler(this.value);
|
||||
});
|
||||
|
||||
$('#editDisplayName').on('keydown', function (e) {
|
||||
editDisplayName.on('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
self.VideoLayout.inputDisplayNameHandler(this.value);
|
||||
@@ -128,38 +127,34 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LocalVideo.prototype.inputDisplayNameHandler = function (name) {
|
||||
NicknameHandler.setNickname(name);
|
||||
|
||||
if (!$('#localDisplayName').is(":visible")) {
|
||||
if (NicknameHandler.getNickname())
|
||||
{
|
||||
var localDisplayName = $('#localDisplayName');
|
||||
if (!localDisplayName.is(":visible")) {
|
||||
if (NicknameHandler.getNickname()) {
|
||||
var meHTML = APP.translation.generateTranslationHTML("me");
|
||||
$('#localDisplayName').html(NicknameHandler.getNickname() + " (" + meHTML + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
localDisplayName.html(NicknameHandler.getNickname() + " (" +
|
||||
meHTML + ")");
|
||||
} else {
|
||||
var defaultHTML = APP.translation.generateTranslationHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
$('#localDisplayName')
|
||||
.html(defaultHTML);
|
||||
localDisplayName .html(defaultHTML);
|
||||
}
|
||||
$('#localDisplayName').show();
|
||||
localDisplayName.show();
|
||||
}
|
||||
|
||||
$('#editDisplayName').hide();
|
||||
}
|
||||
};
|
||||
|
||||
LocalVideo.prototype.createConnectionIndicator = function()
|
||||
{
|
||||
LocalVideo.prototype.createConnectionIndicator = function() {
|
||||
if(this.connectionIndicator)
|
||||
return;
|
||||
|
||||
this.connectionIndicator
|
||||
= new ConnectionIndicator(this, null);
|
||||
}
|
||||
this.connectionIndicator = new ConnectionIndicator(this, null);
|
||||
};
|
||||
|
||||
LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||
var self = this;
|
||||
@@ -171,31 +166,19 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
self.VideoLayout.handleVideoThumbClicked(
|
||||
false,
|
||||
true,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
|
||||
$('#localVideoContainer').off('click');
|
||||
$('#localVideoContainer').on('click', localVideoClick);
|
||||
var localVideoContainerSelector = $('#localVideoContainer');
|
||||
localVideoContainerSelector.off('click');
|
||||
localVideoContainerSelector.on('click', localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
self.showDisplayName(true);
|
||||
},
|
||||
function() {
|
||||
if (!LargeVideo.isLargeVideoVisible()
|
||||
|| APP.xmpp.myResource() !== LargeVideo.getResourceJid())
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
|
||||
if(isMuted)
|
||||
{
|
||||
if(isMuted) {
|
||||
APP.UI.setVideoMute(true);
|
||||
return;
|
||||
}
|
||||
this.flipX = (stream.videoType == "screen")? false : true;
|
||||
this.flipX = stream.videoType != "screen";
|
||||
var localVideo = document.createElement('video');
|
||||
localVideo.id = 'localVideo_' +
|
||||
APP.RTC.getStreamID(stream.getOriginalStream());
|
||||
@@ -206,7 +189,8 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||
localVideo.oncontextmenu = function () { return false; };
|
||||
|
||||
var localVideoContainer = document.getElementById('localVideoWrapper');
|
||||
localVideoContainer.appendChild(localVideo);
|
||||
// Put the new video always in front
|
||||
UIUtil.prependChild(localVideoContainer, localVideo);
|
||||
|
||||
var localVideoSelector = $('#' + localVideo.id);
|
||||
|
||||
@@ -231,11 +215,18 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
self.VideoLayout.updateRemovedVideo(APP.xmpp.myResource());
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
LocalVideo.prototype.joined = function (jid) {
|
||||
this.peerJid = jid;
|
||||
this.resourceJid = Strophe.getResourceFromJid(jid);
|
||||
}
|
||||
};
|
||||
|
||||
LocalVideo.prototype.getResourceJid = function () {
|
||||
var myResource = APP.xmpp.myResource();
|
||||
if (!myResource) {
|
||||
console.error("Requested local resource before we're in the MUC");
|
||||
}
|
||||
return myResource;
|
||||
};
|
||||
|
||||
module.exports = LocalVideo;
|
||||
@@ -1,12 +1,13 @@
|
||||
/* global $, APP, require, Strophe, interfaceConfig */
|
||||
var ConnectionIndicator = require("./ConnectionIndicator");
|
||||
var SmallVideo = require("./SmallVideo");
|
||||
var AudioLevels = require("../audio_levels/AudioLevels");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
var Avatar = require("../avatar/Avatar");
|
||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||
var UIUtils = require("../util/UIUtil");
|
||||
|
||||
function RemoteVideo(peerJid, VideoLayout)
|
||||
{
|
||||
function RemoteVideo(peerJid, VideoLayout) {
|
||||
this.peerJid = peerJid;
|
||||
this.resourceJid = Strophe.getResourceFromJid(peerJid);
|
||||
this.videoSpanId = 'participant_' + this.resourceJid;
|
||||
@@ -19,6 +20,7 @@ function RemoteVideo(peerJid, VideoLayout)
|
||||
nickfield.className = "nick";
|
||||
nickfield.appendChild(document.createTextNode(this.resourceJid));
|
||||
this.container.appendChild(nickfield);
|
||||
this.bindHoverHandler();
|
||||
this.flipX = false;
|
||||
this.isLocal = false;
|
||||
}
|
||||
@@ -42,95 +44,106 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||
* @param jid the jid indicating the video for which we're adding a menu.
|
||||
* @param parentElement the parent element where this menu will be added
|
||||
*/
|
||||
RemoteVideo.prototype.addRemoteVideoMenu = function () {
|
||||
var spanElement = document.createElement('span');
|
||||
spanElement.className = 'remotevideomenu';
|
||||
|
||||
this.container.appendChild(spanElement);
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
RemoteVideo.prototype.addRemoteVideoMenu = function () {
|
||||
var spanElement = document.createElement('span');
|
||||
spanElement.className = 'remotevideomenu';
|
||||
|
||||
var menuElement = document.createElement('i');
|
||||
menuElement.className = 'fa fa-angle-down';
|
||||
menuElement.title = 'Remote user controls';
|
||||
spanElement.appendChild(menuElement);
|
||||
this.container.appendChild(spanElement);
|
||||
|
||||
var menuElement = document.createElement('i');
|
||||
menuElement.className = 'fa fa-angle-down';
|
||||
menuElement.title = 'Remote user controls';
|
||||
spanElement.appendChild(menuElement);
|
||||
|
||||
|
||||
var popupmenuElement = document.createElement('ul');
|
||||
popupmenuElement.className = 'popupmenu';
|
||||
popupmenuElement.id
|
||||
= 'remote_popupmenu_' + this.resourceJid;
|
||||
spanElement.appendChild(popupmenuElement);
|
||||
var popupmenuElement = document.createElement('ul');
|
||||
popupmenuElement.className = 'popupmenu';
|
||||
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
|
||||
spanElement.appendChild(popupmenuElement);
|
||||
|
||||
var muteMenuItem = document.createElement('li');
|
||||
var muteLinkItem = document.createElement('a');
|
||||
var muteMenuItem = document.createElement('li');
|
||||
var muteLinkItem = document.createElement('a');
|
||||
|
||||
var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
|
||||
var mutedIndicator = "<i style='float:left;' " +
|
||||
"class='icon-mic-disabled'></i>";
|
||||
|
||||
if (!this.isMuted) {
|
||||
muteLinkItem.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
|
||||
muteLinkItem.className = 'mutelink';
|
||||
}
|
||||
else {
|
||||
muteLinkItem.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
|
||||
muteLinkItem.className = 'mutelink disabled';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
muteLinkItem.onclick = function(){
|
||||
if ($(this).attr('disabled') != undefined) {
|
||||
event.preventDefault();
|
||||
}
|
||||
var isMute = self.isMuted == true;
|
||||
APP.xmpp.setMute(self.peerJid, !isMute);
|
||||
|
||||
popupmenuElement.setAttribute('style', 'display:none;');
|
||||
|
||||
if (isMute) {
|
||||
this.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
|
||||
this.className = 'mutelink disabled';
|
||||
if (!this.isMuted) {
|
||||
muteLinkItem.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' " +
|
||||
"data-i18n='videothumbnail.domute'></div>";
|
||||
muteLinkItem.className = 'mutelink';
|
||||
}
|
||||
else {
|
||||
this.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
|
||||
this.className = 'mutelink';
|
||||
muteLinkItem.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' " +
|
||||
"data-i18n='videothumbnail.muted'></div>";
|
||||
muteLinkItem.className = 'mutelink disabled';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
muteLinkItem.onclick = function(){
|
||||
if ($(this).attr('disabled') != undefined) {
|
||||
event.preventDefault();
|
||||
}
|
||||
var isMute = self.isMuted == true;
|
||||
APP.xmpp.setMute(self.peerJid, !isMute);
|
||||
|
||||
popupmenuElement.setAttribute('style', 'display:none;');
|
||||
|
||||
if (isMute) {
|
||||
this.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' " +
|
||||
"data-i18n='videothumbnail.muted'></div>";
|
||||
this.className = 'mutelink disabled';
|
||||
}
|
||||
else {
|
||||
this.innerHTML = mutedIndicator +
|
||||
" <div style='width: 90px;margin-left: 20px;' " +
|
||||
"data-i18n='videothumbnail.domute'></div>";
|
||||
this.className = 'mutelink';
|
||||
}
|
||||
};
|
||||
|
||||
muteMenuItem.appendChild(muteLinkItem);
|
||||
popupmenuElement.appendChild(muteMenuItem);
|
||||
|
||||
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
|
||||
|
||||
var ejectMenuItem = document.createElement('li');
|
||||
var ejectLinkItem = document.createElement('a');
|
||||
var ejectText = "<div style='width: 90px;margin-left: 20px;' " +
|
||||
"data-i18n='videothumbnail.kick'> </div>";
|
||||
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
|
||||
ejectLinkItem.onclick = function(){
|
||||
APP.xmpp.eject(self.peerJid);
|
||||
popupmenuElement.setAttribute('style', 'display:none;');
|
||||
};
|
||||
|
||||
ejectMenuItem.appendChild(ejectLinkItem);
|
||||
popupmenuElement.appendChild(ejectMenuItem);
|
||||
|
||||
var paddingSpan = document.createElement('span');
|
||||
paddingSpan.className = 'popupmenuPadding';
|
||||
popupmenuElement.appendChild(paddingSpan);
|
||||
APP.translation.translateElement(
|
||||
$("#" + popupmenuElement.id + " > li > a > div"));
|
||||
};
|
||||
|
||||
muteMenuItem.appendChild(muteLinkItem);
|
||||
popupmenuElement.appendChild(muteMenuItem);
|
||||
|
||||
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
|
||||
|
||||
var ejectMenuItem = document.createElement('li');
|
||||
var ejectLinkItem = document.createElement('a');
|
||||
var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'> </div>";
|
||||
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
|
||||
ejectLinkItem.onclick = function(){
|
||||
APP.xmpp.eject(self.peerJid);
|
||||
popupmenuElement.setAttribute('style', 'display:none;');
|
||||
};
|
||||
|
||||
ejectMenuItem.appendChild(ejectLinkItem);
|
||||
popupmenuElement.appendChild(ejectMenuItem);
|
||||
|
||||
var paddingSpan = document.createElement('span');
|
||||
paddingSpan.className = 'popupmenuPadding';
|
||||
popupmenuElement.appendChild(paddingSpan);
|
||||
APP.translation.translateElement($("#" + popupmenuElement.id + " > li > a > div"));
|
||||
} else {
|
||||
RemoteVideo.prototype.addRemoteVideoMenu = function() {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the remote stream element corresponding to the given stream and
|
||||
* parent container.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param isVideo <tt>true</tt> if given <tt>stream</tt> is a video one.
|
||||
* @param container
|
||||
*/
|
||||
RemoteVideo.prototype.removeRemoteStreamElement = function (stream, isVideo, id) {
|
||||
RemoteVideo.prototype.removeRemoteStreamElement =
|
||||
function (stream, isVideo, id) {
|
||||
if (!this.container)
|
||||
return false;
|
||||
|
||||
@@ -141,31 +154,27 @@ RemoteVideo.prototype.removeRemoteStreamElement = function (stream, isVideo, id)
|
||||
else
|
||||
select = $('#' + this.videoSpanId + '>audio');
|
||||
|
||||
|
||||
// Mark video as removed to cancel waiting loop(if video is removed
|
||||
// before has started)
|
||||
select.removed = true;
|
||||
select.remove();
|
||||
|
||||
var audioCount = $('#' + this.videoSpanId + '>audio').length;
|
||||
var videoCount = $('#' + this.videoSpanId + '>' + APP.RTC.getVideoElementName()).length;
|
||||
|
||||
if (!audioCount && !videoCount) {
|
||||
console.log("Remove whole user", this.videoSpanId);
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.remove();
|
||||
// Remove whole container
|
||||
if (this.container.parentNode)
|
||||
this.container.parentNode.removeChild(this.container);
|
||||
|
||||
this.VideoLayout.resizeThumbnails();
|
||||
}
|
||||
console.info((isVideo ? "Video" : "Audio") +
|
||||
" removed " + this.getResourceJid(), select);
|
||||
|
||||
if (isVideo)
|
||||
this.VideoLayout.updateRemovedVideo(this.resourceJid);
|
||||
this.VideoLayout.updateRemovedVideo(this.getResourceJid());
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.waitForPlayback = function (stream) {
|
||||
/**
|
||||
* Removes RemoteVideo from the page.
|
||||
*/
|
||||
RemoteVideo.prototype.remove = function () {
|
||||
console.log("Remove thumbnail", this.peerJid);
|
||||
this.removeConnectionIndicator();
|
||||
// Remove whole container
|
||||
if (this.container.parentNode)
|
||||
this.container.parentNode.removeChild(this.container);
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.waitForPlayback = function (sel, stream) {
|
||||
|
||||
var isVideo = stream.getVideoTracks().length > 0;
|
||||
if (!isVideo || stream.id === 'mixedmslabel') {
|
||||
@@ -173,7 +182,7 @@ RemoteVideo.prototype.waitForPlayback = function (stream) {
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var sel = this.VideoLayout.getPeerVideoSel(this.resourceJid);
|
||||
var resourceJid = this.getResourceJid();
|
||||
|
||||
// Register 'onplaying' listener to trigger 'videoactive' on VideoLayout
|
||||
// when video playback starts
|
||||
@@ -183,9 +192,9 @@ RemoteVideo.prototype.waitForPlayback = function (stream) {
|
||||
APP.RTC.attachMediaStream(sel, stream);
|
||||
}
|
||||
if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||
sel = $('#' + newElementId);
|
||||
sel = self.selectVideoElement();
|
||||
}
|
||||
self.VideoLayout.videoactive(sel, self.resourceJid);
|
||||
self.VideoLayout.videoactive(sel, resourceJid);
|
||||
sel[0].onplaying = null;
|
||||
if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||
// 'currentTime' is used to check if the video has started
|
||||
@@ -205,14 +214,15 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
|
||||
var streamElement = SmallVideo.createStreamElement(sid, stream);
|
||||
var newElementId = streamElement.id;
|
||||
|
||||
this.container.appendChild(streamElement);
|
||||
// Put new stream element always in front
|
||||
UIUtils.prependChild(this.container, streamElement);
|
||||
|
||||
var sel = $('#' + newElementId);
|
||||
sel.hide();
|
||||
|
||||
// If the container is currently visible we attach the stream.
|
||||
if (!isVideo || (this.container.offsetParent !== null && isVideo)) {
|
||||
this.waitForPlayback(stream);
|
||||
this.waitForPlayback(sel, stream);
|
||||
|
||||
APP.RTC.attachMediaStream(sel, stream);
|
||||
}
|
||||
@@ -224,21 +234,11 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
|
||||
|
||||
};
|
||||
|
||||
// Name of video element name is different for IE/Safari
|
||||
var videoElem = APP.RTC.getVideoElementName();
|
||||
|
||||
// Add click handler.
|
||||
var onClickHandler = function (event) {
|
||||
/*
|
||||
* FIXME It turns out that videoThumb may not exist (if there is
|
||||
* no actual video).
|
||||
*/
|
||||
var videoThumb = $('#' + self.videoSpanId + '>' + videoElem).get(0);
|
||||
if (videoThumb) {
|
||||
self.VideoLayout.handleVideoThumbClicked(
|
||||
false,
|
||||
self.resourceJid);
|
||||
}
|
||||
|
||||
self.VideoLayout.handleVideoThumbClicked(false, self.getResourceJid());
|
||||
|
||||
// On IE we need to populate this handler on video <object>
|
||||
// and it does not give event instance as an argument,
|
||||
// so we check here for methods.
|
||||
@@ -253,27 +253,6 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
|
||||
if (RTCBrowserType.isTemasysPluginUsed())
|
||||
sel = $('#' + newElementId);
|
||||
sel[0].onclick = onClickHandler;
|
||||
|
||||
//FIXME
|
||||
// Add hover handler
|
||||
$(this.container).hover(
|
||||
function() {
|
||||
self.showDisplayName(true);
|
||||
},
|
||||
function() {
|
||||
var videoSrc = null;
|
||||
var videoSelector = $('#' + self.videoSpanId + '>' + videoElem);
|
||||
if (videoSelector && videoSelector.length > 0) {
|
||||
videoSrc = APP.RTC.getVideoSrc(videoSelector.get(0));
|
||||
}
|
||||
|
||||
// If the video has been "pinned" by the user we want to
|
||||
// keep the display name on place.
|
||||
if (!LargeVideo.isLargeVideoVisible()
|
||||
|| videoSrc !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -313,14 +292,14 @@ RemoteVideo.prototype.showPeerContainer = function (state) {
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.removeConnectionIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
if (this.connectionIndicator)
|
||||
this.connectionIndicator.remove();
|
||||
}
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
if (this.connectionIndicator)
|
||||
this.connectionIndicator.hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the remote video menu.
|
||||
@@ -330,9 +309,7 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||
*/
|
||||
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
||||
var muteMenuItem
|
||||
= $('#remote_popupmenu_'
|
||||
+ this.resourceJid
|
||||
+ '>li>a.mutelink');
|
||||
= $('#remote_popupmenu_' + this.getResourceJid() + '>li>a.mutelink');
|
||||
|
||||
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
|
||||
|
||||
@@ -348,7 +325,7 @@ RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
||||
muteLink.className = 'mutelink';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
@@ -356,8 +333,8 @@ RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
||||
RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
if (!this.container) {
|
||||
console.warn(
|
||||
"Unable to set displayName - " + this.videoSpanId + " does not exist");
|
||||
console.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -365,12 +342,10 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
if (displayName && displayName.length > 0) {
|
||||
$('#' + this.videoSpanId + '_name').html(displayName);
|
||||
}
|
||||
else if (key && key.length > 0)
|
||||
{
|
||||
else if (key && key.length > 0) {
|
||||
var nameHtml = APP.translation.generateTranslationHTML(key);
|
||||
$('#' + this.videoSpanId + '_name').html(nameHtml);
|
||||
}
|
||||
@@ -384,17 +359,14 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
|
||||
nameSpan.innerText = displayName;
|
||||
}
|
||||
else
|
||||
nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
|
||||
|
||||
nameSpan.id = this.videoSpanId + '_name';
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes remote video menu element from video element identified by
|
||||
@@ -407,7 +379,14 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||
if (menuSpan.length) {
|
||||
menuSpan.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.getResourceJid = function () {
|
||||
if (!this.resourceJid) {
|
||||
console.error("Undefined resource jid");
|
||||
}
|
||||
return this.resourceJid;
|
||||
};
|
||||
|
||||
RemoteVideo.createContainer = function (spanId) {
|
||||
var container = document.createElement('span');
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* global $, APP, require */
|
||||
var Avatar = require("../avatar/Avatar");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||
|
||||
function SmallVideo(){
|
||||
function SmallVideo() {
|
||||
this.isMuted = false;
|
||||
this.hasAvatar = false;
|
||||
}
|
||||
@@ -29,33 +31,48 @@ SmallVideo.prototype.setDeviceAvailabilityIcons = function (devices) {
|
||||
if(!this.container)
|
||||
return;
|
||||
|
||||
$("#" + this.videoSpanId + " > .noMic").remove();
|
||||
$("#" + this.videoSpanId + " > .noVideo").remove();
|
||||
if(!devices.audio)
|
||||
{
|
||||
var noMic = $("#" + this.videoSpanId + " > .noMic");
|
||||
var noVideo = $("#" + this.videoSpanId + " > .noVideo");
|
||||
|
||||
noMic.remove();
|
||||
noVideo.remove();
|
||||
if (!devices.audio) {
|
||||
this.container.appendChild(
|
||||
document.createElement("div")).setAttribute("class","noMic");
|
||||
document.createElement("div")).setAttribute("class", "noMic");
|
||||
}
|
||||
|
||||
if(!devices.video)
|
||||
{
|
||||
if (!devices.video) {
|
||||
this.container.appendChild(
|
||||
document.createElement("div")).setAttribute("class","noVideo");
|
||||
document.createElement("div")).setAttribute("class", "noVideo");
|
||||
}
|
||||
|
||||
if(!devices.audio && !devices.video)
|
||||
{
|
||||
$("#" + this.videoSpanId + " > .noMic").css("background-position", "75%");
|
||||
$("#" + this.videoSpanId + " > .noVideo").css("background-position", "25%");
|
||||
$("#" + this.videoSpanId + " > .noVideo").css("background-color", "transparent");
|
||||
if (!devices.audio && !devices.video) {
|
||||
noMic.css("background-position", "75%");
|
||||
noVideo.css("background-position", "25%");
|
||||
noVideo.css("background-color", "transparent");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the type of the video displayed by this instance.
|
||||
* @param videoType 'camera' or 'screen'
|
||||
*/
|
||||
SmallVideo.prototype.setVideoType = function (videoType) {
|
||||
this.videoType = videoType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the type of the video displayed by this instance.
|
||||
* @returns {String} 'camera', 'screen' or undefined.
|
||||
*/
|
||||
SmallVideo.prototype.getVideoType = function () {
|
||||
return this.videoType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the presence status message for the given video.
|
||||
*/
|
||||
SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
|
||||
|
||||
if (!this.container) {
|
||||
// No container
|
||||
return;
|
||||
@@ -86,15 +103,13 @@ SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
|
||||
/**
|
||||
* Creates an audio or video stream element.
|
||||
*/
|
||||
|
||||
SmallVideo.createStreamElement = function (sid, stream) {
|
||||
var isVideo = stream.getVideoTracks().length > 0;
|
||||
|
||||
var element = isVideo
|
||||
? document.createElement('video')
|
||||
var element = isVideo ? document.createElement('video')
|
||||
: document.createElement('audio');
|
||||
var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_')
|
||||
+ sid + '_' + APP.RTC.getStreamID(stream);
|
||||
var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_') + sid + '_' +
|
||||
APP.RTC.getStreamID(stream);
|
||||
|
||||
element.id = id;
|
||||
if (!RTCBrowserType.isIExplorer()) {
|
||||
@@ -105,6 +120,26 @@ SmallVideo.createStreamElement = function (sid, stream) {
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures hoverIn/hoverOut handlers.
|
||||
*/
|
||||
SmallVideo.prototype.bindHoverHandler = function () {
|
||||
// Add hover handler
|
||||
var self = this;
|
||||
$(this.container).hover(
|
||||
function () {
|
||||
self.showDisplayName(true);
|
||||
},
|
||||
function () {
|
||||
// If the video has been "pinned" by the user we want to
|
||||
// keep the display name on place.
|
||||
if (!LargeVideo.isLargeVideoVisible() ||
|
||||
!LargeVideo.isCurrentlyOnLarge(self.getResourceJid()))
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the data for the indicator
|
||||
* @param id the id of the indicator
|
||||
@@ -114,12 +149,12 @@ SmallVideo.createStreamElement = function (sid, stream) {
|
||||
SmallVideo.prototype.updateStatsIndicator = function (percent, object) {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.updateConnectionQuality(percent, object);
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.hideIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.hideIndicator();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -190,16 +225,16 @@ SmallVideo.prototype.showVideoIndicator = function(isMuted) {
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
|
||||
{
|
||||
var displayName = this.resourceJid;
|
||||
SmallVideo.prototype.enableDominantSpeaker = function (isEnable) {
|
||||
var resourceJid = this.getResourceJid();
|
||||
var displayName = resourceJid;
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
if (nameSpan.length > 0)
|
||||
displayName = nameSpan.html();
|
||||
|
||||
console.log("UI enable dominant speaker",
|
||||
displayName,
|
||||
this.resourceJid,
|
||||
resourceJid,
|
||||
isEnable);
|
||||
|
||||
|
||||
@@ -207,21 +242,17 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
|
||||
return;
|
||||
}
|
||||
|
||||
var video = $('#' + this.videoSpanId + '>' + APP.RTC.getVideoElementName());
|
||||
if (isEnable) {
|
||||
this.showDisplayName(LargeVideo.getState() === "video");
|
||||
|
||||
if (video && video.length > 0) {
|
||||
if (isEnable) {
|
||||
this.showDisplayName(LargeVideo.isLargeVideoOnTop());
|
||||
if (!this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.add("dominantspeaker");
|
||||
}
|
||||
else {
|
||||
this.showDisplayName(false);
|
||||
|
||||
if (!this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.add("dominantspeaker");
|
||||
}
|
||||
else {
|
||||
this.showDisplayName(false);
|
||||
|
||||
if (this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.remove("dominantspeaker");
|
||||
}
|
||||
if (this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.remove("dominantspeaker");
|
||||
}
|
||||
|
||||
this.showAvatar();
|
||||
@@ -231,18 +262,15 @@ SmallVideo.prototype.updateIconPositions = function () {
|
||||
var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted');
|
||||
var connectionIndicator = $('#' + this.videoSpanId + '>div.connectionindicator');
|
||||
var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted');
|
||||
if(connectionIndicator.length > 0
|
||||
&& connectionIndicator[0].style.display != "none") {
|
||||
if(connectionIndicator.length > 0 &&
|
||||
connectionIndicator[0].style.display != "none") {
|
||||
audioMutedSpan.css({right: "23px"});
|
||||
videoMutedSpan.css({right: ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"});
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
audioMutedSpan.css({right: "0px"});
|
||||
videoMutedSpan.css({right: (audioMutedSpan.length > 0? 30 : 0) + "px"});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the element indicating the moderator(owner) of the conference.
|
||||
@@ -274,30 +302,35 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
|
||||
|
||||
//translates text in focus indicators
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + ' .focusindicator'));
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.selectVideoElement = function () {
|
||||
var videoElem = APP.RTC.getVideoElementName();
|
||||
if (!RTCBrowserType.isTemasysPluginUsed()) {
|
||||
return $('#' + this.videoSpanId).find(videoElem);
|
||||
} else {
|
||||
return $('#' + this.videoSpanId +
|
||||
(this.isLocal ? '>>' : '>') +
|
||||
videoElem + '>param[value="video"]').parent();
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.getSrc = function () {
|
||||
var videoElement = APP.RTC.getVideoElementName();
|
||||
return APP.RTC.getVideoSrc(
|
||||
$('#' + this.videoSpanId).find(videoElement).get(0));
|
||||
},
|
||||
var videoElement = this.selectVideoElement().get(0);
|
||||
return APP.RTC.getVideoSrc(videoElement);
|
||||
};
|
||||
|
||||
SmallVideo.prototype.focus = function(isFocused)
|
||||
{
|
||||
SmallVideo.prototype.focus = function(isFocused) {
|
||||
if(!isFocused) {
|
||||
this.container.classList.remove("videoContainerFocused");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.container.classList.add("videoContainerFocused");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.hasVideo = function () {
|
||||
return $("#" + this.videoSpanId).find(
|
||||
APP.RTC.getVideoElementName()).length !== 0;
|
||||
}
|
||||
return this.selectVideoElement().length !== 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides or shows the user's avatar
|
||||
@@ -305,27 +338,33 @@ SmallVideo.prototype.hasVideo = function () {
|
||||
* video because there is no dominant speaker and no focused speaker
|
||||
*/
|
||||
SmallVideo.prototype.showAvatar = function (show) {
|
||||
if (!this.hasAvatar)
|
||||
return;
|
||||
if (!this.hasAvatar) {
|
||||
if (this.peerJid) {
|
||||
// Init avatar
|
||||
this.avatarChanged(Avatar.getThumbUrl(this.peerJid));
|
||||
} else {
|
||||
console.error("Unable to init avatar - no peerjid", this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var videoElem = APP.RTC.getVideoElementName();
|
||||
var video = $('#' + this.videoSpanId).find(videoElem);
|
||||
var avatar = $('#avatar_' + this.resourceJid);
|
||||
var resourceJid = this.getResourceJid();
|
||||
var video = this.selectVideoElement();
|
||||
|
||||
var avatar = $('#avatar_' + resourceJid);
|
||||
|
||||
if (show === undefined || show === null) {
|
||||
if (!this.isLocal &&
|
||||
!this.VideoLayout.isInLastN(this.resourceJid)) {
|
||||
!this.VideoLayout.isInLastN(resourceJid)) {
|
||||
show = true;
|
||||
} else {
|
||||
// We want to show the avatar when the video is muted or not exists
|
||||
// that is when 'true' or 'null' is returned
|
||||
show = APP.RTC.isVideoMuted(this.peerJid) !== false;
|
||||
}
|
||||
else
|
||||
{
|
||||
show = APP.RTC.isVideoMuted(this.peerJid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (LargeVideo.showAvatar(this.resourceJid, show))
|
||||
{
|
||||
if (LargeVideo.showAvatar(resourceJid, show)) {
|
||||
setVisibility(avatar, false);
|
||||
setVisibility(video, false);
|
||||
} else {
|
||||
@@ -333,13 +372,13 @@ SmallVideo.prototype.showAvatar = function (show) {
|
||||
setVisibility(video, !show);
|
||||
}
|
||||
setVisibility(avatar, show);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
||||
var thumbnail = $('#' + this.videoSpanId);
|
||||
var avatar = $('#avatar_' + this.resourceJid);
|
||||
var resourceJid = this.getResourceJid();
|
||||
var avatar = $('#avatar_' + resourceJid);
|
||||
this.hasAvatar = true;
|
||||
|
||||
// set the avatar in the thumbnail
|
||||
@@ -348,12 +387,12 @@ SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
||||
} else {
|
||||
if (thumbnail && thumbnail.length > 0) {
|
||||
avatar = document.createElement('img');
|
||||
avatar.id = 'avatar_' + this.resourceJid;
|
||||
avatar.id = 'avatar_' + resourceJid;
|
||||
avatar.className = 'userAvatar';
|
||||
avatar.src = thumbUrl;
|
||||
thumbnail.append(avatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = SmallVideo;
|
||||
@@ -1,8 +1,9 @@
|
||||
/* global config, APP, $, Strophe, require, interfaceConfig */
|
||||
var AudioLevels = require("../audio_levels/AudioLevels");
|
||||
var Avatar = require("../avatar/Avatar");
|
||||
var ContactList = require("../side_pannels/contactlist/ContactList");
|
||||
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
||||
var UIEvents = require("../../../service/UI/UIEvents");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
|
||||
var RTC = require("../../RTC/RTC");
|
||||
var RTCBrowserType = require('../../RTC/RTCBrowserType');
|
||||
@@ -11,12 +12,11 @@ var RemoteVideo = require("./RemoteVideo");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
var LocalVideo = require("./LocalVideo");
|
||||
|
||||
|
||||
var remoteVideos = {};
|
||||
var remoteVideoTypes = {};
|
||||
var localVideoThumbnail = null;
|
||||
|
||||
|
||||
|
||||
|
||||
var currentDominantSpeaker = null;
|
||||
var lastNCount = config.channelLastN;
|
||||
var localLastNCount = config.channelLastN;
|
||||
@@ -26,7 +26,6 @@ var lastNPickupJid = null;
|
||||
|
||||
var eventEmitter = null;
|
||||
|
||||
|
||||
/**
|
||||
* Currently focused video jid
|
||||
* @type {String}
|
||||
@@ -37,14 +36,22 @@ var VideoLayout = (function (my) {
|
||||
my.init = function (emitter) {
|
||||
eventEmitter = emitter;
|
||||
localVideoThumbnail = new LocalVideo(VideoLayout);
|
||||
if (interfaceConfig.filmStripOnly) {
|
||||
LargeVideo.disable();
|
||||
} else {
|
||||
LargeVideo.init(VideoLayout, emitter);
|
||||
}
|
||||
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
LargeVideo.init(VideoLayout, emitter);
|
||||
|
||||
};
|
||||
|
||||
my.isInLastN = function(resource) {
|
||||
return lastNCount < 0 // lastN is disabled, return true
|
||||
|| (lastNCount > 0 && lastNEndpointsCache.length == 0) // lastNEndpoints cache not built yet, return true
|
||||
|| (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
|
||||
return lastNCount < 0 || // lastN is disabled
|
||||
// lastNEndpoints cache not built yet
|
||||
(lastNCount > 0 && lastNEndpointsCache.length == 0) ||
|
||||
(lastNEndpointsCache &&
|
||||
lastNEndpointsCache.indexOf(resource) !== -1);
|
||||
};
|
||||
|
||||
my.changeLocalStream = function (stream, isMuted) {
|
||||
@@ -61,6 +68,16 @@ var VideoLayout = (function (my) {
|
||||
localAudio.autoplay = true;
|
||||
localAudio.volume = 0;
|
||||
}
|
||||
// Now when Temasys plugin is converting also <audio> elements to
|
||||
// plugin's <object>s, in current layout it will capture click events
|
||||
// before it reaches the local video object. We hide it here in order
|
||||
// to prevent that.
|
||||
if (RTCBrowserType.isIExplorer()) {
|
||||
// The issue is not present on Safari. Also if we hide it in Safari
|
||||
// then the local audio track will have 'enabled' flag set to false
|
||||
// which will result in audio mute issues
|
||||
$('#localAudio').hide();
|
||||
}
|
||||
};
|
||||
|
||||
my.changeLocalVideo = function(stream, isMuted) {
|
||||
@@ -68,15 +85,16 @@ var VideoLayout = (function (my) {
|
||||
localVideoThumbnail.setDisplayName();
|
||||
localVideoThumbnail.createConnectionIndicator();
|
||||
|
||||
this.onVideoTypeChanged(APP.xmpp.myResource(), stream.videoType);
|
||||
|
||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||
|
||||
localVideoThumbnail.changeVideo(stream, isMuted);
|
||||
|
||||
LargeVideo.updateLargeVideo(
|
||||
APP.xmpp.myResource(),
|
||||
/* force update only before conference starts */
|
||||
!APP.xmpp.isConferenceInProgress());
|
||||
|
||||
/* force update if we're currently being displayed */
|
||||
if (LargeVideo.isCurrentlyOnLarge(APP.xmpp.myResource())) {
|
||||
LargeVideo.updateLargeVideo(APP.xmpp.myResource(), true);
|
||||
}
|
||||
};
|
||||
|
||||
my.mucJoined = function () {
|
||||
@@ -96,18 +114,13 @@ var VideoLayout = (function (my) {
|
||||
if(!devices)
|
||||
return;
|
||||
|
||||
if(!resourceJid)
|
||||
{
|
||||
if(!resourceJid) {
|
||||
localVideoThumbnail.setDeviceAvailabilityIcons(devices);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(remoteVideos[resourceJid])
|
||||
remoteVideos[resourceJid].setDeviceAvailabilityIcons(devices);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if removed video is currently displayed and tries to display
|
||||
@@ -116,55 +129,44 @@ var VideoLayout = (function (my) {
|
||||
*/
|
||||
my.updateRemovedVideo = function(resourceJid) {
|
||||
|
||||
var videoElem = RTC.getVideoElementName();
|
||||
|
||||
if (resourceJid === LargeVideo.getResourceJid()) {
|
||||
// this is currently displayed as large
|
||||
// pick the last visible video in the row
|
||||
// if nobody else is left, this picks the local video
|
||||
var pick
|
||||
= $('#remoteVideos>' +
|
||||
'span[id!="mixedstream"]:visible:last>' + videoElem).get(0);
|
||||
|
||||
if (!pick) {
|
||||
console.info("Last visible video no longer exists");
|
||||
pick = $('#remoteVideos>' +
|
||||
'span[id!="mixedstream"]>' + videoElem).get(0);
|
||||
|
||||
if (!pick || !APP.RTC.getVideoSrc(pick)) {
|
||||
// Try local video
|
||||
console.info("Fallback to local video...");
|
||||
pick = $('#remoteVideos>span>span>' + videoElem).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
// mute if localvideo
|
||||
if (pick) {
|
||||
var container = pick.parentNode;
|
||||
var newResourceJid;
|
||||
// We'll show user's avatar if he is the dominant speaker or if
|
||||
// his video thumbnail is pinned
|
||||
if (remoteVideos[resourceJid] &&
|
||||
resourceJid === focusedVideoResourceJid ||
|
||||
resourceJid === currentDominantSpeaker) {
|
||||
newResourceJid = resourceJid;
|
||||
} else {
|
||||
console.warn("Failed to elect large video");
|
||||
container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
|
||||
|
||||
// Otherwise select last visible video
|
||||
newResourceJid = this.electLastVisibleVideo();
|
||||
}
|
||||
|
||||
var jid = null;
|
||||
if(container)
|
||||
{
|
||||
if(container.id == "localVideoWrapper")
|
||||
{
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = VideoLayout.getPeerContainerResourceJid(container);
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
LargeVideo.updateLargeVideo(jid);
|
||||
LargeVideo.updateLargeVideo(newResourceJid);
|
||||
}
|
||||
};
|
||||
|
||||
my.electLastVisibleVideo = function () {
|
||||
// pick the last visible video in the row
|
||||
// if nobody else is left, this picks the local video
|
||||
var jid;
|
||||
var pick = $('#remoteVideos>span[id!="mixedstream"]:visible:last');
|
||||
if (pick.length) {
|
||||
jid = VideoLayout.getPeerContainerResourceJid(pick[0]);
|
||||
} else {
|
||||
console.info("Last visible video no longer exists");
|
||||
pick = $('#remoteVideos>span[id!="mixedstream"]');
|
||||
if (pick.length) {
|
||||
jid = VideoLayout.getPeerContainerResourceJid(pick[0]);
|
||||
}
|
||||
if (!jid) {
|
||||
// Go with local video
|
||||
console.info("Fallback to local video...");
|
||||
jid = APP.xmpp.myResource();
|
||||
}
|
||||
}
|
||||
console.info("electLastVisibleVideo: " + jid);
|
||||
return jid;
|
||||
};
|
||||
|
||||
my.onRemoteStreamAdded = function (stream) {
|
||||
if (stream.peerjid) {
|
||||
@@ -178,15 +180,35 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
};
|
||||
|
||||
my.getLargeVideoJid = function () {
|
||||
my.getLargeVideoResource = function () {
|
||||
return LargeVideo.getResourceJid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when large video update is finished
|
||||
* @param currentSmallVideo small video currently displayed on large video
|
||||
*/
|
||||
my.largeVideoUpdated = function (currentSmallVideo) {
|
||||
// Makes sure that dominant speaker UI
|
||||
// is enabled only on current small video
|
||||
localVideoThumbnail.enableDominantSpeaker(
|
||||
localVideoThumbnail === currentSmallVideo);
|
||||
Object.keys(remoteVideos).forEach(
|
||||
function (resourceJid) {
|
||||
var remoteVideo = remoteVideos[resourceJid];
|
||||
if (remoteVideo) {
|
||||
remoteVideo.enableDominantSpeaker(
|
||||
remoteVideo === currentSmallVideo);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
my.handleVideoThumbClicked = function(noPinnedEndpointChangedEvent,
|
||||
resourceJid) {
|
||||
if(focusedVideoResourceJid) {
|
||||
var oldSmallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
|
||||
if(oldSmallVideo)
|
||||
if (oldSmallVideo && !interfaceConfig.filmStripOnly)
|
||||
oldSmallVideo.focus(false);
|
||||
}
|
||||
|
||||
@@ -197,8 +219,7 @@ var VideoLayout = (function (my) {
|
||||
focusedVideoResourceJid = null;
|
||||
// Enable the currently set dominant speaker.
|
||||
if (currentDominantSpeaker) {
|
||||
if(smallVideo && smallVideo.hasVideo())
|
||||
{
|
||||
if(smallVideo && smallVideo.hasVideo()) {
|
||||
LargeVideo.updateLargeVideo(currentDominantSpeaker);
|
||||
}
|
||||
}
|
||||
@@ -209,14 +230,12 @@ var VideoLayout = (function (my) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Lock new video
|
||||
focusedVideoResourceJid = resourceJid;
|
||||
|
||||
// Update focused/pinned interface.
|
||||
if (resourceJid)
|
||||
{
|
||||
if(smallVideo)
|
||||
if (resourceJid) {
|
||||
if (smallVideo && !interfaceConfig.filmStripOnly)
|
||||
smallVideo.focus(true);
|
||||
|
||||
if (!noPinnedEndpointChangedEvent) {
|
||||
@@ -224,14 +243,7 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
}
|
||||
|
||||
if (LargeVideo.getResourceJid() === resourceJid &&
|
||||
LargeVideo.isLargeVideoOnTop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Triggers a "video.selected" event. The "false" parameter indicates
|
||||
// this isn't a prezi.
|
||||
$(document).trigger("video.selected", [false]);
|
||||
LargeVideo.setState("video");
|
||||
|
||||
LargeVideo.updateLargeVideo(resourceJid);
|
||||
|
||||
@@ -250,26 +262,30 @@ var VideoLayout = (function (my) {
|
||||
* in the document and creates it eventually.
|
||||
*
|
||||
* @param peerJid peer Jid to check.
|
||||
* @param userId user email or id for setting the avatar
|
||||
*
|
||||
* @return Returns <tt>true</tt> if the peer container exists,
|
||||
* <tt>false</tt> - otherwise
|
||||
*/
|
||||
my.ensurePeerContainerExists = function(peerJid, userId) {
|
||||
ContactList.ensureAddContact(peerJid, userId);
|
||||
my.ensurePeerContainerExists = function(peerJid) {
|
||||
ContactList.ensureAddContact(peerJid);
|
||||
|
||||
var resourceJid = Strophe.getResourceFromJid(peerJid);
|
||||
|
||||
if(!remoteVideos[resourceJid])
|
||||
{
|
||||
remoteVideos[resourceJid] = new RemoteVideo(peerJid, VideoLayout);
|
||||
Avatar.setUserAvatar(peerJid, userId);
|
||||
if (!remoteVideos[resourceJid]) {
|
||||
|
||||
var remoteVideo = new RemoteVideo(peerJid, VideoLayout);
|
||||
remoteVideos[resourceJid] = remoteVideo;
|
||||
|
||||
var videoType = remoteVideoTypes[resourceJid];
|
||||
if (videoType) {
|
||||
remoteVideo.setVideoType(videoType);
|
||||
}
|
||||
|
||||
// In case this is not currently in the last n we don't show it.
|
||||
if (localLastNCount
|
||||
&& localLastNCount > 0
|
||||
&& $('#remoteVideos>span').length >= localLastNCount + 2) {
|
||||
remoteVideos[resourceJid].showPeerContainer('hide');
|
||||
if (localLastNCount &&
|
||||
localLastNCount > 0 &&
|
||||
$('#remoteVideos>span').length >= localLastNCount + 2) {
|
||||
remoteVideo.showPeerContainer('hide');
|
||||
}
|
||||
else
|
||||
VideoLayout.resizeThumbnails();
|
||||
@@ -282,6 +298,9 @@ var VideoLayout = (function (my) {
|
||||
};
|
||||
|
||||
my.videoactive = function (videoelem, resourceJid) {
|
||||
|
||||
console.info(resourceJid + " video is now active");
|
||||
|
||||
videoelem.show();
|
||||
VideoLayout.resizeThumbnails();
|
||||
|
||||
@@ -291,11 +310,12 @@ var VideoLayout = (function (my) {
|
||||
if ((!focusedVideoResourceJid &&
|
||||
!currentDominantSpeaker &&
|
||||
!require("../prezi/Prezi").isPresentationVisible()) ||
|
||||
focusedVideoResourceJid === resourceJid ||
|
||||
(resourceJid &&
|
||||
currentDominantSpeaker === resourceJid)) {
|
||||
LargeVideo.updateLargeVideo(resourceJid, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the presence status message for the given video.
|
||||
@@ -324,13 +344,10 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
|
||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||
|
||||
var member = members[jid];
|
||||
|
||||
if (member.role === 'moderator') {
|
||||
|
||||
remoteVideos[resourceJid].removeRemoteVideoMenu();
|
||||
|
||||
remoteVideos[resourceJid].createModeratorIndicatorElement();
|
||||
} else if (isModerator) {
|
||||
// We are moderator, but user is not - add menu
|
||||
@@ -353,7 +370,11 @@ var VideoLayout = (function (my) {
|
||||
* Resizes the large video container.
|
||||
*/
|
||||
my.resizeLargeVideoContainer = function () {
|
||||
LargeVideo.resize();
|
||||
if(LargeVideo.isEnabled()) {
|
||||
LargeVideo.resize();
|
||||
} else {
|
||||
VideoLayout.resizeVideoSpace();
|
||||
}
|
||||
VideoLayout.resizeThumbnails();
|
||||
LargeVideo.position();
|
||||
};
|
||||
@@ -370,10 +391,9 @@ var VideoLayout = (function (my) {
|
||||
|
||||
$('.userAvatar').css('left', (width - height) / 2);
|
||||
|
||||
if(animate)
|
||||
{
|
||||
if(animate) {
|
||||
$('#remoteVideos').animate({
|
||||
height: height
|
||||
height: height + 2 // adds 2 px because of small video 1px border
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
@@ -395,16 +415,13 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// size videos so that while keeping AR and max height, we have a
|
||||
// nice fit
|
||||
$('#remoteVideos').height(height);
|
||||
$('#remoteVideos').height(height + 2);// adds 2 px because of small video 1px border
|
||||
$('#remoteVideos>span').width(width);
|
||||
$('#remoteVideos>span').height(height);
|
||||
|
||||
|
||||
$(document).trigger("remotevideo.resized", [width, height]);
|
||||
}
|
||||
};
|
||||
@@ -434,7 +451,7 @@ var VideoLayout = (function (my) {
|
||||
var availableWidth = availableWinWidth / numvids;
|
||||
var aspectRatio = 16.0 / 9.0;
|
||||
var maxHeight = Math.min(160, availableHeight);
|
||||
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio);
|
||||
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio, window.innerHeight - 18);
|
||||
if (availableHeight < availableWidth / aspectRatio) {
|
||||
availableWidth = Math.floor(availableHeight * aspectRatio);
|
||||
}
|
||||
@@ -450,15 +467,14 @@ var VideoLayout = (function (my) {
|
||||
* DOM element
|
||||
*/
|
||||
my.getPeerContainerResourceJid = function (containerElement) {
|
||||
if (localVideoThumbnail.container === containerElement) {
|
||||
return localVideoThumbnail.getResourceJid();
|
||||
}
|
||||
|
||||
var i = containerElement.id.indexOf('participant_');
|
||||
|
||||
if (i >= 0)
|
||||
return containerElement.id.substring(i + 12);
|
||||
};
|
||||
|
||||
my.getPeerVideoSel = function (peerResourceJid) {
|
||||
return $('#participant_' + peerResourceJid +
|
||||
'>' + APP.RTC.getVideoElementName());
|
||||
return containerElement.id.substring(i + 12);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -469,15 +485,15 @@ var VideoLayout = (function (my) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jid == APP.xmpp.myJid()) {
|
||||
if (jid === APP.xmpp.myJid()) {
|
||||
$("#localVideoContainer").click();
|
||||
return;
|
||||
}
|
||||
|
||||
var resource = Strophe.getResourceFromJid(jid);
|
||||
var videoSel = VideoLayout.getPeerVideoSel(resource);
|
||||
if (videoSel.length > 0) {
|
||||
var videoThumb = videoSel[0];
|
||||
var remoteVideo = remoteVideos[resource];
|
||||
if (remoteVideo && remoteVideo.selectVideoElement().length) {
|
||||
var videoThumb = remoteVideo.selectVideoElement()[0];
|
||||
// It is not always the case that a videoThumb exists (if there is
|
||||
// no actual video).
|
||||
if (RTC.getVideoSrc(videoThumb)) {
|
||||
@@ -520,15 +536,14 @@ var VideoLayout = (function (my) {
|
||||
remoteVideos[resourceJid].updateRemoteVideoMenu(isMuted);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* On video muted event.
|
||||
*/
|
||||
my.onVideoMute = function (jid, value) {
|
||||
if(jid !== APP.xmpp.myJid() && !APP.RTC.muteRemoteVideoStream(jid, value))
|
||||
if (jid !== APP.xmpp.myJid() &&
|
||||
!APP.RTC.muteRemoteVideoStream(jid, value))
|
||||
return;
|
||||
|
||||
if (jid === APP.xmpp.myJid()) {
|
||||
@@ -537,9 +552,10 @@ var VideoLayout = (function (my) {
|
||||
var resource = Strophe.getResourceFromJid(jid);
|
||||
|
||||
VideoLayout.ensurePeerContainerExists(jid);
|
||||
remoteVideos[resource].showVideoIndicator(value);
|
||||
var remoteVideo = remoteVideos[resource];
|
||||
remoteVideo.showVideoIndicator(value);
|
||||
|
||||
var el = VideoLayout.getPeerVideoSel(resource);
|
||||
var el = remoteVideo.selectVideoElement();
|
||||
if (!value)
|
||||
el.show();
|
||||
else
|
||||
@@ -552,8 +568,8 @@ var VideoLayout = (function (my) {
|
||||
*/
|
||||
my.onDisplayNameChanged =
|
||||
function (jid, displayName, status) {
|
||||
if (jid === 'localVideoContainer'
|
||||
|| jid === APP.xmpp.myJid()) {
|
||||
if (jid === 'localVideoContainer' ||
|
||||
jid === APP.xmpp.myJid()) {
|
||||
localVideoThumbnail.setDisplayName(displayName);
|
||||
} else {
|
||||
VideoLayout.ensurePeerContainerExists(jid);
|
||||
@@ -561,7 +577,6 @@ var VideoLayout = (function (my) {
|
||||
displayName,
|
||||
status);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -572,18 +587,19 @@ var VideoLayout = (function (my) {
|
||||
if (resourceJid === APP.xmpp.myResource())
|
||||
return;
|
||||
|
||||
var remoteVideo = remoteVideos[resourceJid];
|
||||
var members = APP.xmpp.getMembers();
|
||||
// Update the current dominant speaker.
|
||||
if (resourceJid !== currentDominantSpeaker) {
|
||||
var currentJID = APP.xmpp.findJidFromResource(currentDominantSpeaker);
|
||||
var newJID = APP.xmpp.findJidFromResource(resourceJid);
|
||||
if(currentDominantSpeaker && (!members || !members[currentJID] ||
|
||||
!members[currentJID].displayName)) {
|
||||
remoteVideos[resourceJid].setDisplayName(null);
|
||||
if (currentDominantSpeaker && (!members || !members[currentJID] ||
|
||||
!members[currentJID].displayName) && remoteVideo) {
|
||||
remoteVideo.setDisplayName(null);
|
||||
}
|
||||
if(resourceJid && (!members || !members[newJID] ||
|
||||
!members[newJID].displayName)) {
|
||||
remoteVideos[resourceJid].setDisplayName(null,
|
||||
if (resourceJid && (!members || !members[newJID] ||
|
||||
!members[newJID].displayName) && remoteVideo) {
|
||||
remoteVideo.setDisplayName(null,
|
||||
interfaceConfig.DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME);
|
||||
}
|
||||
currentDominantSpeaker = resourceJid;
|
||||
@@ -591,13 +607,15 @@ var VideoLayout = (function (my) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!remoteVideo)
|
||||
return;
|
||||
|
||||
// Obtain container for new dominant speaker.
|
||||
var videoSel = VideoLayout.getPeerVideoSel(resourceJid);
|
||||
var videoSel = remoteVideo.selectVideoElement();
|
||||
|
||||
// Local video will not have container found, but that's ok
|
||||
// since we don't want to switch to local video.
|
||||
if (!focusedVideoResourceJid && videoSel.length)
|
||||
{
|
||||
if (!focusedVideoResourceJid && videoSel.length) {
|
||||
// Update the large video if the video source is already available,
|
||||
// otherwise wait for the "videoactive.jingle" event.
|
||||
if (videoSel[0].currentTime > 0) {
|
||||
@@ -613,9 +631,9 @@ var VideoLayout = (function (my) {
|
||||
* @param endpointsEnteringLastN the list currently entering last N
|
||||
* endpoints
|
||||
*/
|
||||
my.onLastNEndpointsChanged = function ( lastNEndpoints,
|
||||
endpointsEnteringLastN,
|
||||
stream) {
|
||||
my.onLastNEndpointsChanged = function (lastNEndpoints,
|
||||
endpointsEnteringLastN,
|
||||
stream) {
|
||||
if (lastNCount !== lastNEndpoints.length)
|
||||
lastNCount = lastNEndpoints.length;
|
||||
|
||||
@@ -652,7 +670,6 @@ var VideoLayout = (function (my) {
|
||||
}
|
||||
|
||||
localLastNSet = nextLocalLastNSet;
|
||||
|
||||
var updateLargeVideo = false;
|
||||
|
||||
// Handle LastN/local LastN changes.
|
||||
@@ -660,17 +677,23 @@ var VideoLayout = (function (my) {
|
||||
var resourceJid = VideoLayout.getPeerContainerResourceJid(element);
|
||||
|
||||
var isReceived = true;
|
||||
if (resourceJid
|
||||
&& lastNEndpoints.indexOf(resourceJid) < 0
|
||||
&& localLastNSet.indexOf(resourceJid) < 0) {
|
||||
if (resourceJid &&
|
||||
lastNEndpoints.indexOf(resourceJid) < 0 &&
|
||||
localLastNSet.indexOf(resourceJid) < 0) {
|
||||
console.log("Remove from last N", resourceJid);
|
||||
remoteVideos[resourceJid].showPeerContainer('hide');
|
||||
if (remoteVideos[resourceJid])
|
||||
remoteVideos[resourceJid].showPeerContainer('hide');
|
||||
else if (APP.xmpp.myResource() !== resourceJid)
|
||||
console.error("No remote video for: " + resourceJid);
|
||||
isReceived = false;
|
||||
} else if (resourceJid
|
||||
&& $('#participant_' + resourceJid).is(':visible')
|
||||
&& lastNEndpoints.indexOf(resourceJid) < 0
|
||||
&& localLastNSet.indexOf(resourceJid) >= 0) {
|
||||
remoteVideos[resourceJid].showPeerContainer('avatar');
|
||||
} else if (resourceJid &&
|
||||
$('#participant_' + resourceJid).is(':visible') &&
|
||||
lastNEndpoints.indexOf(resourceJid) < 0 &&
|
||||
localLastNSet.indexOf(resourceJid) >= 0) {
|
||||
if (remoteVideos[resourceJid])
|
||||
remoteVideos[resourceJid].showPeerContainer('avatar');
|
||||
else if (APP.xmpp.myResource() !== resourceJid)
|
||||
console.error("No remote video for: " + resourceJid);
|
||||
isReceived = false;
|
||||
}
|
||||
|
||||
@@ -679,7 +702,8 @@ var VideoLayout = (function (my) {
|
||||
// it is no longer being received. If resourceJid was being
|
||||
// displayed in the large video we have to switch to another
|
||||
// user.
|
||||
if (!updateLargeVideo && resourceJid === LargeVideo.getResourceJid()) {
|
||||
if (!updateLargeVideo &&
|
||||
resourceJid === LargeVideo.getResourceJid()) {
|
||||
updateLargeVideo = true;
|
||||
}
|
||||
}
|
||||
@@ -692,13 +716,15 @@ var VideoLayout = (function (my) {
|
||||
endpointsEnteringLastN.forEach(function (resourceJid) {
|
||||
|
||||
var isVisible = $('#participant_' + resourceJid).is(':visible');
|
||||
remoteVideos[resourceJid].showPeerContainer('show');
|
||||
var remoteVideo = remoteVideos[resourceJid];
|
||||
remoteVideo.showPeerContainer('show');
|
||||
if (!isVisible) {
|
||||
console.log("Add to last N", resourceJid);
|
||||
|
||||
var jid = APP.xmpp.findJidFromResource(resourceJid);
|
||||
var mediaStream = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
||||
var sel = VideoLayout.getPeerVideoSel(resourceJid);
|
||||
var mediaStream =
|
||||
APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
|
||||
var sel = remoteVideo.selectVideoElement();
|
||||
|
||||
APP.RTC.attachMediaStream(sel, mediaStream.stream);
|
||||
if (lastNPickupJid == mediaStream.peerjid) {
|
||||
@@ -713,7 +739,8 @@ var VideoLayout = (function (my) {
|
||||
|
||||
updateLargeVideo = false;
|
||||
}
|
||||
remoteVideos[resourceJid].waitForPlayback(mediaStream.stream);
|
||||
remoteVideos[resourceJid].
|
||||
waitForPlayback(sel, mediaStream.stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -723,13 +750,12 @@ var VideoLayout = (function (my) {
|
||||
// the large video now.
|
||||
|
||||
if (updateLargeVideo) {
|
||||
|
||||
var resource, container, src;
|
||||
var resource;
|
||||
var myResource
|
||||
= APP.xmpp.myResource();
|
||||
|
||||
// Find out which endpoint to show in the large video.
|
||||
for (var i = 0; i < lastNEndpoints.length; i++) {
|
||||
for (i = 0; i < lastNEndpoints.length; i++) {
|
||||
resource = lastNEndpoints[i];
|
||||
if (!resource || resource === myResource)
|
||||
continue;
|
||||
@@ -737,7 +763,6 @@ var VideoLayout = (function (my) {
|
||||
// videoSrcToSsrc needs to be update for this call to succeed.
|
||||
LargeVideo.updateLargeVideo(resource);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -749,24 +774,22 @@ var VideoLayout = (function (my) {
|
||||
*/
|
||||
my.updateLocalConnectionStats = function (percent, object) {
|
||||
var resolution = null;
|
||||
if(object.resolution !== null)
|
||||
{
|
||||
if (object.resolution !== null) {
|
||||
resolution = object.resolution;
|
||||
object.resolution = resolution[APP.xmpp.myJid()];
|
||||
delete resolution[APP.xmpp.myJid()];
|
||||
}
|
||||
localVideoThumbnail.updateStatsIndicator(percent, object);
|
||||
for(var jid in resolution)
|
||||
{
|
||||
if(resolution[jid] === null)
|
||||
for (var jid in resolution) {
|
||||
if (resolution[jid] === null)
|
||||
continue;
|
||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||
if(remoteVideos[resourceJid] && remoteVideos[resourceJid].connectionIndicator)
|
||||
{
|
||||
remoteVideos[resourceJid].connectionIndicator.updateResolution(resolution[jid]);
|
||||
if (remoteVideos[resourceJid] &&
|
||||
remoteVideos[resourceJid].connectionIndicator) {
|
||||
remoteVideos[resourceJid].connectionIndicator.
|
||||
updateResolution(resolution[jid]);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -778,18 +801,10 @@ var VideoLayout = (function (my) {
|
||||
my.updateConnectionStats = function (jid, percent, object) {
|
||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||
|
||||
if(remoteVideos[resourceJid])
|
||||
if (remoteVideos[resourceJid])
|
||||
remoteVideos[resourceJid].updateStatsIndicator(percent, object);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the connection
|
||||
* @param jid
|
||||
*/
|
||||
my.removeConnectionIndicator = function (jid) {
|
||||
remoteVideos[Strophe.getResourceFromJid(jid)].removeConnectionIndicator();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the connection indicator
|
||||
* @param jid
|
||||
@@ -802,8 +817,7 @@ var VideoLayout = (function (my) {
|
||||
* Hides all the indicators
|
||||
*/
|
||||
my.onStatsStop = function () {
|
||||
for(var video in remoteVideos)
|
||||
{
|
||||
for(var video in remoteVideos) {
|
||||
remoteVideos[video].hideIndicator();
|
||||
}
|
||||
localVideoThumbnail.hideIndicator();
|
||||
@@ -812,77 +826,184 @@ var VideoLayout = (function (my) {
|
||||
my.participantLeft = function (jid) {
|
||||
// Unlock large video
|
||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||
if (focusedVideoResourceJid === resourceJid)
|
||||
{
|
||||
if (focusedVideoResourceJid === resourceJid) {
|
||||
console.info("Focused video owner has left the conference");
|
||||
focusedVideoResourceJid = null;
|
||||
}
|
||||
|
||||
if (currentDominantSpeaker === resourceJid)
|
||||
{
|
||||
console.info("Dominant speaker has left the conference");
|
||||
currentDominantSpeaker = null;
|
||||
}
|
||||
|
||||
var remoteVideo = remoteVideos[resourceJid];
|
||||
if (remoteVideo) {
|
||||
// Remove remote video
|
||||
console.info("Removing remote video: " + resourceJid);
|
||||
remoteVideo.remove();
|
||||
delete remoteVideos[resourceJid];
|
||||
} else {
|
||||
console.warn("No remote video for " + resourceJid);
|
||||
}
|
||||
|
||||
VideoLayout.resizeThumbnails();
|
||||
};
|
||||
|
||||
my.onVideoTypeChanged = function (jid) {
|
||||
LargeVideo.onVideoTypeChanged(jid);
|
||||
my.onVideoTypeChanged = function (resourceJid, newVideoType) {
|
||||
if (remoteVideoTypes[resourceJid] === newVideoType) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("Peer video type changed: ", resourceJid, newVideoType);
|
||||
remoteVideoTypes[resourceJid] = newVideoType;
|
||||
|
||||
var smallVideo;
|
||||
if (resourceJid === APP.xmpp.myResource()) {
|
||||
if (!localVideoThumbnail) {
|
||||
console.warn("Local video not ready yet");
|
||||
return;
|
||||
}
|
||||
smallVideo = localVideoThumbnail;
|
||||
} else if (remoteVideos[resourceJid]) {
|
||||
smallVideo = remoteVideos[resourceJid];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
smallVideo.setVideoType(newVideoType);
|
||||
LargeVideo.onVideoTypeChanged(resourceJid, newVideoType);
|
||||
|
||||
};
|
||||
|
||||
my.showMore = function (jid) {
|
||||
if(APP.xmpp.myJid = jid)
|
||||
{
|
||||
if (jid === 'local') {
|
||||
localVideoThumbnail.connectionIndicator.showMore();
|
||||
} else {
|
||||
var remoteVideo = remoteVideos[Strophe.getResourceFromJid(jid)];
|
||||
if (remoteVideo) {
|
||||
remoteVideo.connectionIndicator.showMore();
|
||||
} else {
|
||||
console.info("Error - no remote video for jid: " + jid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteVideos[Strophe.getResourceFromJid(jid)].connectionIndicator.showMore();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
my.addPreziContainer = function (id) {
|
||||
return RemoteVideo.createContainer(id);
|
||||
var container = RemoteVideo.createContainer(id);
|
||||
VideoLayout.resizeThumbnails();
|
||||
return container;
|
||||
};
|
||||
|
||||
my.setLargeVideoVisible = function (isVisible) {
|
||||
LargeVideo.setLargeVideoVisible(isVisible);
|
||||
if(!isVisible && focusedVideoResourceJid)
|
||||
{
|
||||
if(!isVisible && focusedVideoResourceJid) {
|
||||
var smallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
|
||||
if(smallVideo)
|
||||
if(smallVideo) {
|
||||
smallVideo.focus(false);
|
||||
smallVideo.showAvatar();
|
||||
smallVideo.showAvatar();
|
||||
}
|
||||
focusedVideoResourceJid = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resizes the video area
|
||||
* @param callback a function to be called when the video space is
|
||||
* resized.
|
||||
*/
|
||||
my.resizeVideoArea = function(isVisible, callback) {
|
||||
LargeVideo.resizeVideoAreaAnimated(isVisible, callback);
|
||||
VideoLayout.resizeThumbnails(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resizes the #videospace html element
|
||||
* @param animate boolean property that indicates whether the resize should be animated or not.
|
||||
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
|
||||
* If that parameter is null the method will check the chat pannel visibility.
|
||||
* @param completeFunction a function to be called when the video space is resized
|
||||
*/
|
||||
my.resizeVideoArea = function(isVisible, completeFunction) {
|
||||
LargeVideo.resizeVideoAreaAnimated(isVisible, completeFunction);
|
||||
VideoLayout.resizeThumbnails(true);
|
||||
my.resizeVideoSpace = function (animate, isChatVisible, completeFunction) {
|
||||
var availableHeight = window.innerHeight;
|
||||
var availableWidth = UIUtil.getAvailableVideoWidth(isChatVisible);
|
||||
|
||||
if (availableWidth < 0 || availableHeight < 0) return;
|
||||
|
||||
if(animate) {
|
||||
$('#videospace').animate({
|
||||
right: window.innerWidth - availableWidth,
|
||||
width: availableWidth,
|
||||
height: availableHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500,
|
||||
complete: completeFunction
|
||||
});
|
||||
} else {
|
||||
$('#videospace').width(availableWidth);
|
||||
$('#videospace').height(availableHeight);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
my.getSmallVideo = function (resourceJid) {
|
||||
if(resourceJid == APP.xmpp.myResource())
|
||||
{
|
||||
if(resourceJid == APP.xmpp.myResource()) {
|
||||
return localVideoThumbnail;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if(!remoteVideos[resourceJid])
|
||||
return null;
|
||||
return remoteVideos[resourceJid];
|
||||
}
|
||||
};
|
||||
|
||||
my.userAvatarChanged = function(resourceJid, thumbUrl)
|
||||
{
|
||||
my.userAvatarChanged = function(resourceJid, thumbUrl) {
|
||||
var smallVideo = VideoLayout.getSmallVideo(resourceJid);
|
||||
if(smallVideo)
|
||||
smallVideo.avatarChanged(thumbUrl);
|
||||
else
|
||||
console.warn(
|
||||
"Missed avatar update - no small video yet for " + resourceJid);
|
||||
LargeVideo.updateAvatar(resourceJid, thumbUrl);
|
||||
};
|
||||
|
||||
my.createEtherpadIframe = function(src, onloadHandler)
|
||||
{
|
||||
return LargeVideo.createEtherpadIframe(src, onloadHandler);
|
||||
};
|
||||
|
||||
my.setLargeVideoState = function (state) {
|
||||
LargeVideo.setState(state);
|
||||
};
|
||||
|
||||
my.getLargeVideoState = function () {
|
||||
return LargeVideo.getState();
|
||||
};
|
||||
|
||||
my.setLargeVideoHover = function (inHandler, outHandler) {
|
||||
LargeVideo.setHover(inHandler, outHandler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates that the video has been interrupted.
|
||||
*/
|
||||
my.onVideoInterrupted = function () {
|
||||
LargeVideo.enableVideoProblemFilter(true);
|
||||
var reconnectingKey = "connection.RECONNECTING";
|
||||
$('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
|
||||
$('#videoConnectionMessage').text(APP.translation.translateString(reconnectingKey));
|
||||
$('#videoConnectionMessage').css({display: "block"});
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates that the video has been restored.
|
||||
*/
|
||||
my.onVideoRestored = function () {
|
||||
LargeVideo.enableVideoProblemFilter(false);
|
||||
$('#videoConnectionMessage').css({display: "none"});
|
||||
};
|
||||
|
||||
return my;
|
||||
}(VideoLayout || {}));
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ var adverbs = [
|
||||
"Obviously", "Often", "Painfully", "Patiently", "Playfully", "Politely", "Poorly", "Precisely", "Promptly",
|
||||
"Quickly", "Quietly", "Randomly", "Rapidly", "Rarely", "Recklessly", "Regularly", "Remorsefully", "Responsibly",
|
||||
"Rudely", "Ruthlessly", "Sadly", "Scornfully", "Seamlessly", "Seldom", "Selfishly", "Seriously", "Shakily",
|
||||
"Sharply", "Sideways", "Silently", "Sleepily", "Slightly", "Slowly", "Slyly", "Smoothly", "Softly", "Solemnly", "Steadily", "Sternly", "Strangely", "Strongly", "Stunningly", "Surely", "Tenderly", "Thoughtfully",
|
||||
"Sharply", "Sideways", "Silently", "Sleepily", "Slightly", "Slowly", "Slyly", "Smoothly", "Softly", "Solemnly",
|
||||
"Steadily", "Sternly", "Strangely", "Strongly", "Stunningly", "Surely", "Tenderly", "Thoughtfully",
|
||||
"Tightly", "Uneasily", "Vanishingly", "Violently", "Warmly", "Weakly", "Wearily", "Weekly", "Weirdly", "Well",
|
||||
"Well", "Wickedly", "Wildly", "Wisely", "Wonderfully", "Yearly"
|
||||
];
|
||||
@@ -137,8 +138,7 @@ var PATTERNS = [
|
||||
/*
|
||||
* Returns a random element from the array 'arr'
|
||||
*/
|
||||
function randomElement(arr)
|
||||
{
|
||||
function randomElement(arr) {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
@@ -146,8 +146,7 @@ function randomElement(arr)
|
||||
* Returns true if the string 's' contains one of the
|
||||
* template strings.
|
||||
*/
|
||||
function hasTemplate(s)
|
||||
{
|
||||
function hasTemplate(s) {
|
||||
for (var template in CATEGORIES){
|
||||
if (s.indexOf(template) >= 0){
|
||||
return true;
|
||||
@@ -159,14 +158,15 @@ function hasTemplate(s)
|
||||
* Generates new room name.
|
||||
*/
|
||||
var RoomNameGenerator = {
|
||||
generateRoomWithoutSeparator: function()
|
||||
{
|
||||
// Note that if more than one pattern is available, the choice of 'name' won't be random (names from patterns
|
||||
// with fewer options will have higher probability of being chosen that names from patterns with more options).
|
||||
generateRoomWithoutSeparator: function() {
|
||||
// Note that if more than one pattern is available, the choice of
|
||||
// 'name' won't have a uniform distribution amongst all patterns (names
|
||||
// from patterns with fewer options will have higher probability of
|
||||
// being chosen that names from patterns with more options).
|
||||
var name = randomElement(PATTERNS);
|
||||
var word;
|
||||
while (hasTemplate(name)){
|
||||
for (var template in CATEGORIES){
|
||||
while (hasTemplate(name)) {
|
||||
for (var template in CATEGORIES) {
|
||||
word = randomElement(CATEGORIES[template]);
|
||||
name = name.replace(template, word);
|
||||
}
|
||||
@@ -174,6 +174,6 @@ var RoomNameGenerator = {
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = RoomNameGenerator;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* global $, interfaceConfig */
|
||||
var animateTimeout, updateTimeout;
|
||||
|
||||
var RoomNameGenerator = require("./RoomnameGenerator");
|
||||
|
||||
function enter_room()
|
||||
{
|
||||
function enter_room() {
|
||||
var val = $("#enter_room_field").val();
|
||||
if(!val) {
|
||||
val = $("#enter_room_field").attr("room_name");
|
||||
@@ -21,8 +21,7 @@ function animate(word) {
|
||||
}, 70);
|
||||
}
|
||||
|
||||
function update_roomname()
|
||||
{
|
||||
function update_roomname() {
|
||||
var word = RoomNameGenerator.generateRoomWithoutSeparator();
|
||||
$("#enter_room_field").attr("room_name", word);
|
||||
$("#enter_room_field").attr("placeholder", "");
|
||||
@@ -31,33 +30,30 @@ function update_roomname()
|
||||
updateTimeout = setTimeout(update_roomname, 10000);
|
||||
}
|
||||
|
||||
|
||||
function setupWelcomePage()
|
||||
{
|
||||
function setupWelcomePage() {
|
||||
$("#videoconference_page").hide();
|
||||
$("#domain_name").text(
|
||||
window.location.protocol + "//" + window.location.host + "/");
|
||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
||||
var leftWatermarkDiv
|
||||
= $("#welcome_page_header div[class='watermark leftwatermark']");
|
||||
if(leftWatermarkDiv && leftWatermarkDiv.length > 0)
|
||||
{
|
||||
var leftWatermarkDiv =
|
||||
$("#welcome_page_header div[class='watermark leftwatermark']");
|
||||
if(leftWatermarkDiv && leftWatermarkDiv.length > 0) {
|
||||
leftWatermarkDiv.css({display: 'block'});
|
||||
leftWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.JITSI_WATERMARK_LINK;
|
||||
leftWatermarkDiv.parent().get(0).href =
|
||||
interfaceConfig.JITSI_WATERMARK_LINK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
var rightWatermarkDiv
|
||||
= $("#welcome_page_header div[class='watermark rightwatermark']");
|
||||
var rightWatermarkDiv =
|
||||
$("#welcome_page_header div[class='watermark rightwatermark']");
|
||||
if(rightWatermarkDiv && rightWatermarkDiv.length > 0) {
|
||||
rightWatermarkDiv.css({display: 'block'});
|
||||
rightWatermarkDiv.parent().get(0).href
|
||||
= interfaceConfig.BRAND_WATERMARK_LINK;
|
||||
rightWatermarkDiv.get(0).style.backgroundImage
|
||||
= "url(images/rightwatermark.png)";
|
||||
rightWatermarkDiv.parent().get(0).href =
|
||||
interfaceConfig.BRAND_WATERMARK_LINK;
|
||||
rightWatermarkDiv.get(0).style.backgroundImage =
|
||||
"url(images/rightwatermark.png)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +62,7 @@ function setupWelcomePage()
|
||||
.css({display: 'block'});
|
||||
}
|
||||
|
||||
$("#enter_room_button").click(function()
|
||||
{
|
||||
$("#enter_room_button").click(function() {
|
||||
enter_room();
|
||||
});
|
||||
|
||||
@@ -77,23 +72,23 @@ function setupWelcomePage()
|
||||
}
|
||||
});
|
||||
|
||||
if (!(interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE === false)){
|
||||
if (interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE !== false) {
|
||||
var updateTimeout;
|
||||
var animateTimeout;
|
||||
$("#reload_roomname").click(function () {
|
||||
var selector = $("#reload_roomname");
|
||||
selector.click(function () {
|
||||
clearTimeout(updateTimeout);
|
||||
clearTimeout(animateTimeout);
|
||||
update_roomname();
|
||||
});
|
||||
$("#reload_roomname").show();
|
||||
|
||||
selector.show();
|
||||
|
||||
update_roomname();
|
||||
}
|
||||
|
||||
$("#disable_welcome").click(function () {
|
||||
window.localStorage.welcomePageDisabled
|
||||
= $("#disable_welcome").is(":checked");
|
||||
window.localStorage.welcomePageDisabled =
|
||||
$("#disable_welcome").is(":checked");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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;
|
||||
55
modules/config/HttpConfigFetch.js
Normal file
55
modules/config/HttpConfigFetch.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/* global $, $iq, config, interfaceConfig */
|
||||
|
||||
var configUtil = require('./Util');
|
||||
|
||||
var HttpConfig = {
|
||||
/**
|
||||
* Sends HTTP POST request to specified <tt>endpoint</tt>. In request
|
||||
* the name of the room is included in JSON format:
|
||||
* {
|
||||
* "rooomName": "someroom12345"
|
||||
* }
|
||||
* @param endpoint the name of HTTP endpoint to which HTTP POST request will
|
||||
* be sent.
|
||||
* @param roomName the name of the conference room for which config will be
|
||||
* requested.
|
||||
* @param complete
|
||||
*/
|
||||
obtainConfig: function (endpoint, roomName, complete) {
|
||||
console.info(
|
||||
"Send config request to " + endpoint + " for room: " + roomName);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
var error = null;
|
||||
request.onreadystatechange = function (aEvt) {
|
||||
if (request.readyState == 4) {
|
||||
var status = request.status;
|
||||
if (status === 200) {
|
||||
try {
|
||||
var data = JSON.parse(request.responseText);
|
||||
configUtil.overrideConfigJSON(
|
||||
config, interfaceConfig, data);
|
||||
complete(true);
|
||||
return;
|
||||
} catch (exception) {
|
||||
console.error("Parse config error: ", exception);
|
||||
error = exception;
|
||||
}
|
||||
} else {
|
||||
console.error("Get config error: ", request, status);
|
||||
error = "Get config response status: " + status;
|
||||
}
|
||||
complete(false, error);
|
||||
}
|
||||
};
|
||||
|
||||
request.open("POST", endpoint, true);
|
||||
|
||||
request.setRequestHeader(
|
||||
"Content-Type", "application/json;charset=UTF-8");
|
||||
|
||||
request.send({ "roomName": roomName });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = HttpConfig;
|
||||
65
modules/config/URLProcessor.js
Normal file
65
modules/config/URLProcessor.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/* global $, $iq, config, interfaceConfig */
|
||||
var configUtils = require('./Util');
|
||||
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 () {
|
||||
// Convert 'params' to JSON object
|
||||
// We have:
|
||||
// {
|
||||
// "config.disableAudioLevels": false,
|
||||
// "config.channelLastN": -1,
|
||||
// "interfaceConfig.APP_NAME": "Jitsi Meet"
|
||||
// }
|
||||
// We want to have:
|
||||
// {
|
||||
// "config": {
|
||||
// "disableAudioLevels": false,
|
||||
// "channelLastN": -1
|
||||
// },
|
||||
// interfaceConfig: {
|
||||
// APP_NAME: "Jitsi Meet"
|
||||
// }
|
||||
// }
|
||||
var configJSON = {
|
||||
config: {},
|
||||
interfaceConfig: {}
|
||||
};
|
||||
for (var key in params) {
|
||||
if (typeof key !== "string") {
|
||||
console.warn("Invalid config key: ", key);
|
||||
continue;
|
||||
}
|
||||
var confObj = null, confKey;
|
||||
if (key.indexOf("config.") === 0) {
|
||||
confObj = configJSON.config;
|
||||
confKey = key.substr("config.".length);
|
||||
} else if (key.indexOf("interfaceConfig.") === 0) {
|
||||
confObj = configJSON.interfaceConfig;
|
||||
confKey = key.substr("interfaceConfig.".length);
|
||||
}
|
||||
|
||||
if (!confObj)
|
||||
continue;
|
||||
|
||||
confObj[confKey] = params[key];
|
||||
}
|
||||
configUtils.overrideConfigJSON(config, interfaceConfig, configJSON);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = URLProcessor;
|
||||
49
modules/config/Util.js
Normal file
49
modules/config/Util.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* global $ */
|
||||
var ConfigUtil = {
|
||||
/**
|
||||
* Method overrides JSON properties in <tt>config</tt> and
|
||||
* <tt>interfaceConfig</tt> Objects with the values from <tt>newConfig</tt>
|
||||
* @param config the config object for which we'll be overriding properties
|
||||
* @param interfaceConfig the interfaceConfig object for which we'll be
|
||||
* overriding properties.
|
||||
* @param newConfig object containing configuration properties. Destination
|
||||
* object is selected based on root property name:
|
||||
* {
|
||||
* config: {
|
||||
* // config.js properties to be
|
||||
* },
|
||||
* interfaceConfig: {
|
||||
* // interfaceConfig.js properties here
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
overrideConfigJSON: function (config, interfaceConfig, newConfig) {
|
||||
for (var configRoot in newConfig) {
|
||||
|
||||
var confObj = null;
|
||||
if (configRoot == "config") {
|
||||
confObj = config;
|
||||
} else if (configRoot == "interfaceConfig") {
|
||||
confObj = interfaceConfig;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var key in newConfig[configRoot]) {
|
||||
var value = newConfig[configRoot][key];
|
||||
if (confObj[key] && typeof confObj[key] !== typeof value)
|
||||
{
|
||||
console.warn(
|
||||
"The type of " + key +
|
||||
" is wrong. That parameter won't be updated in: ",
|
||||
confObj);
|
||||
continue;
|
||||
}
|
||||
console.info("Overriding " + key + " with: " + value);
|
||||
confObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ConfigUtil;
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global APP, require */
|
||||
var EventEmitter = require("events");
|
||||
var eventEmitter = new EventEmitter();
|
||||
var CQEvents = require("../../service/connectionquality/CQEvents");
|
||||
@@ -53,7 +54,7 @@ function convertToMUCStats(stats) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts statitistics to format used by VideoLayout
|
||||
* Converts statistics to format used by VideoLayout
|
||||
* @param stats
|
||||
* @returns {{bitrate: {download: *, upload: *}, packetLoss: {total: *, download: *, upload: *}}}
|
||||
*/
|
||||
@@ -71,7 +72,6 @@ function parseMUCStats(stats) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var ConnectionQuality = {
|
||||
init: function () {
|
||||
APP.xmpp.addListener(XMPPEvents.REMOTE_STATS, this.updateRemoteStats);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global APP, $ */
|
||||
//maps keycode to character, id of popover for given function and function
|
||||
var shortcuts = {};
|
||||
function initShortcutHandlers() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global APP */
|
||||
/* global APP, require, $ */
|
||||
|
||||
/**
|
||||
* This module is meant to (eventually) contain and manage all information
|
||||
@@ -87,7 +87,7 @@ function updateDtmf(jid, newValue) {
|
||||
* Checks each member's 'supportsDtmf' field and updates
|
||||
* 'atLastOneSupportsDtmf'.
|
||||
*/
|
||||
function updateAtLeastOneDtmf(){
|
||||
function updateAtLeastOneDtmf() {
|
||||
var newAtLeastOneDtmf = false;
|
||||
for (var key in members) {
|
||||
if (typeof members[key].supportsDtmf !== 'undefined'
|
||||
@@ -108,11 +108,10 @@ function updateAtLeastOneDtmf(){
|
||||
* Exported interface.
|
||||
*/
|
||||
var Members = {
|
||||
start: function(){
|
||||
start: function() {
|
||||
registerListeners();
|
||||
},
|
||||
addListener: function(type, listener)
|
||||
{
|
||||
addListener: function(type, listener) {
|
||||
eventEmitter.on(type, listener);
|
||||
},
|
||||
removeListener: function (type, listener) {
|
||||
|
||||
@@ -35,15 +35,13 @@ if (supportsLocalStorage()) {
|
||||
userId = generateUniqueId();
|
||||
}
|
||||
|
||||
var Settings =
|
||||
{
|
||||
var Settings = {
|
||||
setDisplayName: function (newDisplayName) {
|
||||
displayName = newDisplayName;
|
||||
window.localStorage.displayname = displayName;
|
||||
return displayName;
|
||||
},
|
||||
setEmail: function (newEmail)
|
||||
{
|
||||
setEmail: function (newEmail) {
|
||||
email = newEmail;
|
||||
window.localStorage.email = newEmail;
|
||||
return email;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/* global config, $, APP, Strophe, callstats */
|
||||
var jsSHA = require('jssha');
|
||||
var io = require('socket.io-client');
|
||||
var callStats = null;
|
||||
|
||||
function initCallback (err, msg) {
|
||||
@@ -10,7 +13,7 @@ var CallStats = {
|
||||
if(!config.callStatsID || !config.callStatsSecret || callStats !== null)
|
||||
return;
|
||||
|
||||
callStats = new callstats($,io,jsSHA);
|
||||
callStats = new callstats($, io, jsSHA);
|
||||
|
||||
this.session = jingleSession;
|
||||
this.peerconnection = jingleSession.peerconnection.peerconnection;
|
||||
@@ -27,9 +30,7 @@ var CallStats = {
|
||||
this.userID,
|
||||
initCallback);
|
||||
|
||||
var usage = callStats.fabricUsage.unbundled;
|
||||
if(config.useBundle)
|
||||
usage = callStats.fabricUsage.multiplex;
|
||||
var usage = callStats.fabricUsage.multiplex;
|
||||
|
||||
callStats.addNewFabric(this.peerconnection,
|
||||
Strophe.getResourceFromJid(jingleSession.peerjid),
|
||||
@@ -38,37 +39,37 @@ var CallStats = {
|
||||
this.pcCallback.bind(this));
|
||||
},
|
||||
pcCallback: function (err, msg) {
|
||||
if(!callStats)
|
||||
if (!callStats)
|
||||
return;
|
||||
console.log("Monitoring status: "+ err + " msg: " + msg);
|
||||
callStats.sendFabricEvent(this.peerconnection,
|
||||
callStats.fabricEvent.fabricSetup, this.confID);
|
||||
},
|
||||
sendMuteEvent: function (mute, type) {
|
||||
if(!callStats)
|
||||
if (!callStats)
|
||||
return;
|
||||
var event = null;
|
||||
if(type === "video")
|
||||
{
|
||||
if (type === "video") {
|
||||
event = (mute? callStats.fabricEvent.videoPause :
|
||||
callStats.fabricEvent.videoResume);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
event = (mute? callStats.fabricEvent.audioMute :
|
||||
callStats.fabricEvent.audioUnmute);
|
||||
}
|
||||
callStats.sendFabricEvent(this.peerconnection, event, this.confID);
|
||||
},
|
||||
sendTerminateEvent: function () {
|
||||
if(!callStats)
|
||||
if(!callStats) {
|
||||
return;
|
||||
}
|
||||
callStats.sendFabricEvent(this.peerconnection,
|
||||
callStats.fabricEvent.fabricTerminated, this.confID);
|
||||
},
|
||||
sendSetupFailedEvent: function () {
|
||||
if(!callStats)
|
||||
if(!callStats) {
|
||||
return;
|
||||
}
|
||||
callStats.sendFabricEvent(this.peerconnection,
|
||||
callStats.fabricEvent.fabricSetupFailed, this.confID);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
/* global config */
|
||||
/**
|
||||
* Provides statistics for the local stream.
|
||||
*/
|
||||
|
||||
var RTCBrowserType = require('../RTC/RTCBrowserType');
|
||||
|
||||
/**
|
||||
* Size of the webaudio analizer buffer.
|
||||
* Size of the webaudio analyzer buffer.
|
||||
* @type {number}
|
||||
*/
|
||||
var WEBAUDIO_ANALIZER_FFT_SIZE = 2048;
|
||||
var WEBAUDIO_ANALYZER_FFT_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* Value of the webaudio analizer smoothing time parameter.
|
||||
* Value of the webaudio analyzer smoothing time parameter.
|
||||
* @type {number}
|
||||
*/
|
||||
var WEBAUDIO_ANALIZER_SMOOTING_TIME = 0.8;
|
||||
var WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8;
|
||||
|
||||
/**
|
||||
* Converts time domain data array to audio level.
|
||||
* @param array the time domain data array.
|
||||
* @param samples the time domain data array.
|
||||
* @returns {number} the audio level
|
||||
*/
|
||||
function timeDomainDataToAudioLevel(samples) {
|
||||
@@ -32,7 +34,7 @@ function timeDomainDataToAudioLevel(samples) {
|
||||
}
|
||||
|
||||
return parseFloat(((maxVolume - 127) / 128).toFixed(3));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates audio level change
|
||||
@@ -40,20 +42,16 @@ function timeDomainDataToAudioLevel(samples) {
|
||||
* @param lastLevel the last audio level
|
||||
* @returns {Number} the audio level to be set
|
||||
*/
|
||||
function animateLevel(newLevel, lastLevel)
|
||||
{
|
||||
function animateLevel(newLevel, lastLevel) {
|
||||
var value = 0;
|
||||
var diff = lastLevel - newLevel;
|
||||
if(diff > 0.2)
|
||||
{
|
||||
if(diff > 0.2) {
|
||||
value = lastLevel - 0.2;
|
||||
}
|
||||
else if(diff < -0.4)
|
||||
{
|
||||
else if(diff < -0.4) {
|
||||
value = lastLevel + 0.4;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
value = newLevel;
|
||||
}
|
||||
|
||||
@@ -66,8 +64,6 @@ function animateLevel(newLevel, lastLevel)
|
||||
*
|
||||
* @param stream the local stream
|
||||
* @param interval stats refresh interval given in ms.
|
||||
* @param {function(LocalStatsCollector)} updateCallback the callback called on stats
|
||||
* update.
|
||||
* @constructor
|
||||
*/
|
||||
function LocalStatsCollector(stream, interval, statisticsService, eventEmitter) {
|
||||
@@ -84,13 +80,14 @@ function LocalStatsCollector(stream, interval, statisticsService, eventEmitter)
|
||||
* Starts the collecting the statistics.
|
||||
*/
|
||||
LocalStatsCollector.prototype.start = function () {
|
||||
if (config.disableAudioLevels || !window.AudioContext)
|
||||
if (config.disableAudioLevels || !window.AudioContext ||
|
||||
RTCBrowserType.isTemasysPluginUsed())
|
||||
return;
|
||||
|
||||
var context = new AudioContext();
|
||||
var analyser = context.createAnalyser();
|
||||
analyser.smoothingTimeConstant = WEBAUDIO_ANALIZER_SMOOTING_TIME;
|
||||
analyser.fftSize = WEBAUDIO_ANALIZER_FFT_SIZE;
|
||||
analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;
|
||||
analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;
|
||||
|
||||
|
||||
var source = context.createMediaStreamSource(this.stream);
|
||||
@@ -104,7 +101,7 @@ LocalStatsCollector.prototype.start = function () {
|
||||
var array = new Uint8Array(analyser.frequencyBinCount);
|
||||
analyser.getByteTimeDomainData(array);
|
||||
var audioLevel = timeDomainDataToAudioLevel(array);
|
||||
if(audioLevel != self.audioLevel) {
|
||||
if (audioLevel != self.audioLevel) {
|
||||
self.audioLevel = animateLevel(audioLevel, self.audioLevel);
|
||||
self.eventEmitter.emit(
|
||||
"statistics.audioLevel",
|
||||
@@ -114,7 +111,6 @@ LocalStatsCollector.prototype.start = function () {
|
||||
},
|
||||
this.intervalMilis
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* global ssrc2jid */
|
||||
/* global require, ssrc2jid */
|
||||
/* jshint -W117 */
|
||||
var RTCBrowserType = require("../RTC/RTCBrowserType");
|
||||
|
||||
|
||||
/* Whether we support the browser we are running into for logging statistics */
|
||||
var browserSupported = RTCBrowserType.isChrome() ||
|
||||
RTCBrowserType.isOpera() || RTCBrowserType.isFirefox();
|
||||
/**
|
||||
* Calculates packet lost percent using the number of lost packets and the
|
||||
* number of all packet.
|
||||
@@ -49,8 +51,6 @@ PeerStats.bandwidth = {};
|
||||
*/
|
||||
PeerStats.bitrate = {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The packet loss rate
|
||||
* @type {{}}
|
||||
@@ -235,7 +235,7 @@ StatsCollector.prototype.errorCallback = function (error)
|
||||
StatsCollector.prototype.start = function ()
|
||||
{
|
||||
var self = this;
|
||||
if(!config.disableAudioLevels) {
|
||||
if (!config.disableAudioLevels) {
|
||||
this.audioLevelsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -262,7 +262,7 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if(!config.disableStats && !navigator.mozGetUserMedia) {
|
||||
if (!config.disableStats && browserSupported) {
|
||||
this.statsIntervalId = setInterval(
|
||||
function () {
|
||||
// Interval updates
|
||||
@@ -296,7 +296,8 @@ StatsCollector.prototype.start = function ()
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logStats && !navigator.mozGetUserMedia) {
|
||||
// Logging statistics does not support firefox
|
||||
if (config.logStats && (browserSupported && !RTCBrowserType.isFirefox())) {
|
||||
this.gatherStatsIntervalId = setInterval(
|
||||
function () {
|
||||
self.peerconnection.getStats(
|
||||
@@ -675,41 +676,34 @@ StatsCollector.prototype.processStatsReport = function () {
|
||||
/**
|
||||
* Stats processing logic.
|
||||
*/
|
||||
StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
{
|
||||
if (!this.baselineAudioLevelsReport)
|
||||
{
|
||||
StatsCollector.prototype.processAudioLevelReport = function () {
|
||||
if (!this.baselineAudioLevelsReport) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var idx in this.currentAudioLevelsReport)
|
||||
{
|
||||
for (var idx in this.currentAudioLevelsReport) {
|
||||
var now = this.currentAudioLevelsReport[idx];
|
||||
|
||||
if (now.type != 'ssrc')
|
||||
{
|
||||
if (now.type != 'ssrc') {
|
||||
continue;
|
||||
}
|
||||
|
||||
var before = this.baselineAudioLevelsReport[idx];
|
||||
if (!before)
|
||||
{
|
||||
if (!before) {
|
||||
console.warn(getStatValue(now, 'ssrc') + ' not enough data');
|
||||
continue;
|
||||
}
|
||||
|
||||
var ssrc = getStatValue(now, 'ssrc');
|
||||
var jid = APP.xmpp.getJidFromSSRC(ssrc);
|
||||
if (!jid)
|
||||
{
|
||||
if (!jid) {
|
||||
if((Date.now() - now.timestamp) < 3000)
|
||||
console.warn("No jid for ssrc: " + ssrc);
|
||||
continue;
|
||||
}
|
||||
|
||||
var jidStats = this.jid2stats[jid];
|
||||
if (!jidStats)
|
||||
{
|
||||
if (!jidStats) {
|
||||
jidStats = new PeerStats();
|
||||
this.jid2stats[jid] = jidStats;
|
||||
}
|
||||
@@ -728,8 +722,7 @@ StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioLevel)
|
||||
{
|
||||
if (audioLevel) {
|
||||
// TODO: can't find specs about what this value really is,
|
||||
// but it seems to vary between 0 and around 32k.
|
||||
audioLevel = audioLevel / 32767;
|
||||
@@ -737,8 +730,5 @@ StatsCollector.prototype.processAudioLevelReport = function ()
|
||||
if(jid != APP.xmpp.myJid())
|
||||
this.eventEmitter.emit("statistics.audioLevel", jid, audioLevel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global require, APP */
|
||||
/**
|
||||
* Created by hristo on 8/4/14.
|
||||
*/
|
||||
@@ -15,19 +16,15 @@ var localStats = null;
|
||||
|
||||
var rtpStats = null;
|
||||
|
||||
function stopLocal()
|
||||
{
|
||||
if(localStats)
|
||||
{
|
||||
function stopLocal() {
|
||||
if (localStats) {
|
||||
localStats.stop();
|
||||
localStats = null;
|
||||
}
|
||||
}
|
||||
|
||||
function stopRemote()
|
||||
{
|
||||
if(rtpStats)
|
||||
{
|
||||
function stopRemote() {
|
||||
if (rtpStats) {
|
||||
rtpStats.stop();
|
||||
eventEmitter.emit("statistics.stop");
|
||||
rtpStats = null;
|
||||
@@ -35,20 +32,18 @@ function stopRemote()
|
||||
}
|
||||
|
||||
function startRemoteStats (peerconnection) {
|
||||
if(rtpStats)
|
||||
{
|
||||
if (rtpStats) {
|
||||
rtpStats.stop();
|
||||
rtpStats = null;
|
||||
}
|
||||
|
||||
rtpStats = new RTPStats(peerconnection, 200, 2000, eventEmitter);
|
||||
rtpStats.start();
|
||||
}
|
||||
|
||||
function onStreamCreated(stream)
|
||||
{
|
||||
if(stream.getOriginalStream().getAudioTracks().length === 0)
|
||||
function onStreamCreated(stream) {
|
||||
if(stream.getOriginalStream().getAudioTracks().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStats = new LocalStats(stream.getOriginalStream(), 200, statistics,
|
||||
eventEmitter);
|
||||
@@ -64,9 +59,7 @@ function onDisposeConference(onUnload) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var statistics =
|
||||
{
|
||||
var statistics = {
|
||||
/**
|
||||
* Indicates that this audio level is for local jid.
|
||||
* @type {string}
|
||||
@@ -129,18 +122,17 @@ var statistics =
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.PEERCONNECTION_READY, function (session) {
|
||||
CallStats.init(session);
|
||||
})
|
||||
});
|
||||
APP.RTC.addListener(RTCEvents.AUDIO_MUTE, function (mute) {
|
||||
CallStats.sendMuteEvent(mute, "audio");
|
||||
});
|
||||
APP.xmpp.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () {
|
||||
CallStats.sendSetupFailedEvent();
|
||||
})
|
||||
});
|
||||
APP.RTC.addListener(RTCEvents.VIDEO_MUTE, function (mute) {
|
||||
CallStats.sendMuteEvent(mute, "video");
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* global $, require, config, interfaceConfig */
|
||||
var i18n = require("i18next-client");
|
||||
var languages = require("../../service/translation/languages");
|
||||
var Settings = require("../settings/Settings");
|
||||
@@ -23,7 +24,6 @@ var defaultOptions = {
|
||||
fallbackOnNull: true,
|
||||
fallbackOnEmpty: true,
|
||||
useDataAttrOptions: true,
|
||||
defaultValueFromContent: false,
|
||||
app: interfaceConfig.APP_NAME,
|
||||
getAsync: false,
|
||||
defaultValueFromContent: false,
|
||||
@@ -63,8 +63,7 @@ var defaultOptions = {
|
||||
// localStorageExpirationTime: 86400000 // in ms, default 1 week
|
||||
};
|
||||
|
||||
function initCompleted(t)
|
||||
{
|
||||
function initCompleted(t) {
|
||||
$("[data-i18n]").i18n();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1447
modules/xmpp/JingleSessionPC.js
Normal file
1447
modules/xmpp/JingleSessionPC.js
Normal file
File diff suppressed because it is too large
Load Diff
268
modules/xmpp/LocalSSRCReplacement.js
Normal file
268
modules/xmpp/LocalSSRCReplacement.js
Normal file
@@ -0,0 +1,268 @@
|
||||
/* global $ */
|
||||
|
||||
/*
|
||||
Here we do modifications of local video SSRCs. There are 2 situations we have
|
||||
to handle:
|
||||
|
||||
1. We generate SSRC for local recvonly video stream. This is the case when we
|
||||
have no local camera and it is not generated automatically, but SSRC=1 is
|
||||
used implicitly. If that happens RTCP packets will be dropped by the JVB
|
||||
and we won't be able to request video key frames correctly.
|
||||
|
||||
2. A hack to re-use SSRC of the first video stream for any new stream created
|
||||
in future. It turned out that Chrome may keep on using the SSRC of removed
|
||||
video stream in RTCP even though a new one has been created. So we just
|
||||
want to avoid that by re-using it. Jingle 'source-remove'/'source-add'
|
||||
notifications are blocked once first video SSRC is advertised to the focus.
|
||||
|
||||
What this hack does:
|
||||
|
||||
1. Stores the SSRC of the first video stream created by
|
||||
a) scanning Jingle session-accept/session-invite for existing video SSRC
|
||||
b) watching for 'source-add' for new video stream if it has not been
|
||||
created in step a)
|
||||
2. Exposes method 'mungeLocalVideoSSRC' which replaces any new video SSRC with
|
||||
the stored one. It is called by 'TracablePeerConnection' before local SDP is
|
||||
returned to the other parts of the application.
|
||||
3. Scans 'source-remove'/'source-add' notifications for stored video SSRC and
|
||||
blocks those notifications. This makes Jicofo and all participants think
|
||||
that it exists all the time even if the video stream has been removed or
|
||||
replaced locally. Thanks to that there is no additional signaling activity
|
||||
on video mute or when switching to the desktop stream.
|
||||
*/
|
||||
|
||||
var SDP = require('./SDP');
|
||||
var RTCBrowserType = require('../RTC/RTCBrowserType');
|
||||
|
||||
/**
|
||||
* The hack is enabled on all browsers except FF by default
|
||||
* FIXME finish the hack once removeStream method is implemented in FF
|
||||
* @type {boolean}
|
||||
*/
|
||||
var isEnabled = !RTCBrowserType.isFirefox();
|
||||
|
||||
/**
|
||||
* Stored SSRC of local video stream.
|
||||
*/
|
||||
var localVideoSSRC;
|
||||
|
||||
/**
|
||||
* SSRC used for recvonly video stream when we have no local camera.
|
||||
* This is in order to tell Chrome what SSRC should be used in RTCP requests
|
||||
* instead of 1.
|
||||
*/
|
||||
var localRecvOnlySSRC;
|
||||
|
||||
/**
|
||||
* cname for <tt>localRecvOnlySSRC</tt>
|
||||
*/
|
||||
var localRecvOnlyCName;
|
||||
|
||||
/**
|
||||
* Method removes <source> element which describes <tt>localVideoSSRC</tt>
|
||||
* from given Jingle IQ.
|
||||
* @param modifyIq 'source-add' or 'source-remove' Jingle IQ.
|
||||
* @param actionName display name of the action which will be printed in log
|
||||
* messages.
|
||||
* @returns {*} modified Jingle IQ, so that it does not contain <source> element
|
||||
* corresponding to <tt>localVideoSSRC</tt> or <tt>null</tt> if no
|
||||
* other SSRCs left to be signaled after removing it.
|
||||
*/
|
||||
var filterOutSource = function (modifyIq, actionName) {
|
||||
var modifyIqTree = $(modifyIq.tree());
|
||||
|
||||
if (!localVideoSSRC)
|
||||
return modifyIqTree[0];
|
||||
|
||||
var videoSSRC = modifyIqTree.find(
|
||||
'>jingle>content[name="video"]' +
|
||||
'>description>source[ssrc="' + localVideoSSRC + '"]');
|
||||
|
||||
if (!videoSSRC.length) {
|
||||
return modifyIqTree[0];
|
||||
}
|
||||
|
||||
console.info(
|
||||
'Blocking ' + actionName + ' for local video SSRC: ' + localVideoSSRC);
|
||||
|
||||
videoSSRC.remove();
|
||||
|
||||
// Check if any sources still left to be added/removed
|
||||
if (modifyIqTree.find('>jingle>content>description>source').length) {
|
||||
return modifyIqTree[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans given Jingle IQ for video SSRC and stores it.
|
||||
* @param jingleIq the Jingle IQ to be scanned for video SSRC.
|
||||
*/
|
||||
var storeLocalVideoSSRC = function (jingleIq) {
|
||||
var videoSSRCs =
|
||||
$(jingleIq.tree())
|
||||
.find('>jingle>content[name="video"]>description>source');
|
||||
|
||||
videoSSRCs.each(function (idx, ssrcElem) {
|
||||
if (localVideoSSRC)
|
||||
return;
|
||||
// We consider SSRC real only if it has msid attribute
|
||||
// recvonly streams in FF do not have it as well as local SSRCs
|
||||
// we generate for recvonly streams in Chrome
|
||||
var ssrSel = $(ssrcElem);
|
||||
var msid = ssrSel.find('>parameter[name="msid"]');
|
||||
if (msid.length) {
|
||||
var ssrcVal = ssrSel.attr('ssrc');
|
||||
if (ssrcVal) {
|
||||
localVideoSSRC = ssrcVal;
|
||||
console.info('Stored local video SSRC' +
|
||||
' for future re-use: ' + localVideoSSRC);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates new SSRC for local video recvonly stream.
|
||||
* FIXME what about eventual SSRC collision ?
|
||||
*/
|
||||
function generateRecvonlySSRC() {
|
||||
//
|
||||
localRecvOnlySSRC =
|
||||
Math.random().toString(10).substring(2, 11);
|
||||
localRecvOnlyCName =
|
||||
Math.random().toString(36).substring(2);
|
||||
console.info(
|
||||
"Generated local recvonly SSRC: " + localRecvOnlySSRC +
|
||||
", cname: " + localRecvOnlyCName);
|
||||
}
|
||||
|
||||
var LocalSSRCReplacement = {
|
||||
/**
|
||||
* Method must be called before 'session-initiate' or 'session-invite' is
|
||||
* sent. Scans the IQ for local video SSRC and stores it if detected.
|
||||
*
|
||||
* @param sessionInit our 'session-initiate' or 'session-accept' Jingle IQ
|
||||
* which will be scanned for local video SSRC.
|
||||
*/
|
||||
processSessionInit: function (sessionInit) {
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
if (localVideoSSRC) {
|
||||
console.error("Local SSRC stored already: " + localVideoSSRC);
|
||||
return;
|
||||
}
|
||||
storeLocalVideoSSRC(sessionInit);
|
||||
},
|
||||
/**
|
||||
* If we have local video SSRC stored searched given
|
||||
* <tt>localDescription</tt> for video SSRC and makes sure it is replaced
|
||||
* with the stored one.
|
||||
* @param localDescription local description object that will have local
|
||||
* video SSRC replaced with the stored one
|
||||
* @returns modified <tt>localDescription</tt> object.
|
||||
*/
|
||||
mungeLocalVideoSSRC: function (localDescription) {
|
||||
if (!isEnabled)
|
||||
return localDescription;
|
||||
|
||||
if (!localDescription) {
|
||||
console.warn("localDescription is null or undefined");
|
||||
return localDescription;
|
||||
}
|
||||
|
||||
// IF we have local video SSRC stored make sure it is replaced
|
||||
// with old SSRC
|
||||
if (localVideoSSRC) {
|
||||
var newSdp = new SDP(localDescription.sdp);
|
||||
if (newSdp.media[1].indexOf("a=ssrc:") !== -1 &&
|
||||
!newSdp.containsSSRC(localVideoSSRC)) {
|
||||
// Get new video SSRC
|
||||
var map = newSdp.getMediaSsrcMap();
|
||||
var videoPart = map[1];
|
||||
var videoSSRCs = videoPart.ssrcs;
|
||||
var newSSRC = Object.keys(videoSSRCs)[0];
|
||||
|
||||
console.info(
|
||||
"Replacing new video SSRC: " + newSSRC +
|
||||
" with " + localVideoSSRC);
|
||||
|
||||
localDescription.sdp =
|
||||
newSdp.raw.replace(
|
||||
new RegExp('a=ssrc:' + newSSRC, 'g'),
|
||||
'a=ssrc:' + localVideoSSRC);
|
||||
}
|
||||
} else {
|
||||
// Make sure we have any SSRC for recvonly video stream
|
||||
var sdp = new SDP(localDescription.sdp);
|
||||
|
||||
if (sdp.media[1] && sdp.media[1].indexOf('a=ssrc:') === -1 &&
|
||||
sdp.media[1].indexOf('a=recvonly') !== -1) {
|
||||
|
||||
if (!localRecvOnlySSRC) {
|
||||
generateRecvonlySSRC();
|
||||
}
|
||||
|
||||
console.info('No SSRC in video recvonly stream' +
|
||||
' - adding SSRC: ' + localRecvOnlySSRC);
|
||||
|
||||
sdp.media[1] += 'a=ssrc:' + localRecvOnlySSRC +
|
||||
' cname:' + localRecvOnlyCName + '\r\n';
|
||||
|
||||
localDescription.sdp = sdp.session + sdp.media.join('');
|
||||
}
|
||||
}
|
||||
return localDescription;
|
||||
},
|
||||
/**
|
||||
* Method must be called before 'source-add' notification is sent. In case
|
||||
* we have local video SSRC advertised already it will be removed from the
|
||||
* notification. If no other SSRCs are described by given IQ null will be
|
||||
* returned which means that there is no point in sending the notification.
|
||||
* @param sourceAdd 'source-add' Jingle IQ to be processed
|
||||
* @returns modified 'source-add' IQ which can be sent to the focus or
|
||||
* <tt>null</tt> if no notification shall be sent. It is no longer
|
||||
* a Strophe IQ Builder instance, but DOM element tree.
|
||||
*/
|
||||
processSourceAdd: function (sourceAdd) {
|
||||
if (!isEnabled)
|
||||
return sourceAdd;
|
||||
|
||||
if (!localVideoSSRC) {
|
||||
// Store local SSRC if available
|
||||
storeLocalVideoSSRC(sourceAdd);
|
||||
return sourceAdd;
|
||||
} else {
|
||||
return filterOutSource(sourceAdd, 'source-add');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Method must be called before 'source-remove' notification is sent.
|
||||
* Removes local video SSRC from the notification. If there are no other
|
||||
* SSRCs described in the given IQ <tt>null</tt> will be returned which
|
||||
* means that there is no point in sending the notification.
|
||||
* @param sourceRemove 'source-remove' Jingle IQ to be processed
|
||||
* @returns modified 'source-remove' IQ which can be sent to the focus or
|
||||
* <tt>null</tt> if no notification shall be sent. It is no longer
|
||||
* a Strophe IQ Builder instance, but DOM element tree.
|
||||
*/
|
||||
processSourceRemove: function (sourceRemove) {
|
||||
if (!isEnabled)
|
||||
return sourceRemove;
|
||||
|
||||
return filterOutSource(sourceRemove, 'source-remove');
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns the hack on or off
|
||||
* @param enabled <tt>true</tt> to enable the hack or <tt>false</tt>
|
||||
* to disable it
|
||||
*/
|
||||
setEnabled: function (enabled) {
|
||||
isEnabled = enabled;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = LocalSSRCReplacement;
|
||||
@@ -125,7 +125,7 @@ SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
|
||||
}
|
||||
|
||||
// add content's to a jingle element
|
||||
SDP.prototype.toJingle = function (elem, thecreator, ssrcs, videoType) {
|
||||
SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
|
||||
// console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]);
|
||||
var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
|
||||
var self = this;
|
||||
@@ -258,14 +258,6 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs, videoType) {
|
||||
elem.up();
|
||||
}
|
||||
}
|
||||
// Video type
|
||||
if (videoType && mline.media == "video") {
|
||||
elem.c('ssrc-info',
|
||||
{
|
||||
xmlns: 'http://jitsi.org/jitmeet',
|
||||
'video-type': videoType
|
||||
}).up();
|
||||
}
|
||||
elem.up();
|
||||
|
||||
// XEP-0339 handle ssrc-group attributes
|
||||
|
||||
@@ -106,7 +106,7 @@ SDPDiffer.prototype.getNewMedia = function() {
|
||||
* @param toJid destination Jid
|
||||
* @param isAdd indicates if this is remove or add operation.
|
||||
*/
|
||||
SDPDiffer.prototype.toJingle = function(modify, videoType) {
|
||||
SDPDiffer.prototype.toJingle = function(modify) {
|
||||
var sdpMediaSsrcs = this.getNewMedia();
|
||||
var self = this;
|
||||
|
||||
@@ -141,15 +141,6 @@ SDPDiffer.prototype.toJingle = function(modify, videoType) {
|
||||
}
|
||||
modify.up(); // end of parameter
|
||||
});
|
||||
// indicate video type
|
||||
if (videoType && media.mid == 'video') {
|
||||
modify.c('ssrc-info',
|
||||
{
|
||||
xmlns: 'http://jitsi.org/jitmeet',
|
||||
'video-type': videoType
|
||||
})
|
||||
.up();
|
||||
}
|
||||
modify.up(); // end of source
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
var RTC = require('../RTC/RTC');
|
||||
var RTCBrowserType = require("../RTC/RTCBrowserType.js");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var SSRCReplacement = require("./LocalSSRCReplacement");
|
||||
|
||||
function TraceablePeerConnection(ice_config, constraints, session) {
|
||||
var self = this;
|
||||
var RTCPeerconnectionType = null;
|
||||
var RTCPeerConnectionType = null;
|
||||
if (RTCBrowserType.isFirefox()) {
|
||||
RTCPeerconnectionType = mozRTCPeerConnection;
|
||||
RTCPeerConnectionType = mozRTCPeerConnection;
|
||||
} else if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||
RTCPeerconnectionType = RTCPeerConnection;
|
||||
RTCPeerConnectionType = RTCPeerConnection;
|
||||
} else {
|
||||
RTCPeerconnectionType = webkitRTCPeerConnection;
|
||||
RTCPeerConnectionType = webkitRTCPeerConnection;
|
||||
}
|
||||
this.peerconnection = new RTCPeerconnectionType(ice_config, constraints);
|
||||
this.peerconnection = new RTCPeerConnectionType(ice_config, constraints);
|
||||
this.updateLog = [];
|
||||
this.stats = {};
|
||||
this.statsinterval = null;
|
||||
@@ -90,13 +91,13 @@ function TraceablePeerConnection(ice_config, constraints, session) {
|
||||
self.ondatachannel(event);
|
||||
}
|
||||
};
|
||||
if (!navigator.mozGetUserMedia && this.maxstats) {
|
||||
// XXX: do all non-firefox browsers which we support also support this?
|
||||
if (!RTCBrowserType.isFirefox() && this.maxstats) {
|
||||
this.statsinterval = window.setInterval(function() {
|
||||
self.peerconnection.getStats(function(stats) {
|
||||
var results = stats.result();
|
||||
var now = new Date();
|
||||
for (var i = 0; i < results.length; ++i) {
|
||||
//console.log(results[i].type, results[i].id, results[i].names())
|
||||
var now = new Date();
|
||||
results[i].names().forEach(function (name) {
|
||||
var id = results[i].id + '-' + name;
|
||||
if (!self.stats[id]) {
|
||||
@@ -120,9 +121,12 @@ function TraceablePeerConnection(ice_config, constraints, session) {
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dumpSDP = function(description) {
|
||||
/**
|
||||
* Returns a string representation of a SessionDescription object.
|
||||
*/
|
||||
var dumpSDP = function(description) {
|
||||
if (typeof description === 'undefined' || description == null) {
|
||||
return '';
|
||||
}
|
||||
@@ -130,31 +134,109 @@ dumpSDP = function(description) {
|
||||
return 'type: ' + description.type + '\r\n' + description.sdp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a SessionDescription object and returns a "normalized" version.
|
||||
* Currently it only takes care of ordering the a=ssrc lines.
|
||||
*/
|
||||
var normalizePlanB = function(desc) {
|
||||
if (typeof desc !== 'object' || desc === null ||
|
||||
typeof desc.sdp !== 'string') {
|
||||
console.warn('An empty description was passed as an argument.');
|
||||
return desc;
|
||||
}
|
||||
|
||||
var transform = require('sdp-transform');
|
||||
var session = transform.parse(desc.sdp);
|
||||
|
||||
if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
|
||||
Array.isArray(session.media)) {
|
||||
session.media.forEach(function (mLine) {
|
||||
|
||||
// Chrome appears to be picky about the order in which a=ssrc lines
|
||||
// are listed in an m-line when rtx is enabled (and thus there are
|
||||
// a=ssrc-group lines with FID semantics). Specifically if we have
|
||||
// "a=ssrc-group:FID S1 S2" and the "a=ssrc:S2" lines appear before
|
||||
// the "a=ssrc:S1" lines, SRD fails.
|
||||
// So, put SSRC which appear as the first SSRC in an FID ssrc-group
|
||||
// first.
|
||||
var firstSsrcs = [];
|
||||
var newSsrcLines = [];
|
||||
|
||||
if (typeof mLine.ssrcGroups !== 'undefined' && Array.isArray(mLine.ssrcGroups)) {
|
||||
mLine.ssrcGroups.forEach(function (group) {
|
||||
if (typeof group.semantics !== 'undefined' &&
|
||||
group.semantics === 'FID') {
|
||||
if (typeof group.ssrcs !== 'undefined') {
|
||||
firstSsrcs.push(Number(group.ssrcs.split(' ')[0]));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
|
||||
for (var i = 0; i<mLine.ssrcs.length; i++){
|
||||
if (typeof mLine.ssrcs[i] === 'object'
|
||||
&& typeof mLine.ssrcs[i].id !== 'undefined'
|
||||
&& $.inArray(mLine.ssrcs[i].id, firstSsrcs) == 0) {
|
||||
newSsrcLines.push(mLine.ssrcs[i]);
|
||||
delete mLine.ssrcs[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i<mLine.ssrcs.length; i++){
|
||||
if (typeof mLine.ssrcs[i] !== 'undefined') {
|
||||
newSsrcLines.push(mLine.ssrcs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mLine.ssrcs = newSsrcLines;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var resStr = transform.write(session);
|
||||
return new RTCSessionDescription({
|
||||
type: desc.type,
|
||||
sdp: resStr
|
||||
});
|
||||
};
|
||||
|
||||
if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
|
||||
TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
|
||||
var desc = this.peerconnection.localDescription;
|
||||
this.trace('getLocalDescription::preTransform', dumpSDP(desc));
|
||||
TraceablePeerConnection.prototype.__defineGetter__(
|
||||
'signalingState',
|
||||
function() { return this.peerconnection.signalingState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__(
|
||||
'iceConnectionState',
|
||||
function() { return this.peerconnection.iceConnectionState; });
|
||||
TraceablePeerConnection.prototype.__defineGetter__(
|
||||
'localDescription',
|
||||
function() {
|
||||
var desc = this.peerconnection.localDescription;
|
||||
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
}
|
||||
return desc;
|
||||
});
|
||||
TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() {
|
||||
var desc = this.peerconnection.remoteDescription;
|
||||
this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
|
||||
desc = SSRCReplacement.mungeLocalVideoSSRC(desc);
|
||||
|
||||
this.trace('getLocalDescription::preTransform', dumpSDP(desc));
|
||||
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
}
|
||||
return desc;
|
||||
});
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
this.trace('getLocalDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
}
|
||||
return desc;
|
||||
});
|
||||
TraceablePeerConnection.prototype.__defineGetter__(
|
||||
'remoteDescription',
|
||||
function() {
|
||||
var desc = this.peerconnection.remoteDescription;
|
||||
this.trace('getRemoteDescription::preTransform', dumpSDP(desc));
|
||||
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
desc = this.interop.toPlanB(desc);
|
||||
this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));
|
||||
}
|
||||
return desc;
|
||||
});
|
||||
}
|
||||
|
||||
TraceablePeerConnection.prototype.addStream = function (stream) {
|
||||
@@ -166,7 +248,6 @@ TraceablePeerConnection.prototype.addStream = function (stream) {
|
||||
catch (e)
|
||||
{
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -204,10 +285,11 @@ TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
|
||||
return this.peerconnection.createDataChannel(label, opts);
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
|
||||
TraceablePeerConnection.prototype.setLocalDescription
|
||||
= function (description, successCallback, failureCallback) {
|
||||
this.trace('setLocalDescription::preTransform', dumpSDP(description));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
this.trace('setLocalDescription::postTransform (Plan A)', dumpSDP(description));
|
||||
}
|
||||
@@ -230,17 +312,23 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
|
||||
*/
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
|
||||
TraceablePeerConnection.prototype.setRemoteDescription
|
||||
= function (description, successCallback, failureCallback) {
|
||||
this.trace('setRemoteDescription::preTransform', dumpSDP(description));
|
||||
// TODO the focus should squeze or explode the remote simulcast
|
||||
description = this.simulcast.mungeRemoteDescription(description);
|
||||
this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(description));
|
||||
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
description = this.interop.toUnifiedPlan(description);
|
||||
this.trace('setRemoteDescription::postTransform (Plan A)', dumpSDP(description));
|
||||
}
|
||||
|
||||
if (RTCBrowserType.usesPlanB()) {
|
||||
description = normalizePlanB(description);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.peerconnection.setRemoteDescription(description,
|
||||
function () {
|
||||
@@ -268,20 +356,24 @@ TraceablePeerConnection.prototype.close = function () {
|
||||
this.peerconnection.close();
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
|
||||
TraceablePeerConnection.prototype.createOffer
|
||||
= function (successCallback, failureCallback, constraints) {
|
||||
var self = this;
|
||||
this.trace('createOffer', JSON.stringify(constraints, null, ' '));
|
||||
this.peerconnection.createOffer(
|
||||
function (offer) {
|
||||
self.trace('createOfferOnSuccess::preTransform', dumpSDP(offer));
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
// NOTE this is not tested because in meet the focus generates the
|
||||
// offer.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
|
||||
// if we're running on FF, transform to Plan B first.
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
offer = self.interop.toPlanB(offer);
|
||||
self.trace('createOfferOnSuccess::postTransform (Plan B)', dumpSDP(offer));
|
||||
}
|
||||
|
||||
offer = SSRCReplacement.mungeLocalVideoSSRC(offer);
|
||||
|
||||
if (config.enableSimulcast && self.simulcast.isSupported()) {
|
||||
offer = self.simulcast.mungeLocalDescription(offer);
|
||||
self.trace('createOfferOnSuccess::postTransform (simulcast)', dumpSDP(offer));
|
||||
@@ -296,20 +388,25 @@ TraceablePeerConnection.prototype.createOffer = function (successCallback, failu
|
||||
);
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
|
||||
TraceablePeerConnection.prototype.createAnswer
|
||||
= function (successCallback, failureCallback, constraints) {
|
||||
var self = this;
|
||||
this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
|
||||
this.peerconnection.createAnswer(
|
||||
function (answer) {
|
||||
self.trace('createAnswerOnSuccess::preTransfom', dumpSDP(answer));
|
||||
self.trace('createAnswerOnSuccess::preTransform', dumpSDP(answer));
|
||||
// if we're running on FF, transform to Plan A first.
|
||||
if (navigator.mozGetUserMedia) {
|
||||
if (RTCBrowserType.usesUnifiedPlan()) {
|
||||
answer = self.interop.toPlanB(answer);
|
||||
self.trace('createAnswerOnSuccess::postTransfom (Plan B)', dumpSDP(answer));
|
||||
self.trace('createAnswerOnSuccess::postTransform (Plan B)', dumpSDP(answer));
|
||||
}
|
||||
|
||||
// munge local video SSRC
|
||||
answer = SSRCReplacement.mungeLocalVideoSSRC(answer);
|
||||
|
||||
if (config.enableSimulcast && self.simulcast.isSupported()) {
|
||||
answer = self.simulcast.mungeLocalDescription(answer);
|
||||
self.trace('createAnswerOnSuccess::postTransfom (simulcast)', dumpSDP(answer));
|
||||
self.trace('createAnswerOnSuccess::postTransform (simulcast)', dumpSDP(answer));
|
||||
}
|
||||
successCallback(answer);
|
||||
},
|
||||
@@ -321,8 +418,9 @@ TraceablePeerConnection.prototype.createAnswer = function (successCallback, fail
|
||||
);
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
|
||||
var self = this;
|
||||
TraceablePeerConnection.prototype.addIceCandidate
|
||||
= function (candidate, successCallback, failureCallback) {
|
||||
//var self = this;
|
||||
this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
|
||||
this.peerconnection.addIceCandidate(candidate);
|
||||
/* maybe later
|
||||
@@ -340,13 +438,12 @@ TraceablePeerConnection.prototype.addIceCandidate = function (candidate, success
|
||||
};
|
||||
|
||||
TraceablePeerConnection.prototype.getStats = function(callback, errback) {
|
||||
if (navigator.mozGetUserMedia) {
|
||||
// TODO: Is this the correct way to handle Opera, Temasys?
|
||||
if (RTCBrowserType.isFirefox()) {
|
||||
// ignore for now...
|
||||
if(!errback)
|
||||
errback = function () {
|
||||
|
||||
}
|
||||
this.peerconnection.getStats(null,callback,errback);
|
||||
errback = function () {};
|
||||
this.peerconnection.getStats(null, callback, errback);
|
||||
} else {
|
||||
this.peerconnection.getStats(callback);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, $iq, APP, config, connection, UI, messageHandler,
|
||||
/* global $, $iq, APP, config, messageHandler,
|
||||
roomName, sessionTerminated, Strophe, Util */
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var Settings = require("../settings/Settings");
|
||||
@@ -35,7 +35,7 @@ var externalAuthEnabled = false;
|
||||
// Sip gateway can be enabled by configuring Jigasi host in config.js or
|
||||
// it will be enabled automatically if focus detects the component through
|
||||
// service discovery.
|
||||
var sipGatewayEnabled = config.hosts.call_control !== undefined;
|
||||
var sipGatewayEnabled;
|
||||
|
||||
var eventEmitter = null;
|
||||
|
||||
@@ -65,12 +65,15 @@ var Moderator = {
|
||||
this.xmppService = xmpp;
|
||||
eventEmitter = emitter;
|
||||
|
||||
sipGatewayEnabled =
|
||||
config.hosts && config.hosts.call_control !== undefined;
|
||||
|
||||
// Message listener that talks to POPUP window
|
||||
function listener(event) {
|
||||
if (event.data && event.data.sessionId) {
|
||||
if (event.origin !== window.location.origin) {
|
||||
console.warn(
|
||||
"Ignoring sessionId from different origin: " + event.origin);
|
||||
console.warn("Ignoring sessionId from different origin: " +
|
||||
event.origin);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('sessionId', event.data.sessionId);
|
||||
@@ -219,8 +222,7 @@ var Moderator = {
|
||||
|
||||
console.info("Authentication enabled: " + authenticationEnabled);
|
||||
|
||||
externalAuthEnabled
|
||||
= $(resultIq).find(
|
||||
externalAuthEnabled = $(resultIq).find(
|
||||
'>conference>property' +
|
||||
'[name=\'externalAuth\'][value=\'true\']').length > 0;
|
||||
|
||||
@@ -333,10 +335,8 @@ var Moderator = {
|
||||
// Do not show in case of session invalid
|
||||
// which means just a retry
|
||||
if (!invalidSession) {
|
||||
APP.UI.messageHandler.notify(
|
||||
null, "notify.focus",
|
||||
'disconnected', "notify.focusFail",
|
||||
{component: focusComponent, ms: retrySec});
|
||||
eventEmitter.emit(XMPPEvents.FOCUS_DISCONNECTED,
|
||||
focusComponent, retrySec);
|
||||
}
|
||||
// Reset response timeout
|
||||
getNextTimeout(true);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, $iq, config, connection, focusMucJid, messageHandler, Moderator,
|
||||
/* global $, $iq, config, connection, focusMucJid, messageHandler,
|
||||
Toolbar, Util */
|
||||
var Moderator = require("./moderator");
|
||||
|
||||
@@ -10,7 +10,7 @@ var recordingEnabled;
|
||||
* Whether to use a jirecon component for recording, or use the videobridge
|
||||
* through COLIBRI.
|
||||
*/
|
||||
var useJirecon = (typeof config.hosts.jirecon != "undefined");
|
||||
var useJirecon;
|
||||
|
||||
/**
|
||||
* The ID of the jirecon recording session. Jirecon generates it when we
|
||||
@@ -19,18 +19,16 @@ var useJirecon = (typeof config.hosts.jirecon != "undefined");
|
||||
*/
|
||||
var jireconRid = null;
|
||||
|
||||
/**
|
||||
* The callback to update the recording button. Currently used from colibri
|
||||
* after receiving a pending status.
|
||||
*/
|
||||
var recordingStateChangeCallback = null;
|
||||
|
||||
function setRecordingToken(token) {
|
||||
recordingToken = token;
|
||||
}
|
||||
|
||||
function setRecording(state, token, callback, connection) {
|
||||
if (useJirecon){
|
||||
setRecordingJirecon(state, token, callback, connection);
|
||||
} else {
|
||||
setRecordingColibri(state, token, callback, connection);
|
||||
}
|
||||
}
|
||||
|
||||
function setRecordingJirecon(state, token, callback, connection) {
|
||||
if (state == recordingEnabled){
|
||||
return;
|
||||
@@ -38,9 +36,9 @@ function setRecordingJirecon(state, token, callback, connection) {
|
||||
|
||||
var iq = $iq({to: config.hosts.jirecon, type: 'set'})
|
||||
.c('recording', {xmlns: 'http://jitsi.org/protocol/jirecon',
|
||||
action: state ? 'start' : 'stop',
|
||||
action: (state === 'on') ? 'start' : 'stop',
|
||||
mucjid: connection.emuc.roomjid});
|
||||
if (!state){
|
||||
if (state === 'off'){
|
||||
iq.attrs({rid: jireconRid});
|
||||
}
|
||||
|
||||
@@ -52,10 +50,10 @@ function setRecordingJirecon(state, token, callback, connection) {
|
||||
// TODO wait for an IQ with the real status, since this is
|
||||
// provisional?
|
||||
jireconRid = $(result).find('recording').attr('rid');
|
||||
console.log('Recording ' + (state ? 'started' : 'stopped') +
|
||||
console.log('Recording ' + ((state === 'on') ? 'started' : 'stopped') +
|
||||
'(jirecon)' + result);
|
||||
recordingEnabled = state;
|
||||
if (!state){
|
||||
if (state === 'off'){
|
||||
jireconRid = null;
|
||||
}
|
||||
|
||||
@@ -81,10 +79,19 @@ function setRecordingColibri(state, token, callback, connection) {
|
||||
function (result) {
|
||||
console.log('Set recording "', state, '". Result:', result);
|
||||
var recordingElem = $(result).find('>conference>recording');
|
||||
var newState = ('true' === recordingElem.attr('state'));
|
||||
var newState = recordingElem.attr('state');
|
||||
|
||||
recordingEnabled = newState;
|
||||
callback(newState);
|
||||
|
||||
if (newState === 'pending' && recordingStateChangeCallback == null) {
|
||||
recordingStateChangeCallback = callback;
|
||||
connection.addHandler(function(iq){
|
||||
var state = $(iq).find('recording').attr('state');
|
||||
if (state)
|
||||
recordingStateChangeCallback(state);
|
||||
}, 'http://jitsi.org/protocol/colibri', 'iq', null, null, null);
|
||||
}
|
||||
},
|
||||
function (error) {
|
||||
console.warn(error);
|
||||
@@ -93,9 +100,20 @@ function setRecordingColibri(state, token, callback, connection) {
|
||||
);
|
||||
}
|
||||
|
||||
function setRecording(state, token, callback, connection) {
|
||||
if (useJirecon){
|
||||
setRecordingJirecon(state, token, callback, connection);
|
||||
} else {
|
||||
setRecordingColibri(state, token, callback, connection);
|
||||
}
|
||||
}
|
||||
|
||||
var Recording = {
|
||||
toggleRecording: function (tokenEmptyCallback,
|
||||
startingCallback, startedCallback, connection) {
|
||||
init: function () {
|
||||
useJirecon = config.hosts &&
|
||||
(typeof config.hosts.jirecon != "undefined");
|
||||
},
|
||||
toggleRecording: function (tokenEmptyCallback, recordingStateChangeCallback, connection) {
|
||||
if (!Moderator.isModerator()) {
|
||||
console.log(
|
||||
'non-focus, or conference not yet organized:' +
|
||||
@@ -108,16 +126,16 @@ var Recording = {
|
||||
if (!recordingToken && !useJirecon) {
|
||||
tokenEmptyCallback(function (value) {
|
||||
setRecordingToken(value);
|
||||
self.toggleRecording(tokenEmptyCallback,
|
||||
startingCallback, startedCallback, connection);
|
||||
self.toggleRecording(tokenEmptyCallback, recordingStateChangeCallback, connection);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var oldState = recordingEnabled;
|
||||
startingCallback(!oldState);
|
||||
setRecording(!oldState,
|
||||
var newState = (oldState === 'off' || !oldState) ? 'on' : 'off';
|
||||
|
||||
setRecording(newState,
|
||||
recordingToken,
|
||||
function (state) {
|
||||
console.log("New recording state: ", state);
|
||||
@@ -143,13 +161,13 @@ var Recording = {
|
||||
// have been wrong
|
||||
setRecordingToken(null);
|
||||
}
|
||||
startedCallback(state);
|
||||
recordingStateChangeCallback(state);
|
||||
|
||||
},
|
||||
connection
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Recording;
|
||||
@@ -4,9 +4,6 @@
|
||||
*/
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var Moderator = require("./moderator");
|
||||
var JingleSession = require("./JingleSession");
|
||||
|
||||
var bridgeIsDown = false;
|
||||
|
||||
module.exports = function(XMPP, eventEmitter) {
|
||||
Strophe.addConnectionPlugin('emuc', {
|
||||
@@ -21,18 +18,17 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
isOwner: false,
|
||||
role: null,
|
||||
focusMucJid: null,
|
||||
bridgeIsDown: false,
|
||||
init: function (conn) {
|
||||
this.connection = conn;
|
||||
},
|
||||
initPresenceMap: function (myroomjid) {
|
||||
this.presMap['to'] = myroomjid;
|
||||
this.presMap['xns'] = 'http://jabber.org/protocol/muc';
|
||||
if (APP.RTC.localAudio && APP.RTC.localAudio.isMuted())
|
||||
{
|
||||
if (APP.RTC.localAudio && APP.RTC.localAudio.isMuted()) {
|
||||
this.addAudioInfoToPresence(true);
|
||||
}
|
||||
if (APP.RTC.localVideo && APP.RTC.localVideo.isMuted())
|
||||
{
|
||||
if (APP.RTC.localVideo && APP.RTC.localVideo.isMuted()) {
|
||||
this.addVideoInfoToPresence(true);
|
||||
}
|
||||
},
|
||||
@@ -115,10 +111,11 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
}
|
||||
}
|
||||
|
||||
var url;
|
||||
// Parse prezi tag.
|
||||
var presentation = $(pres).find('>prezi');
|
||||
if (presentation.length) {
|
||||
var url = presentation.attr('url');
|
||||
url = presentation.attr('url');
|
||||
var current = presentation.find('>current').text();
|
||||
|
||||
console.log('presentation info received from', from, url);
|
||||
@@ -133,7 +130,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
}
|
||||
}
|
||||
else if (this.preziMap[from] != null) {
|
||||
var url = this.preziMap[from];
|
||||
url = this.preziMap[from];
|
||||
delete this.preziMap[from];
|
||||
$(document).trigger('presentationremoved.muc', [from, url]);
|
||||
}
|
||||
@@ -141,22 +138,22 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
// Parse audio info tag.
|
||||
var audioMuted = $(pres).find('>audiomuted');
|
||||
if (audioMuted.length) {
|
||||
eventEmitter.emit(XMPPEvents.AUDIO_MUTED,
|
||||
eventEmitter.emit(XMPPEvents.PARTICIPANT_AUDIO_MUTED,
|
||||
from, (audioMuted.text() === "true"));
|
||||
}
|
||||
|
||||
// Parse video info tag.
|
||||
var videoMuted = $(pres).find('>videomuted');
|
||||
if (videoMuted.length) {
|
||||
eventEmitter.emit(XMPPEvents.VIDEO_MUTED,
|
||||
eventEmitter.emit(XMPPEvents.PARTICIPANT_VIDEO_MUTED,
|
||||
from, (videoMuted.text() === "true"));
|
||||
}
|
||||
|
||||
var startMuted = $(pres).find('>startmuted');
|
||||
if (startMuted.length)
|
||||
{
|
||||
eventEmitter.emit(XMPPEvents.START_MUTED,
|
||||
startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
|
||||
if (startMuted.length && Moderator.isPeerModerator(from)) {
|
||||
eventEmitter.emit(XMPPEvents.START_MUTED_SETTING_CHANGED,
|
||||
startMuted.attr("audio") === "true",
|
||||
startMuted.attr("video") === "true");
|
||||
}
|
||||
|
||||
var devices = $(pres).find('>devices');
|
||||
@@ -178,6 +175,16 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
Strophe.getResourceFromJid(from), devicesValues);
|
||||
}
|
||||
|
||||
var videoType = $(pres).find('>videoType');
|
||||
if (videoType.length)
|
||||
{
|
||||
if (videoType.text().length)
|
||||
{
|
||||
eventEmitter.emit(XMPPEvents.PARTICIPANT_VIDEO_TYPE_CHANGED,
|
||||
Strophe.getResourceFromJid(from), videoType.text());
|
||||
}
|
||||
}
|
||||
|
||||
var stats = $(pres).find('>stats');
|
||||
if (stats.length) {
|
||||
var statsObj = {};
|
||||
@@ -210,7 +217,7 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
}
|
||||
|
||||
var nicktag = $(pres).find('>nick[xmlns="http://jabber.org/protocol/nick"]');
|
||||
member.displayName = (nicktag.length > 0 ? nicktag.html() : null);
|
||||
member.displayName = (nicktag.length > 0 ? nicktag.text() : null);
|
||||
|
||||
if (from == this.myroomjid) {
|
||||
if (member.affiliation == 'owner') this.isOwner = true;
|
||||
@@ -321,19 +328,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
// We're either missing Jicofo/Prosody config for anonymous
|
||||
// domains or something is wrong.
|
||||
// XMPP.promptLogin();
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.joinError", pres);
|
||||
eventEmitter.emit(XMPPEvents.ROOM_JOIN_ERROR, pres);
|
||||
|
||||
} else {
|
||||
console.warn('onPresError ', pres);
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.connectError",
|
||||
pres);
|
||||
eventEmitter.emit(XMPPEvents.ROOM_CONNECT_ERROR, pres);
|
||||
}
|
||||
} else {
|
||||
console.warn('onPresError ', pres);
|
||||
APP.UI.messageHandler.openReportDialog(null,
|
||||
"dialog.connectError",
|
||||
pres);
|
||||
eventEmitter.emit(XMPPEvents.ROOM_CONNECT_ERROR, pres);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -490,6 +493,11 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
.t(this.presMap['videomuted']).up();
|
||||
}
|
||||
|
||||
if (this.presMap['videoTypeNs']) {
|
||||
pres.c('videoType', { xmlns: this.presMap['videoTypeNs'] })
|
||||
.t(this.presMap['videoType']).up();
|
||||
}
|
||||
|
||||
if (this.presMap['statsns']) {
|
||||
var stats = pres.c('stats', {xmlns: this.presMap['statsns']});
|
||||
for (var stat in this.presMap["stats"])
|
||||
@@ -522,6 +530,14 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
addDevicesToPresence: function (devices) {
|
||||
this.presMap['devices'] = devices;
|
||||
},
|
||||
/**
|
||||
* Adds the info about the type of our video stream.
|
||||
* @param videoType 'camera' or 'screen'
|
||||
*/
|
||||
addVideoTypeToPresence: function (videoType) {
|
||||
this.presMap['videoTypeNs'] = 'http://jitsi.org/jitmeet/video';
|
||||
this.presMap['videoType'] = videoType;
|
||||
},
|
||||
addPreziToPresence: function (url, currentSlide) {
|
||||
this.presMap['prezins'] = 'http://jitsi.org/jitmeet/prezi';
|
||||
this.presMap['preziurl'] = url;
|
||||
@@ -596,27 +612,25 @@ module.exports = function(XMPP, eventEmitter) {
|
||||
|
||||
Moderator.onMucMemberLeft(jid);
|
||||
},
|
||||
parsePresence: function (from, memeber, pres) {
|
||||
if($(pres).find(">bridgeIsDown").length > 0 && !bridgeIsDown) {
|
||||
bridgeIsDown = true;
|
||||
parsePresence: function (from, member, pres) {
|
||||
if($(pres).find(">bridgeIsDown").length > 0 && !this.bridgeIsDown) {
|
||||
this.bridgeIsDown = true;
|
||||
eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);
|
||||
}
|
||||
|
||||
if(memeber.isFocus)
|
||||
if(member.isFocus)
|
||||
return;
|
||||
|
||||
var displayName = !config.displayJids
|
||||
? memeber.displayName : Strophe.getResourceFromJid(from);
|
||||
? member.displayName : Strophe.getResourceFromJid(from);
|
||||
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
if (displayName && displayName.length > 0) {
|
||||
eventEmitter.emit(XMPPEvents.DISPLAY_NAME_CHANGED, from, displayName);
|
||||
}
|
||||
|
||||
|
||||
var id = $(pres).find('>userID').text();
|
||||
var email = $(pres).find('>email');
|
||||
if(email.length > 0) {
|
||||
if (email.length > 0) {
|
||||
id = email.text();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
/* jshint -W117 */
|
||||
|
||||
var JingleSession = require("./JingleSession");
|
||||
var JingleSession = require("./JingleSessionPC");
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
var RTCBrowserType = require("../RTC/RTCBrowserType");
|
||||
|
||||
|
||||
module.exports = function(XMPP, eventEmitter)
|
||||
{
|
||||
function CallIncomingJingle(sid, connection) {
|
||||
var sess = connection.jingle.sessions[sid];
|
||||
|
||||
// TODO: do we check activecall == null?
|
||||
connection.jingle.activecall = sess;
|
||||
|
||||
eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
|
||||
|
||||
// TODO: check affiliation and/or role
|
||||
console.log('emuc data for', sess.peerjid, connection.emuc.members[sess.peerjid]);
|
||||
sess.usedrip = true; // not-so-naive trickle ice
|
||||
sess.sendAnswer();
|
||||
sess.accept();
|
||||
|
||||
};
|
||||
|
||||
module.exports = function(XMPP, eventEmitter) {
|
||||
Strophe.addConnectionPlugin('jingle', {
|
||||
connection: null,
|
||||
sessions: {},
|
||||
@@ -55,15 +38,13 @@ module.exports = function(XMPP, eventEmitter)
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:4588');
|
||||
}
|
||||
|
||||
// this is dealt with by SDP O/A so we don't need to annouce this
|
||||
// this is dealt with by SDP O/A so we don't need to announce this
|
||||
//this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0'); // XEP-0293
|
||||
//this.connection.disco.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0'); // XEP-0294
|
||||
if (config.useRtcpMux) {
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
|
||||
}
|
||||
if (config.useBundle) {
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
|
||||
}
|
||||
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5761'); // rtcp-mux
|
||||
this.connection.disco.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle
|
||||
|
||||
//this.connection.disco.addFeature('urn:ietf:rfc:5576'); // a=ssrc
|
||||
}
|
||||
this.connection.addHandler(this.onJingle.bind(this), 'urn:xmpp:jingle:1', 'iq', 'set', null, null);
|
||||
@@ -88,9 +69,8 @@ module.exports = function(XMPP, eventEmitter)
|
||||
this.connection.send(ack);
|
||||
return true;
|
||||
}
|
||||
// compare from to sess.peerjid (bare jid comparison for later compat with message-mode)
|
||||
// local jid is not checked
|
||||
if (Strophe.getBareJidFromJid(fromJid) != Strophe.getBareJidFromJid(sess.peerjid)) {
|
||||
if (fromJid != sess.peerjid) {
|
||||
console.warn('jid mismatch for session id', sid, fromJid, sess.peerjid);
|
||||
ack.type = 'error';
|
||||
ack.c('error', {type: 'cancel'})
|
||||
@@ -115,12 +95,11 @@ module.exports = function(XMPP, eventEmitter)
|
||||
switch (action) {
|
||||
case 'session-initiate':
|
||||
var startMuted = $(iq).find('jingle>startmuted');
|
||||
if(startMuted && startMuted.length > 0)
|
||||
{
|
||||
if (startMuted && startMuted.length > 0) {
|
||||
var audioMuted = startMuted.attr("audio");
|
||||
var videoMuted = startMuted.attr("video");
|
||||
APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
|
||||
(videoMuted === "true"));
|
||||
eventEmitter.emit(XMPPEvents.START_MUTED_FROM_FOCUS,
|
||||
audioMuted === "true", videoMuted === "true");
|
||||
}
|
||||
sess = new JingleSession(
|
||||
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
||||
@@ -131,20 +110,30 @@ module.exports = function(XMPP, eventEmitter)
|
||||
sess.pc_constraints = this.pc_constraints;
|
||||
sess.ice_config = this.ice_config;
|
||||
|
||||
sess.initiate(fromJid, false);
|
||||
sess.initialize(fromJid, false);
|
||||
// FIXME: setRemoteDescription should only be done when this call is to be accepted
|
||||
sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
|
||||
sess.setOffer($(iq).find('>jingle'));
|
||||
|
||||
this.sessions[sess.sid] = sess;
|
||||
this.jid2session[sess.peerjid] = sess;
|
||||
|
||||
// the callback should either
|
||||
// .sendAnswer and .accept
|
||||
// or .sendTerminate -- not necessarily synchronus
|
||||
CallIncomingJingle(sess.sid, this.connection);
|
||||
// or .sendTerminate -- not necessarily synchronous
|
||||
|
||||
// TODO: do we check activecall == null?
|
||||
this.connection.jingle.activecall = sess;
|
||||
|
||||
eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
|
||||
|
||||
// TODO: check affiliation and/or role
|
||||
console.log('emuc data for', sess.peerjid,
|
||||
this.connection.emuc.members[sess.peerjid]);
|
||||
sess.sendAnswer();
|
||||
sess.accept();
|
||||
break;
|
||||
case 'session-accept':
|
||||
sess.setRemoteDescription($(iq).find('>jingle'), 'answer');
|
||||
sess.setAnswer($(iq).find('>jingle'));
|
||||
sess.accept();
|
||||
$(document).trigger('callaccepted.jingle', [sess.sid]);
|
||||
break;
|
||||
@@ -211,7 +200,7 @@ module.exports = function(XMPP, eventEmitter)
|
||||
sess.pc_constraints = this.pc_constraints;
|
||||
sess.ice_config = this.ice_config;
|
||||
|
||||
sess.initiate(peerjid, true);
|
||||
sess.initialize(peerjid, true);
|
||||
this.sessions[sess.sid] = sess;
|
||||
this.jid2session[sess.peerjid] = sess;
|
||||
sess.sendOffer();
|
||||
@@ -329,9 +318,9 @@ module.exports = function(XMPP, eventEmitter)
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates the log data
|
||||
* Returns the data saved in 'updateLog' in a format to be logged.
|
||||
*/
|
||||
populateData: function () {
|
||||
getLog: function () {
|
||||
var data = {};
|
||||
var self = this;
|
||||
Object.keys(this.sessions).forEach(function (sid) {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
/**
|
||||
* Moderate connection plugin.
|
||||
*/
|
||||
module.exports = function (XMPP) {
|
||||
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
|
||||
|
||||
module.exports = function (XMPP, eventEmitter) {
|
||||
Strophe.addConnectionPlugin('moderate', {
|
||||
connection: null,
|
||||
init: function (conn) {
|
||||
@@ -44,7 +46,7 @@ module.exports = function (XMPP) {
|
||||
var mute = $(iq).find('mute');
|
||||
if (mute.length) {
|
||||
var doMuteAudio = mute.text() === "true";
|
||||
APP.UI.setAudioMuted(doMuteAudio);
|
||||
eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, doMuteAudio);
|
||||
XMPP.forceMuted = doMuteAudio;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -7,7 +7,6 @@ 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');
|
||||
|
||||
@@ -27,9 +26,6 @@ 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
|
||||
@@ -132,8 +128,8 @@ function connect(jid, password) {
|
||||
// if we get disconnected from the XMPP server permanently.
|
||||
|
||||
// If the connection failed, retry.
|
||||
if (connectionFailed
|
||||
&& faultTolerantConnect.retry("connection-failed")) {
|
||||
if (connectionFailed &&
|
||||
faultTolerantConnect.retry("connection-failed")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,41 +148,53 @@ function connect(jid, password) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
function maybeDoJoin() {
|
||||
if (connection && connection.connected &&
|
||||
Strophe.getResourceFromJid(connection.jid)
|
||||
&& (APP.RTC.localAudio || APP.RTC.localVideo)) {
|
||||
Strophe.getResourceFromJid(connection.jid) &&
|
||||
(APP.RTC.localAudio || APP.RTC.localVideo)) {
|
||||
// .connected is true while connecting?
|
||||
doJoin();
|
||||
}
|
||||
}
|
||||
|
||||
function doJoin() {
|
||||
var roomName = APP.UI.generateRoomName();
|
||||
|
||||
Moderator.allocateConferenceFocus(
|
||||
roomName, APP.UI.checkForNicknameAndJoin);
|
||||
eventEmitter.emit(XMPPEvents.READY_TO_JOIN);
|
||||
}
|
||||
|
||||
function initStrophePlugins()
|
||||
{
|
||||
require("./strophe.emuc")(XMPP, eventEmitter);
|
||||
require("./strophe.jingle")(XMPP, eventEmitter);
|
||||
require("./strophe.moderate")(XMPP);
|
||||
require("./strophe.moderate")(XMPP, eventEmitter);
|
||||
require("./strophe.util")();
|
||||
require("./strophe.rayo")();
|
||||
require("./strophe.logger")();
|
||||
}
|
||||
|
||||
/**
|
||||
* If given <tt>localStream</tt> is video one this method will advertise it's
|
||||
* video type in MUC presence.
|
||||
* @param localStream new or modified <tt>LocalStream</tt>.
|
||||
*/
|
||||
function broadcastLocalVideoType(localStream) {
|
||||
if (localStream.videoType)
|
||||
XMPP.addToPresence('videoType', localStream.videoType);
|
||||
}
|
||||
|
||||
function registerListeners() {
|
||||
APP.RTC.addStreamListener(maybeDoJoin,
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
|
||||
APP.RTC.addStreamListener(
|
||||
function (localStream) {
|
||||
maybeDoJoin();
|
||||
broadcastLocalVideoType(localStream);
|
||||
},
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CREATED
|
||||
);
|
||||
APP.RTC.addStreamListener(
|
||||
broadcastLocalVideoType,
|
||||
StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED
|
||||
);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -205,7 +213,8 @@ var unload = (function () {
|
||||
async: false,
|
||||
cache: false,
|
||||
contentType: 'application/xml',
|
||||
data: "<body rid='" + (connection.rid || connection._proto.rid) +
|
||||
data: "<body rid='" +
|
||||
(connection.rid || connection._proto.rid) +
|
||||
"' xmlns='http://jabber.org/protocol/httpbind' sid='" +
|
||||
(connection.sid || connection._proto.sid) +
|
||||
"' type='terminate'>" +
|
||||
@@ -231,7 +240,7 @@ function setupEvents() {
|
||||
// (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'
|
||||
// 'beforeunload' event seems to guarantee the execution of the 'unload'
|
||||
// method at least once.
|
||||
//
|
||||
// The 'unload' method can safely be run multiple times, it will actually do
|
||||
@@ -261,6 +270,7 @@ var XMPP = {
|
||||
initStrophePlugins();
|
||||
registerListeners();
|
||||
Moderator.init(this, eventEmitter);
|
||||
Recording.init();
|
||||
var configDomain = config.hosts.anonymousdomain || config.hosts.domain;
|
||||
// Force authenticated domain if room is appended with '?login=true'
|
||||
if (config.hosts.anonymousdomain &&
|
||||
@@ -279,13 +289,10 @@ var XMPP = {
|
||||
return Strophe.getStatusString(status);
|
||||
},
|
||||
promptLogin: function () {
|
||||
// FIXME: re-use LoginDialog which supports retries
|
||||
APP.UI.showLoginPopup(connect);
|
||||
eventEmitter.emit(XMPPEvents.PROMPT_FOR_LOGIN);
|
||||
},
|
||||
joinRoom: function(roomName, useNicks, nick)
|
||||
{
|
||||
var roomjid;
|
||||
roomjid = roomName;
|
||||
joinRoom: function(roomName, useNicks, nick) {
|
||||
var roomjid = roomName;
|
||||
|
||||
if (useNicks) {
|
||||
if (nick) {
|
||||
@@ -294,7 +301,6 @@ var XMPP = {
|
||||
roomjid += '/' + Strophe.getNodeFromJid(connection.jid);
|
||||
}
|
||||
} else {
|
||||
|
||||
var tmpJid = Strophe.getNodeFromJid(connection.jid);
|
||||
|
||||
if(!authenticatedUser)
|
||||
@@ -331,14 +337,12 @@ var XMPP = {
|
||||
}
|
||||
eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
|
||||
connection.jingle.activecall = null;
|
||||
if(!onUnload)
|
||||
{
|
||||
if (!onUnload) {
|
||||
this.sessionTerminated = true;
|
||||
connection.emuc.doLeave();
|
||||
}
|
||||
},
|
||||
addListener: function(type, listener)
|
||||
{
|
||||
addListener: function(type, listener) {
|
||||
eventEmitter.on(type, listener);
|
||||
},
|
||||
removeListener: function (type, listener) {
|
||||
@@ -406,7 +410,6 @@ var XMPP = {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (this.forceMuted && !mute) {
|
||||
console.info("Asking focus for unmute");
|
||||
connection.moderate.setMute(connection.emuc.myroomjid, mute);
|
||||
@@ -419,16 +422,11 @@ var XMPP = {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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.setMute(!mute);
|
||||
// isMuted is the opposite of audioEnabled
|
||||
APP.RTC.localAudio.setMute(mute);
|
||||
this.sendAudioInfoPresence(mute, callback);
|
||||
return true;
|
||||
},
|
||||
sendAudioInfoPresence: function(mute, callback)
|
||||
{
|
||||
sendAudioInfoPresence: function(mute, callback) {
|
||||
if(connection) {
|
||||
connection.emuc.addAudioInfoToPresence(mute);
|
||||
connection.emuc.sendPresence();
|
||||
@@ -436,56 +434,13 @@ var XMPP = {
|
||||
callback();
|
||||
return true;
|
||||
},
|
||||
// Really mute video, i.e. dont even send black frames
|
||||
muteVideo: function (pc, unmute) {
|
||||
// FIXME: this probably needs another of those lovely state safeguards...
|
||||
// which checks for iceconn == connected and sigstate == stable
|
||||
pc.setRemoteDescription(pc.remoteDescription,
|
||||
function () {
|
||||
pc.createAnswer(
|
||||
function (answer) {
|
||||
var sdp = new SDP(answer.sdp);
|
||||
if (sdp.media.length > 1) {
|
||||
if (unmute)
|
||||
sdp.media[1] = sdp.media[1].replace('a=recvonly', 'a=sendrecv');
|
||||
else
|
||||
sdp.media[1] = sdp.media[1].replace('a=sendrecv', 'a=recvonly');
|
||||
sdp.raw = sdp.session + sdp.media.join('');
|
||||
answer.sdp = sdp.raw;
|
||||
}
|
||||
pc.setLocalDescription(answer,
|
||||
function () {
|
||||
console.log('mute SLD ok');
|
||||
},
|
||||
function (error) {
|
||||
console.log('mute SLD error');
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
"dialog.SLDFailure");
|
||||
}
|
||||
);
|
||||
},
|
||||
function (error) {
|
||||
console.log(error);
|
||||
APP.UI.messageHandler.showError();
|
||||
}
|
||||
);
|
||||
},
|
||||
function (error) {
|
||||
console.log('muteVideo SRD error');
|
||||
APP.UI.messageHandler.showError("dialog.error",
|
||||
"dialog.SRDFailure");
|
||||
|
||||
}
|
||||
);
|
||||
},
|
||||
toggleRecording: function (tokenEmptyCallback,
|
||||
startingCallback, startedCallback) {
|
||||
recordingStateChangeCallback) {
|
||||
Recording.toggleRecording(tokenEmptyCallback,
|
||||
startingCallback, startedCallback, connection);
|
||||
recordingStateChangeCallback, connection);
|
||||
},
|
||||
addToPresence: function (name, value, dontSend) {
|
||||
switch (name)
|
||||
{
|
||||
switch (name) {
|
||||
case "displayName":
|
||||
connection.emuc.addDisplayNameToPresence(value);
|
||||
break;
|
||||
@@ -504,6 +459,9 @@ var XMPP = {
|
||||
case "devices":
|
||||
connection.emuc.addDevicesToPresence(value);
|
||||
break;
|
||||
case "videoType":
|
||||
connection.emuc.addVideoTypeToPresence(value);
|
||||
break;
|
||||
case "startMuted":
|
||||
if(!Moderator.isModerator())
|
||||
return;
|
||||
@@ -547,17 +505,13 @@ var XMPP = {
|
||||
connection.send(message);
|
||||
return true;
|
||||
},
|
||||
populateData: function () {
|
||||
var data = {};
|
||||
if (connection.jingle) {
|
||||
data = connection.jingle.populateData();
|
||||
}
|
||||
return data;
|
||||
// Gets the logs from strophe.jingle.
|
||||
getJingleLog: function () {
|
||||
return connection.jingle ? connection.jingle.getLog() : {};
|
||||
},
|
||||
getLogger: function () {
|
||||
if(connection.logger)
|
||||
return connection.logger.log;
|
||||
return null;
|
||||
// Gets the logs from strophe.
|
||||
getXmppLog: function () {
|
||||
return connection.logger ? connection.logger.log : null;
|
||||
},
|
||||
getPrezi: function () {
|
||||
return connection.emuc.getPrezi(this.myJid());
|
||||
@@ -598,7 +552,8 @@ var XMPP = {
|
||||
return null;
|
||||
return connection.jingle.activecall.getSsrcOwner(ssrc);
|
||||
},
|
||||
getMUCJoined: function () {
|
||||
// Returns true iff we have joined the MUC.
|
||||
isMUCJoined: function () {
|
||||
return connection.emuc.joined;
|
||||
},
|
||||
getSessions: function () {
|
||||
|
||||
@@ -14,17 +14,20 @@
|
||||
],
|
||||
"author": "",
|
||||
"readmeFilename": "README.md",
|
||||
"//": "Callstats.io does not work with recent versions of jsSHA (2.0.1 in particular)",
|
||||
"dependencies": {
|
||||
"events": "*",
|
||||
"pako": "*",
|
||||
"i18next-client": "1.7.7",
|
||||
"sdp-interop": "0.1.4",
|
||||
"sdp-transform": "1.4.0",
|
||||
"sdp-transform": "1.4.1",
|
||||
"sdp-simulcast": "0.1.0",
|
||||
"async": "0.9.0",
|
||||
"retry": "0.6.1"
|
||||
"retry": "0.6.1",
|
||||
"jssha": "1.5.0",
|
||||
"socket.io-client": "1.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"license": "MIT"
|
||||
"license": "Apache"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var RTCEvents = {
|
||||
RTC_READY: "rtc.ready",
|
||||
DATA_CHANNEL_OPEN: "rtc.data_channel_open",
|
||||
LASTN_CHANGED: "rtc.lastn_changed",
|
||||
DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed",
|
||||
LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
|
||||
|
||||
@@ -7,9 +7,7 @@ var StreamEventTypes = {
|
||||
|
||||
EVENT_TYPE_REMOTE_CREATED: "stream.remote_created",
|
||||
|
||||
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended",
|
||||
|
||||
EVENT_TYPE_REMOTE_CHANGED: "stream.changed"
|
||||
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended"
|
||||
};
|
||||
|
||||
module.exports = StreamEventTypes;
|
||||
@@ -1,6 +1,7 @@
|
||||
var UIEvents = {
|
||||
NICKNAME_CHANGED: "UI.nickname_changed",
|
||||
SELECTED_ENDPOINT: "UI.selected_endpoint",
|
||||
PINNED_ENDPOINT: "UI.pinned_endpoint"
|
||||
PINNED_ENDPOINT: "UI.pinned_endpoint",
|
||||
LARGEVIDEO_INIT: "UI.largevideo_init"
|
||||
};
|
||||
module.exports = UIEvents;
|
||||
@@ -1,37 +1,92 @@
|
||||
var XMPPEvents = {
|
||||
// Designates an event indicating that the connection to the XMPP server
|
||||
// failed.
|
||||
CONNECTION_FAILED: "xmpp.connection.failed",
|
||||
CONFERENCE_CREATED: "xmpp.conferenceCreated.jingle",
|
||||
// Designates an event indicating that the media (ICE) connection was
|
||||
// interrupted. This should go to the RTC module.
|
||||
CONNECTION_INTERRUPTED: "xmpp.connection.interrupted",
|
||||
// Designates an event indicating that the media (ICE) connection was
|
||||
// restored. This should go to the RTC module.
|
||||
CONNECTION_RESTORED: "xmpp.connection.restored",
|
||||
// Designates an event indicating that an offer (e.g. Jingle
|
||||
// session-initiate) was received.
|
||||
CALL_INCOMING: "xmpp.callincoming.jingle",
|
||||
DISPOSE_CONFERENCE: "xmpp.dispose_conference",
|
||||
GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
|
||||
// Designates an event indicating that we were kicked from the XMPP MUC.
|
||||
KICKED: "xmpp.kicked",
|
||||
BRIDGE_DOWN: "xmpp.bridge_down",
|
||||
// Designates an event indicating that the userID for a specific JID has
|
||||
// changed.
|
||||
USER_ID_CHANGED: "xmpp.user_id_changed",
|
||||
// We joined the MUC
|
||||
// Designates an event indicating that we have joined the XMPP MUC.
|
||||
MUC_JOINED: "xmpp.muc_joined",
|
||||
// A member joined the MUC
|
||||
// Designates an event indicating that a participant joined the XMPP MUC.
|
||||
MUC_MEMBER_JOINED: "xmpp.muc_member_joined",
|
||||
// A member left the MUC
|
||||
// Designates an event indicating that a participant left the XMPP MUC.
|
||||
MUC_MEMBER_LEFT: "xmpp.muc_member_left",
|
||||
// Designates an event indicating that the MUC role of a participant has
|
||||
// changed.
|
||||
MUC_ROLE_CHANGED: "xmpp.muc_role_changed",
|
||||
// Designates an event indicating that the XMPP MUC was destroyed.
|
||||
MUC_DESTROYED: "xmpp.muc_destroyed",
|
||||
// Designates an event indicating that the display name of a participant
|
||||
// has changed.
|
||||
DISPLAY_NAME_CHANGED: "xmpp.display_name_changed",
|
||||
// Designates an event indicating that we received statistics from a
|
||||
// participant in the MUC.
|
||||
REMOTE_STATS: "xmpp.remote_stats",
|
||||
// Designates an event indicating that our role in the XMPP MUC has changed.
|
||||
LOCAL_ROLE_CHANGED: "xmpp.localrole_changed",
|
||||
PRESENCE_STATUS: "xmpp.presence_status",
|
||||
RESERVATION_ERROR: "xmpp.room_reservation_error",
|
||||
// Designates an event indicating that the subject of the XMPP MUC has
|
||||
// changed.
|
||||
SUBJECT_CHANGED: "xmpp.subject_changed",
|
||||
// Designates an event indicating that an XMPP message in the MUC was
|
||||
// received.
|
||||
MESSAGE_RECEIVED: "xmpp.message_received",
|
||||
// Designates an event indicating that we sent an XMPP message to the MUC.
|
||||
SENDING_CHAT_MESSAGE: "xmpp.sending_chat_message",
|
||||
// Designates an event indicating that the video type (e.g. 'camera' or
|
||||
// 'screen') for a participant has changed.
|
||||
PARTICIPANT_VIDEO_TYPE_CHANGED: "xmpp.video_type",
|
||||
// Designates an event indicating that a participant in the XMPP MUC has
|
||||
// advertised that they have audio muted (or unmuted).
|
||||
PARTICIPANT_AUDIO_MUTED: "xmpp.audio_muted",
|
||||
// Designates an event indicating that a participant in the XMPP MUC has
|
||||
// advertised that they have video muted (or unmuted).
|
||||
PARTICIPANT_VIDEO_MUTED: "xmpp.video_muted",
|
||||
// Designates an event indicating that the focus has asked us to mute our
|
||||
// audio.
|
||||
AUDIO_MUTED_BY_FOCUS: "xmpp.audio_muted_by_focus",
|
||||
// Designates an event indicating that a moderator in the room changed the
|
||||
// "start muted" settings for the conference.
|
||||
START_MUTED_SETTING_CHANGED: "xmpp.start_muted_setting_changed",
|
||||
// Designates an event indicating that we should join the conference with
|
||||
// audio and/or video muted.
|
||||
START_MUTED_FROM_FOCUS: "xmpp.start_muted_from_focus",
|
||||
|
||||
|
||||
PEERCONNECTION_READY: "xmpp.peerconnection_ready",
|
||||
CONFERENCE_SETUP_FAILED: "xmpp.conference_setup_failed",
|
||||
PASSWORD_REQUIRED: "xmpp.password_required",
|
||||
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
||||
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
||||
ETHERPAD: "xmpp.etherpad",
|
||||
DEVICE_AVAILABLE: "xmpp.device_available",
|
||||
START_MUTED: "xmpp.start_muted",
|
||||
PEERCONNECTION_READY: "xmpp.peerconnection_ready",
|
||||
CONFERENCE_SETUP_FAILED: "xmpp.conference_setup_failed",
|
||||
AUDIO_MUTED: "xmpp.audio_muted",
|
||||
VIDEO_MUTED: "xmpp.video_muted"
|
||||
BRIDGE_DOWN: "xmpp.bridge_down",
|
||||
PRESENCE_STATUS: "xmpp.presence_status",
|
||||
RESERVATION_ERROR: "xmpp.room_reservation_error",
|
||||
DISPOSE_CONFERENCE: "xmpp.dispose_conference",
|
||||
GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
|
||||
// TODO: only used in a hack, should probably be removed.
|
||||
SET_LOCAL_DESCRIPTION_ERROR: 'xmpp.set_local_description_error',
|
||||
// TODO: only used in a hack, should probably be removed.
|
||||
SET_REMOTE_DESCRIPTION_ERROR: 'xmpp.set_remote_description_error',
|
||||
// TODO: only used in a hack, should probably be removed.
|
||||
CREATE_ANSWER_ERROR: 'xmpp.create_answer_error',
|
||||
JINGLE_FATAL_ERROR: 'xmpp.jingle_fatal_error',
|
||||
PROMPT_FOR_LOGIN: 'xmpp.prompt_for_login',
|
||||
FOCUS_DISCONNECTED: 'xmpp.focus_disconnected',
|
||||
ROOM_JOIN_ERROR: 'xmpp.room_join_error',
|
||||
ROOM_CONNECT_ERROR: 'xmpp.room_connect_error',
|
||||
// xmpp is connected and obtained user media
|
||||
READY_TO_JOIN: 'xmpp.ready_to_join'
|
||||
};
|
||||
module.exports = XMPPEvents;
|
||||
Reference in New Issue
Block a user