Merge remote-tracking branch 'upstream/master'

This commit is contained in:
luciash 2016-05-09 08:22:23 +02:00
commit a353501dab
16 changed files with 725 additions and 63 deletions

View File

@ -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
View File

@ -3,6 +3,7 @@
import "babel-polyfill";
import "jquery";
import "jquery-contextmenu";
import "jquery-ui";
import "strophe";
import "strophe-disco";

View File

@ -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);
});

206
css/jquery.contextMenu.css Normal file
View File

@ -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;
}

11
lang/languages-hy.json Normal file
View File

@ -0,0 +1,11 @@
{
"en": "",
"bg": "",
"de": "",
"tr": "",
"it": "",
"fr": "",
"sl": "",
"sk": "",
"sv": ""
}

250
lang/main-hy.json Normal file
View File

@ -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": ""
}
}

View File

@ -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"
}
}

View File

@ -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));
}
};

View File

@ -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);
}
}

View File

@ -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;

View File

@ -165,9 +165,6 @@ SmallVideo.createStreamElement = function (stream) {
console.log("(TIME) Render " + type + ":\t",
now);
};
element.oncontextmenu = function () { return false; };
return element;
};

View File

@ -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;
}
};

View File

@ -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;

View File

@ -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.

View File

@ -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"
}

View File

@ -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"
};