Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a353501dab
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ BROWSERIFY = ./node_modules/.bin/browserify
|
|||
UGLIFYJS = ./node_modules/.bin/uglifyjs
|
||||
EXORCIST = ./node_modules/.bin/exorcist
|
||||
CLEANCSS = ./node_modules/.bin/cleancss
|
||||
CSS_FILES = font.css toastr.css main.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css
|
||||
CSS_FILES = font.css toastr.css main.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css
|
||||
DEPLOY_DIR = libs
|
||||
BROWSERIFY_FLAGS = -d
|
||||
OUTPUT_DIR = .
|
||||
|
|
1
app.js
1
app.js
|
@ -3,6 +3,7 @@
|
|||
|
||||
import "babel-polyfill";
|
||||
import "jquery";
|
||||
import "jquery-contextmenu";
|
||||
import "jquery-ui";
|
||||
import "strophe";
|
||||
import "strophe-disco";
|
||||
|
|
|
@ -7,6 +7,8 @@ import AuthHandler from './modules/UI/authentication/AuthHandler';
|
|||
|
||||
import ConnectionQuality from './modules/connectionquality/connectionquality';
|
||||
|
||||
import Recorder from './modules/recorder/Recorder';
|
||||
|
||||
import CQEvents from './service/connectionquality/CQEvents';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
|
@ -121,30 +123,49 @@ function muteLocalVideo (muted) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the welcome page is enabled and redirects to it.
|
||||
*/
|
||||
function maybeRedirectToWelcomePage() {
|
||||
if (!config.enableWelcomePage) {
|
||||
return;
|
||||
}
|
||||
// redirect to welcome page
|
||||
setTimeout(() => {
|
||||
APP.settings.setWelcomePageEnabled(true);
|
||||
window.location.pathname = "/";
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes connection.disconnect and shows the feedback dialog
|
||||
* @param {boolean} [requestFeedback=false] if user feedback should be requested
|
||||
* @returns Promise.
|
||||
*/
|
||||
function disconnectAndShowFeedback(requestFeedback) {
|
||||
connection.disconnect();
|
||||
if (requestFeedback) {
|
||||
return APP.UI.requestFeedback();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the conference and optionally request user feedback.
|
||||
* @param {boolean} [requestFeedback=false] if user feedback should be requested
|
||||
*/
|
||||
function hangup (requestFeedback = false) {
|
||||
APP.conference._room.leave().then(() => {
|
||||
connection.disconnect();
|
||||
if (requestFeedback) {
|
||||
return APP.UI.requestFeedback();
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}).then(function () {
|
||||
if (!config.enableWelcomePage) {
|
||||
return;
|
||||
}
|
||||
// redirect to welcome page
|
||||
setTimeout(() => {
|
||||
APP.settings.setWelcomePageEnabled(true);
|
||||
window.location.pathname = "/";
|
||||
}, 3000);
|
||||
}, function (err) {
|
||||
console.error('Failed to hangup the call:', err);
|
||||
});
|
||||
const errCallback = (f, err) => {
|
||||
console.error('Error occurred during hanging up: ', err);
|
||||
return f();
|
||||
};
|
||||
const disconnect = disconnectAndShowFeedback.bind(null, requestFeedback);
|
||||
APP.conference._room.leave()
|
||||
.then(disconnect)
|
||||
.catch(errCallback.bind(null, disconnect))
|
||||
.then(maybeRedirectToWelcomePage)
|
||||
.catch(errCallback.bind(null, maybeRedirectToWelcomePage));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,6 +367,9 @@ export default {
|
|||
devices => APP.UI.onAvailableDevicesChanged(devices)
|
||||
);
|
||||
}
|
||||
if (config.iAmRecorder)
|
||||
this.recorder = new Recorder();
|
||||
|
||||
// XXX The API will take care of disconnecting from the XMPP server
|
||||
// (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -532,14 +556,14 @@ export default {
|
|||
* @param command {String} the name of the command
|
||||
* @param handler {Function} handler for the command
|
||||
*/
|
||||
addCommandListener () {
|
||||
addCommandListener () {
|
||||
room.addCommandListener.apply(room, arguments);
|
||||
},
|
||||
/**
|
||||
* Removes command.
|
||||
* @param name {String} the name of the command.
|
||||
*/
|
||||
removeCommand () {
|
||||
removeCommand () {
|
||||
room.removeCommand.apply(room, arguments);
|
||||
},
|
||||
/**
|
||||
|
@ -547,7 +571,7 @@ export default {
|
|||
* @param name {String} the name of the command.
|
||||
* @param values {Object} with keys and values that will be sent.
|
||||
*/
|
||||
sendCommand () {
|
||||
sendCommand () {
|
||||
room.sendCommand.apply(room, arguments);
|
||||
},
|
||||
/**
|
||||
|
@ -555,7 +579,7 @@ export default {
|
|||
* @param name {String} the name of the command.
|
||||
* @param values {Object} with keys and values that will be sent.
|
||||
*/
|
||||
sendCommandOnce () {
|
||||
sendCommandOnce () {
|
||||
room.sendCommandOnce.apply(room, arguments);
|
||||
}
|
||||
},
|
||||
|
@ -878,10 +902,6 @@ export default {
|
|||
|
||||
room.on(ConferenceEvents.RECORDER_STATE_CHANGED, (status, error) => {
|
||||
console.log("Received recorder status change: ", status, error);
|
||||
if(status == "error") {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
APP.UI.updateRecordingState(status);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
@charset "UTF-8";
|
||||
/*!
|
||||
* jQuery contextMenu - Plugin for simple contextMenu handling
|
||||
*
|
||||
* Version: v2.1.1
|
||||
*
|
||||
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
|
||||
* Web: http://swisnl.github.io/jQuery-contextMenu/
|
||||
*
|
||||
* Copyright (c) 2011-2016 SWIS BV and contributors
|
||||
*
|
||||
* Licensed under
|
||||
* MIT License http://www.opensource.org/licenses/mit-license
|
||||
*
|
||||
* Date: 2016-02-28T09:53:18.890Z
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "context-menu-icons";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
src: url("font/context-menu-icons.eot?2qmzf");
|
||||
src: url("font/context-menu-icons.eot?2qmzf#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2qmzf") format("woff2"), url("font/context-menu-icons.woff?2qmzf") format("woff"), url("font/context-menu-icons.ttf?2qmzf") format("truetype");
|
||||
}
|
||||
|
||||
.context-menu-icon:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 28px;
|
||||
font-family: "context-menu-icons";
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #2980b9;
|
||||
text-align: center;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.context-menu-icon-add:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-copy:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-cut:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-delete:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-edit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-paste:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon-quit:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.context-menu-icon.context-menu-hover:before {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-list {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
min-width: 180px;
|
||||
max-width: 360px;
|
||||
padding: 4px 0;
|
||||
margin: 5px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border: 1px solid #bebebe;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
position: relative;
|
||||
padding: 3px 28px;
|
||||
color: #2f2f2f;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
padding: 0;
|
||||
margin: 5px 0;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.context-menu-item > label > input,
|
||||
.context-menu-item > label > textarea {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-hover {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-disabled {
|
||||
color: #626262;
|
||||
}
|
||||
|
||||
.context-menu-input.context-menu-hover,
|
||||
.context-menu-item.context-menu-disabled.context-menu-hover {
|
||||
cursor: default;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.context-menu-submenu:after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
z-index: 1;
|
||||
width: 0;
|
||||
height: 0;
|
||||
content: '';
|
||||
border-color: transparent transparent transparent #2f2f2f;
|
||||
border-style: solid;
|
||||
border-width: 4px 0 4px 4px;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs
|
||||
*/
|
||||
.context-menu-item.context-menu-input {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* vertically align inside labels */
|
||||
.context-menu-input > label > * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* position checkboxes and radios as icons */
|
||||
.context-menu-input > label > input[type="checkbox"],
|
||||
.context-menu-input > label > input[type="radio"] {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
.context-menu-input > label,
|
||||
.context-menu-input > label > input[type="text"],
|
||||
.context-menu-input > label > textarea,
|
||||
.context-menu-input > label > select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.context-menu-input > label > textarea {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.context-menu-item > .context-menu-list {
|
||||
top: 5px;
|
||||
/* re-positioned by js */
|
||||
right: -5px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.context-menu-item.context-menu-visible > .context-menu-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu-accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"en": "",
|
||||
"bg": "",
|
||||
"de": "",
|
||||
"tr": "",
|
||||
"it": "",
|
||||
"fr": "",
|
||||
"sl": "",
|
||||
"sk": "",
|
||||
"sv": ""
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
{
|
||||
"contactlist": "",
|
||||
"connectionsettings": "",
|
||||
"poweredby": "",
|
||||
"downloadlogs": "",
|
||||
"feedback": "",
|
||||
"roomUrlDefaultMsg": "",
|
||||
"participant": "",
|
||||
"me": "",
|
||||
"speaker": "",
|
||||
"defaultNickname": "",
|
||||
"defaultLink": "",
|
||||
"welcomepage": {
|
||||
"go": "",
|
||||
"roomname": "",
|
||||
"disable": "",
|
||||
"feature1": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature2": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature3": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature4": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature5": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature6": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature7": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
},
|
||||
"feature8": {
|
||||
"title": "",
|
||||
"content": ""
|
||||
}
|
||||
},
|
||||
"toolbar": {
|
||||
"mute": "",
|
||||
"videomute": "",
|
||||
"authenticate": "",
|
||||
"lock": "",
|
||||
"invite": "",
|
||||
"chat": "",
|
||||
"etherpad": "",
|
||||
"sharedvideo": "",
|
||||
"sharescreen": "",
|
||||
"fullscreen": "",
|
||||
"sip": "",
|
||||
"Settings": "",
|
||||
"hangup": "",
|
||||
"login": "",
|
||||
"logout": "",
|
||||
"dialpad": "",
|
||||
"sharedVideoMutedPopup": "",
|
||||
"micMutedPopup": "",
|
||||
"unableToUnmutePopup": ""
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "",
|
||||
"filmstrip": "",
|
||||
"contactlist": ""
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "",
|
||||
"popover": ""
|
||||
},
|
||||
"messagebox": ""
|
||||
},
|
||||
"settings": {
|
||||
"title": "",
|
||||
"update": "",
|
||||
"name": "",
|
||||
"startAudioMuted": "",
|
||||
"startVideoMuted": "",
|
||||
"selectCamera": "",
|
||||
"selectMic": "",
|
||||
"followMe": ""
|
||||
},
|
||||
"videothumbnail": {
|
||||
"editnickname": "",
|
||||
"moderator": "",
|
||||
"videomute": "",
|
||||
"mute": "",
|
||||
"kick": "",
|
||||
"muted": "",
|
||||
"domute": ""
|
||||
},
|
||||
"connectionindicator": {
|
||||
"bitrate": "",
|
||||
"packetloss": "",
|
||||
"resolution": "",
|
||||
"less": "",
|
||||
"more": "",
|
||||
"address": "",
|
||||
"remoteport": "",
|
||||
"remoteport_plural": "",
|
||||
"localport": "",
|
||||
"localport_plural": "",
|
||||
"localaddress": "",
|
||||
"localaddress_plural": "",
|
||||
"remoteaddress": "",
|
||||
"remoteaddress_plural": "",
|
||||
"transport": "",
|
||||
"bandwidth": "",
|
||||
"na": ""
|
||||
},
|
||||
"notify": {
|
||||
"disconnected": "",
|
||||
"moderator": "",
|
||||
"connected": "",
|
||||
"somebody": "",
|
||||
"me": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"grantedToUnknown": "",
|
||||
"muted": "",
|
||||
"mutedTitle": ""
|
||||
},
|
||||
"dialog": {
|
||||
"kickMessage": "",
|
||||
"popupError": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"connecting": "",
|
||||
"error": "",
|
||||
"detectext": "",
|
||||
"failtoinstall": "",
|
||||
"failedpermissions": "",
|
||||
"bridgeUnavailable": "",
|
||||
"jicofoUnavailable": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"lockTitle": "",
|
||||
"lockMessage": "",
|
||||
"warning": "",
|
||||
"passwordNotSupported": "",
|
||||
"sorry": "",
|
||||
"internalError": "",
|
||||
"unableToSwitch": "",
|
||||
"SLDFailure": "",
|
||||
"SRDFailure": "",
|
||||
"oops": "",
|
||||
"defaultError": "",
|
||||
"passwordRequired": "",
|
||||
"Ok": "",
|
||||
"Remove": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareVideoLinkError": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"WaitingForHost": "",
|
||||
"WaitForHostMsg": "",
|
||||
"IamHost": "",
|
||||
"Cancel": "",
|
||||
"retry": "",
|
||||
"logoutTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"sessTerminated": "",
|
||||
"hungUp": "",
|
||||
"joinAgain": "",
|
||||
"Share": "",
|
||||
"Save": "",
|
||||
"recording": "",
|
||||
"recordingToken": "",
|
||||
"Dial": "",
|
||||
"sipMsg": "",
|
||||
"passwordCheck": "",
|
||||
"passwordMsg": "",
|
||||
"Invite": "",
|
||||
"shareLink": "",
|
||||
"settings1": "",
|
||||
"settings2": "",
|
||||
"settings3": "",
|
||||
"yourPassword": "",
|
||||
"Back": "",
|
||||
"serviceUnavailable": "",
|
||||
"gracefulShutdown": "",
|
||||
"Yes": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"password": "",
|
||||
"userPassword": "",
|
||||
"token": "",
|
||||
"tokenAuthFailed": "",
|
||||
"displayNameRequired": "",
|
||||
"extensionRequired": "",
|
||||
"firefoxExtensionPrompt": "",
|
||||
"feedbackQuestion": "",
|
||||
"thankYou": "",
|
||||
"sorryFeedback": "",
|
||||
"liveStreaming": "",
|
||||
"streamKey": "",
|
||||
"startLiveStreaming": "",
|
||||
"stopStreamingWarning": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": ""
|
||||
},
|
||||
"email": {
|
||||
"sharedKey": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"and": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "",
|
||||
"CONNECTING": "",
|
||||
"RECONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ATTACHED": ""
|
||||
},
|
||||
"recording": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"pending": "",
|
||||
"on": "",
|
||||
"off": "",
|
||||
"unavailable": "",
|
||||
"failedToStart": "",
|
||||
"buttonTooltip": "",
|
||||
"streamIdRequired": ""
|
||||
}
|
||||
}
|
|
@ -99,7 +99,8 @@
|
|||
"mute": "Participant is muted",
|
||||
"kick": "Kick out",
|
||||
"muted": "Muted",
|
||||
"domute": "Mute"
|
||||
"domute": "Mute",
|
||||
"flip": "Flip"
|
||||
|
||||
},
|
||||
"connectionindicator":
|
||||
|
@ -268,7 +269,8 @@
|
|||
"on": "Recording",
|
||||
"off": "Recording stopped",
|
||||
"failedToStart": "Recording failed to start",
|
||||
"buttonTooltip": "Start / stop recording"
|
||||
"buttonTooltip": "Start / stop recording",
|
||||
"error": "Recording failed. Please try again."
|
||||
},
|
||||
"liveStreaming":
|
||||
{
|
||||
|
@ -278,6 +280,7 @@
|
|||
"unavailable": "The live streaming service is currently unavailable. Please try again later.",
|
||||
"failedToStart": "Live streaming failed to start",
|
||||
"buttonTooltip": "Start / stop live stream",
|
||||
"streamIdRequired": "Please fill in the stream id in order to launch the live streaming."
|
||||
"streamIdRequired": "Please fill in the stream id in order to launch the live streaming.",
|
||||
"error": "Live streaming failed. Please try again"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,7 +205,8 @@ var Status = {
|
|||
OFF: "off",
|
||||
AVAILABLE: "available",
|
||||
UNAVAILABLE: "unavailable",
|
||||
PENDING: "pending"
|
||||
PENDING: "pending",
|
||||
ERROR: "error"
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -248,6 +249,7 @@ var Recording = {
|
|||
this.recordingOffKey = "liveStreaming.off";
|
||||
this.recordingPendingKey = "liveStreaming.pending";
|
||||
this.failedToStartKey = "liveStreaming.failedToStart";
|
||||
this.recordingErrorKey = "liveStreaming.error";
|
||||
this.recordingButtonTooltip = "liveStreaming.buttonTooltip";
|
||||
}
|
||||
else {
|
||||
|
@ -256,6 +258,7 @@ var Recording = {
|
|||
this.recordingOffKey = "recording.off";
|
||||
this.recordingPendingKey = "recording.pending";
|
||||
this.failedToStartKey = "recording.failedToStart";
|
||||
this.recordingErrorKey = "recording.error";
|
||||
this.recordingButtonTooltip = "recording.buttonTooltip";
|
||||
}
|
||||
|
||||
|
@ -338,7 +341,6 @@ var Recording = {
|
|||
*/
|
||||
updateRecordingUI (recordingState) {
|
||||
let buttonSelector = $('#toolbar_button_record');
|
||||
let labelSelector = $('#recordingLabel');
|
||||
|
||||
// TODO: handle recording state=available
|
||||
if (recordingState === Status.ON) {
|
||||
|
@ -346,12 +348,10 @@ var Recording = {
|
|||
buttonSelector.removeClass(this.baseClass);
|
||||
buttonSelector.addClass(this.baseClass + " active");
|
||||
|
||||
labelSelector.attr("data-i18n", this.recordingOnKey);
|
||||
moveToCorner(labelSelector, true, 3000);
|
||||
labelSelector
|
||||
.text(APP.translation.translateString(this.recordingOnKey));
|
||||
} else if (recordingState === Status.OFF
|
||||
|| recordingState === Status.UNAVAILABLE) {
|
||||
this._updateStatusLabel(this.recordingOnKey, false);
|
||||
}
|
||||
else if (recordingState === Status.OFF
|
||||
|| recordingState === Status.UNAVAILABLE) {
|
||||
|
||||
// We don't want to do any changes if this is
|
||||
// an availability change.
|
||||
|
@ -362,15 +362,13 @@ var Recording = {
|
|||
buttonSelector.removeClass(this.baseClass + " active");
|
||||
buttonSelector.addClass(this.baseClass);
|
||||
|
||||
moveToCorner(labelSelector, false);
|
||||
let messageKey;
|
||||
if (this.currentState === Status.PENDING)
|
||||
messageKey = this.failedToStartKey;
|
||||
else
|
||||
messageKey = this.recordingOffKey;
|
||||
|
||||
labelSelector.attr("data-i18n", messageKey);
|
||||
labelSelector.text(APP.translation.translateString(messageKey));
|
||||
this._updateStatusLabel(messageKey, true);
|
||||
|
||||
setTimeout(function(){
|
||||
$('#recordingLabel').css({display: "none"});
|
||||
|
@ -381,16 +379,19 @@ var Recording = {
|
|||
buttonSelector.removeClass(this.baseClass + " active");
|
||||
buttonSelector.addClass(this.baseClass);
|
||||
|
||||
moveToCorner(labelSelector, false);
|
||||
labelSelector
|
||||
.attr("data-i18n", this.recordingPendingKey);
|
||||
labelSelector
|
||||
.text(APP.translation.translateString(
|
||||
this.recordingPendingKey));
|
||||
this._updateStatusLabel(this.recordingPendingKey, true);
|
||||
}
|
||||
else if (recordingState === Status.ERROR) {
|
||||
buttonSelector.removeClass(this.baseClass + " active");
|
||||
buttonSelector.addClass(this.baseClass);
|
||||
|
||||
this._updateStatusLabel(this.recordingErrorKey, true);
|
||||
}
|
||||
|
||||
this.currentState = recordingState;
|
||||
|
||||
let labelSelector = $('#recordingLabel');
|
||||
|
||||
// We don't show the label for available state.
|
||||
if (recordingState !== Status.AVAILABLE
|
||||
&& !labelSelector.is(":visible"))
|
||||
|
@ -404,6 +405,20 @@ var Recording = {
|
|||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
|
||||
this.predefinedToken);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Updates the status label.
|
||||
* @param textKey the text to show
|
||||
* @param isCentered indicates if the label should be centered on the window
|
||||
* or moved to the top right corner.
|
||||
*/
|
||||
_updateStatusLabel(textKey, isCentered) {
|
||||
let labelSelector = $('#recordingLabel');
|
||||
|
||||
moveToCorner(labelSelector, !isCentered);
|
||||
|
||||
labelSelector.attr("data-i18n", textKey);
|
||||
labelSelector.text(APP.translation.translateString(textKey));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@ class VideoContainer extends LargeContainer {
|
|||
super();
|
||||
this.stream = null;
|
||||
this.videoType = null;
|
||||
this.localFlipX = true;
|
||||
|
||||
this.isVisible = false;
|
||||
|
||||
|
@ -284,13 +285,25 @@ class VideoContainer extends LargeContainer {
|
|||
}
|
||||
|
||||
stream.attach(this.$video[0]);
|
||||
|
||||
let flipX = stream.isLocal() && !this.isScreenSharing();
|
||||
let flipX = stream.isLocal() && this.localFlipX;
|
||||
this.$video.css({
|
||||
transform: flipX ? 'scaleX(-1)' : 'none'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the flipX state of the local video.
|
||||
* @param val {boolean} true if flipped.
|
||||
*/
|
||||
setLocalFlipX(val) {
|
||||
this.localFlipX = val;
|
||||
if(!this.$video || !this.stream || !this.stream.isLocal())
|
||||
return;
|
||||
this.$video.css({
|
||||
transform: this.localFlipX ? 'scaleX(-1)' : 'none'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current video stream is screen sharing.
|
||||
* @returns {boolean}
|
||||
|
@ -453,7 +466,7 @@ export default class LargeVideoManager {
|
|||
} else {
|
||||
preUpdate = Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
preUpdate.then(() => {
|
||||
let {id, stream, videoType, resolve} = this.newStreamData;
|
||||
this.newStreamData = null;
|
||||
|
@ -651,4 +664,12 @@ export default class LargeVideoManager {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the flipX state of the local video.
|
||||
* @param val {boolean} true if flipped.
|
||||
*/
|
||||
onLocalFlipXChange(val) {
|
||||
this.videoContainer.setLocalFlipX(val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,16 +4,15 @@ import UIUtil from "../util/UIUtil";
|
|||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import SmallVideo from "./SmallVideo";
|
||||
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
|
||||
const RTCUIUtils = JitsiMeetJS.util.RTCUIHelper;
|
||||
const TrackEvents = JitsiMeetJS.events.track;
|
||||
|
||||
function LocalVideo(VideoLayout, emitter) {
|
||||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.localVideoId = null;
|
||||
this.bindHoverHandler();
|
||||
this.flipX = true;
|
||||
this._buildContextMenu();
|
||||
this.isLocal = true;
|
||||
this.emitter = emitter;
|
||||
Object.defineProperty(this, 'id', {
|
||||
|
@ -165,9 +164,8 @@ LocalVideo.prototype.changeVideo = function (stream) {
|
|||
localVideoContainerSelector.off('click');
|
||||
localVideoContainerSelector.on('click', localVideoClick);
|
||||
|
||||
this.flipX = stream.videoType != "desktop";
|
||||
let localVideo = document.createElement('video');
|
||||
localVideo.id = 'localVideo_' + stream.getId();
|
||||
localVideo.id = this.localVideoId = 'localVideo_' + stream.getId();
|
||||
|
||||
RTCUIUtils.setAutoPlay(localVideo, true);
|
||||
RTCUIUtils.setVolume(localVideo, 0);
|
||||
|
@ -182,9 +180,9 @@ LocalVideo.prototype.changeVideo = function (stream) {
|
|||
// onclick has to be used with Temasys plugin
|
||||
localVideo.onclick = localVideoClick;
|
||||
|
||||
if (this.flipX) {
|
||||
$(localVideo).addClass("flipVideoX");
|
||||
}
|
||||
let isVideo = stream.videoType != "desktop";
|
||||
this._enableDisableContextMenu(isVideo);
|
||||
this.setFlipX(isVideo? APP.settings.getLocalFlipX() : false);
|
||||
|
||||
// Attach WebRTC stream
|
||||
localVideo = stream.attach(localVideo);
|
||||
|
@ -222,4 +220,54 @@ LocalVideo.prototype.setVisible = function(visible) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the flipX state of the video.
|
||||
* @param val {boolean} true for flipped otherwise false;
|
||||
*/
|
||||
LocalVideo.prototype.setFlipX = function (val) {
|
||||
this.emitter.emit(UIEvents.LOCAL_FLIPX_CHANGED, val);
|
||||
if(!this.localVideoId)
|
||||
return;
|
||||
if(val) {
|
||||
this.selectVideoElement().addClass("flipVideoX");
|
||||
} else {
|
||||
this.selectVideoElement().removeClass("flipVideoX");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the context menu for the local video.
|
||||
*/
|
||||
LocalVideo.prototype._buildContextMenu = function () {
|
||||
$.contextMenu({
|
||||
selector: '#' + this.videoSpanId,
|
||||
zIndex: 10000,
|
||||
items: {
|
||||
flip: {
|
||||
name: "Flip",
|
||||
callback: () => {
|
||||
let val = !APP.settings.getLocalFlipX();
|
||||
this.setFlipX(val);
|
||||
APP.settings.setLocalFlipX(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
events: {
|
||||
show : function(options){
|
||||
options.items.flip.name =
|
||||
APP.translation.translateString("videothumbnail.flip");
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables or disables the context menu for the local video.
|
||||
* @param enable {boolean} true for enable, false for disable
|
||||
*/
|
||||
LocalVideo.prototype._enableDisableContextMenu = function (enable) {
|
||||
if($('#' + this.videoSpanId).contextMenu)
|
||||
$('#' + this.videoSpanId).contextMenu(enable);
|
||||
};
|
||||
|
||||
export default LocalVideo;
|
||||
|
|
|
@ -165,9 +165,6 @@ SmallVideo.createStreamElement = function (stream) {
|
|||
console.log("(TIME) Render " + type + ":\t",
|
||||
now);
|
||||
};
|
||||
|
||||
element.oncontextmenu = function () { return false; };
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@ var eventEmitter = null;
|
|||
*/
|
||||
var pinnedId = null;
|
||||
|
||||
/**
|
||||
* flipX state of the localVideo
|
||||
*/
|
||||
let localFlipX = null;
|
||||
|
||||
/**
|
||||
* On contact list item clicked.
|
||||
*/
|
||||
|
@ -92,6 +97,11 @@ let largeVideo;
|
|||
var VideoLayout = {
|
||||
init (emitter) {
|
||||
eventEmitter = emitter;
|
||||
eventEmitter.addListener(UIEvents.LOCAL_FLIPX_CHANGED, function (val) {
|
||||
localFlipX = val;
|
||||
if(largeVideo)
|
||||
largeVideo.onLocalFlipXChange(val);
|
||||
});
|
||||
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
||||
// sets default video type of local video
|
||||
localVideoThumbnail.setVideoType(VIDEO_CONTAINER_TYPE);
|
||||
|
@ -105,6 +115,9 @@ var VideoLayout = {
|
|||
|
||||
initLargeVideo (isSideBarVisible) {
|
||||
largeVideo = new LargeVideoManager();
|
||||
if(localFlipX) {
|
||||
largeVideo.onLocalFlipXChange(localFlipX);
|
||||
}
|
||||
largeVideo.updateContainerSize(isSideBarVisible);
|
||||
AudioLevels.init();
|
||||
},
|
||||
|
@ -1084,6 +1097,15 @@ var VideoLayout = {
|
|||
videoResolutionLabel.css({display: "block"});
|
||||
else if (!isResolutionHD && videoResolutionLabel.is(":visible"))
|
||||
videoResolutionLabel.css({display: "none"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the flipX state of the local video.
|
||||
* @param {boolean} true for flipped otherwise false;
|
||||
*/
|
||||
setLocalFlipX: function (val) {
|
||||
this.localFlipX = val;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* global APP, $, config */
|
||||
|
||||
/**
|
||||
* The (name of the) command which transports the recorder info.
|
||||
*/
|
||||
const _USER_INFO_COMMAND = "userinfo";
|
||||
|
||||
/**
|
||||
* The Recorder class is meant to take care of recorder related presence
|
||||
* commands.
|
||||
*/
|
||||
class Recorder {
|
||||
constructor() {
|
||||
if (config.iAmRecorder)
|
||||
this._sendRecorderInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the information that this is a recorder through the presence.
|
||||
* @private
|
||||
*/
|
||||
_sendRecorderInfo() {
|
||||
var commands = APP.conference.commands;
|
||||
|
||||
// XXX The "Follow Me" command represents a snapshot of all states
|
||||
// which are to be followed so don't forget to removeCommand before
|
||||
// sendCommand!
|
||||
commands.removeCommand(_USER_INFO_COMMAND);
|
||||
var self = this;
|
||||
commands.sendCommand(
|
||||
_USER_INFO_COMMAND,
|
||||
{
|
||||
attributes: {
|
||||
xmlns: 'http://jitsi.org/jitmeet/userinfo',
|
||||
robot: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Recorder;
|
|
@ -6,6 +6,7 @@ let language = null;
|
|||
let cameraDeviceId = '';
|
||||
let micDeviceId = '';
|
||||
let welcomePageDisabled = false;
|
||||
let localFlipX = null;
|
||||
|
||||
function supportsLocalStorage() {
|
||||
try {
|
||||
|
@ -31,6 +32,7 @@ if (supportsLocalStorage()) {
|
|||
}
|
||||
|
||||
email = UIUtil.unescapeHtml(window.localStorage.email || '');
|
||||
localFlipX = JSON.parse(window.localStorage.localFlipX || true);
|
||||
displayName = UIUtil.unescapeHtml(window.localStorage.displayname || '');
|
||||
language = window.localStorage.language;
|
||||
cameraDeviceId = window.localStorage.cameraDeviceId || '';
|
||||
|
@ -87,6 +89,23 @@ export default {
|
|||
window.localStorage.language = lang;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets new flipX state of local video and saves it to the local storage.
|
||||
* @param {string} val flipX state of local video
|
||||
*/
|
||||
setLocalFlipX: function (val) {
|
||||
localFlipX = val;
|
||||
window.localStorage.localFlipX = val;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns flipX state of local video.
|
||||
* @returns {string} flipX
|
||||
*/
|
||||
getLocalFlipX: function () {
|
||||
return localFlipX;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get device id of the camera which is currently in use.
|
||||
* Empty string stands for default device.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"jquery": "~2.1.1",
|
||||
"jQuery-Impromptu": "git+https://github.com/trentrichardson/jQuery-Impromptu.git#v6.0.0",
|
||||
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
|
||||
"jquery-contextmenu": "*",
|
||||
"jquery-ui": "^1.10.5",
|
||||
"jssha": "1.5.0",
|
||||
"retry": "0.6.1",
|
||||
|
@ -98,6 +99,9 @@
|
|||
"jQuery-Impromptu": {
|
||||
"depends": "jquery:jQuery"
|
||||
},
|
||||
"jquery-contextmenu": {
|
||||
"depends": "jquery:jQuery"
|
||||
},
|
||||
"autosize": {
|
||||
"depends": "jquery:jQuery"
|
||||
}
|
||||
|
|
|
@ -71,5 +71,9 @@ export default {
|
|||
* Notifies interested listeners that the follow-me feature is enabled or
|
||||
* disabled.
|
||||
*/
|
||||
FOLLOW_ME_ENABLED: "UI.follow_me_enabled"
|
||||
FOLLOW_ME_ENABLED: "UI.follow_me_enabled",
|
||||
/**
|
||||
* Notifies that flipX property of the local video is changed.
|
||||
*/
|
||||
LOCAL_FLIPX_CHANGED: "UI.local_flipx_changed"
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue