mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-10 08:40:18 +00:00
Compare commits
90 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4db8faa526 | ||
|
|
586ea2ae0d | ||
|
|
e39648ce21 | ||
|
|
e0a05c5908 | ||
|
|
2c01fde713 | ||
|
|
92641c20f3 | ||
|
|
f145d98a12 | ||
|
|
a3d2c95d80 | ||
|
|
a5a9936e25 | ||
|
|
cfeb03740c | ||
|
|
4ab0fca4d7 | ||
|
|
df3b7e2dbc | ||
|
|
302b0cf776 | ||
|
|
e7cbacf9a2 | ||
|
|
f9817b12bf | ||
|
|
bbc7aedb48 | ||
|
|
4fdc11c6fb | ||
|
|
e1cb75fe04 | ||
|
|
0bf6d52eef | ||
|
|
7453198472 | ||
|
|
0c1120c1a8 | ||
|
|
efbc84d18b | ||
|
|
cdca1a46ef | ||
|
|
689f7dc8f3 | ||
|
|
4ca9349de7 | ||
|
|
9d6253455a | ||
|
|
388f868165 | ||
|
|
aacb39a439 | ||
|
|
298338f076 | ||
|
|
2d2915967c | ||
|
|
4572e1d344 | ||
|
|
f8b200f32c | ||
|
|
3d4addd9ef | ||
|
|
2919a60403 | ||
|
|
a07858cc72 | ||
|
|
d1d4674136 | ||
|
|
9b5d4b8ceb | ||
|
|
abe216a0bb | ||
|
|
84983c341e | ||
|
|
075423ee96 | ||
|
|
2ff77676e2 | ||
|
|
2da3373e10 | ||
|
|
1486eac752 | ||
|
|
c5adecb6e1 | ||
|
|
e4c4236386 | ||
|
|
e5e7e043ee | ||
|
|
f8d01b4312 | ||
|
|
f0a898c674 | ||
|
|
8b7bdb4957 | ||
|
|
bd46430434 | ||
|
|
a1635ccc68 | ||
|
|
1853fa6fae | ||
|
|
692f0792e1 | ||
|
|
bc222c60e0 | ||
|
|
96bbf0419c | ||
|
|
531e3d2765 | ||
|
|
02165786f1 | ||
|
|
a449223b40 | ||
|
|
12344ab486 | ||
|
|
b6e18d8a68 | ||
|
|
1a0677cb59 | ||
|
|
c483587853 | ||
|
|
9b25467080 | ||
|
|
f37fd15fca | ||
|
|
5092f52716 | ||
|
|
0013745783 | ||
|
|
ad5fa13339 | ||
|
|
e1fa5ecb34 | ||
|
|
e1512e3776 | ||
|
|
dab5252746 | ||
|
|
d8dd564b06 | ||
|
|
407b082780 | ||
|
|
a671093489 | ||
|
|
29f0c0b311 | ||
|
|
955680018f | ||
|
|
686e85cd4f | ||
|
|
297d4e65fc | ||
|
|
0e94bf7e0b | ||
|
|
a5bc9625ef | ||
|
|
cbde4f89b2 | ||
|
|
891c108191 | ||
|
|
764d767789 | ||
|
|
7ded10cd8d | ||
|
|
3b05a16b32 | ||
|
|
cf49c8c6ff | ||
|
|
99bf4bc44d | ||
|
|
929639b06b | ||
|
|
4c72833f5a | ||
|
|
e3eaac5bef | ||
|
|
0683f94edb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ all.css
|
||||
*css.map
|
||||
unsupported_browser.css
|
||||
.remote-sync.json
|
||||
.sync-config.cson
|
||||
|
||||
8
app.js
8
app.js
@@ -8,10 +8,14 @@ import "jquery-ui";
|
||||
import "strophe";
|
||||
import "strophe-disco";
|
||||
import "strophe-caps";
|
||||
import "tooltip";
|
||||
import "popover";
|
||||
import "jQuery-Impromptu";
|
||||
import "autosize";
|
||||
|
||||
import 'aui';
|
||||
import 'aui-experimental';
|
||||
import 'aui-css';
|
||||
import 'aui-experimental-css';
|
||||
|
||||
window.toastr = require("toastr");
|
||||
|
||||
import URLProcessor from "./modules/config/URLProcessor";
|
||||
|
||||
@@ -39,7 +39,7 @@ let connectionIsInterrupted = false;
|
||||
*/
|
||||
let DSExternalInstallationInProgress = false;
|
||||
|
||||
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
|
||||
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/VideoContainer";
|
||||
|
||||
/**
|
||||
* Known custom conference commands.
|
||||
@@ -293,8 +293,13 @@ function createLocalTracks (options, checkForPermissionPrompt) {
|
||||
firefox_fake_device: config.firefox_fake_device,
|
||||
desktopSharingExtensionExternalInstallation:
|
||||
options.desktopSharingExtensionExternalInstallation
|
||||
}, checkForPermissionPrompt)
|
||||
.catch(function (err) {
|
||||
}, checkForPermissionPrompt).then( (tracks) => {
|
||||
tracks.forEach((track) => {
|
||||
track.on(TrackEvents.NO_DATA_FROM_SOURCE,
|
||||
APP.UI.showTrackNotWorkingDialog.bind(null, track));
|
||||
});
|
||||
return tracks;
|
||||
}).catch(function (err) {
|
||||
console.error(
|
||||
'failed to create local tracks', options.devices, err);
|
||||
return Promise.reject(err);
|
||||
@@ -357,6 +362,14 @@ class ConferenceConnector {
|
||||
case ConferenceErrors.PASSWORD_REQUIRED:
|
||||
APP.UI.markRoomLocked(true);
|
||||
roomLocker.requirePassword().then(function () {
|
||||
let pass = roomLocker.password;
|
||||
// we received that password is required, but user is trying
|
||||
// anyway to login without a password, mark room as not locked
|
||||
// in case he succeeds (maybe someone removed the password
|
||||
// meanwhile), if it is still locked another password required
|
||||
// will be received and the room again will be marked as locked
|
||||
if (!pass)
|
||||
APP.UI.markRoomLocked(false);
|
||||
room.join(roomLocker.password);
|
||||
});
|
||||
break;
|
||||
@@ -698,6 +711,30 @@ export default {
|
||||
return room.getLogs();
|
||||
},
|
||||
|
||||
/**
|
||||
* Download logs, a function that can be called from console while
|
||||
* debugging.
|
||||
* @param filename (optional) specify target filename
|
||||
*/
|
||||
saveLogs (filename = 'meetlog.json') {
|
||||
// this can be called from console and will not have reference to this
|
||||
// that's why we reference the global var
|
||||
let logs = APP.conference.getLogs();
|
||||
let data = encodeURIComponent(JSON.stringify(logs, null, ' '));
|
||||
|
||||
let elem = document.createElement('a');
|
||||
|
||||
elem.download = filename;
|
||||
elem.href = 'data:application/json;charset=utf-8,\n' + data;
|
||||
elem.dataset.downloadurl
|
||||
= ['text/json', elem.download, elem.href].join(':');
|
||||
elem.dispatchEvent(new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: false
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposes a Command(s) API on this instance. It is necessitated by (1) the
|
||||
* desire to keep room private to this instance and (2) the need of other
|
||||
@@ -857,8 +894,6 @@ export default {
|
||||
|
||||
return promise.then(function () {
|
||||
if (stream) {
|
||||
stream.on(TrackEvents.TRACK_AUDIO_NOT_WORKING,
|
||||
APP.UI.showAudioNotWorkingDialog);
|
||||
return room.addTrack(stream);
|
||||
}
|
||||
}).then(() => {
|
||||
@@ -1389,6 +1424,8 @@ export default {
|
||||
|
||||
APP.UI.addListener(UIEvents.PINNED_ENDPOINT, (smallVideo, isPinned) => {
|
||||
var smallVideoId = smallVideo.getId();
|
||||
// FIXME why VIDEO_CONTAINER_TYPE instead of checking if
|
||||
// the participant is on the large video ?
|
||||
if (smallVideo.getVideoType() === VIDEO_CONTAINER_TYPE
|
||||
&& !APP.conference.isLocalId(smallVideoId)) {
|
||||
|
||||
@@ -1416,7 +1453,7 @@ export default {
|
||||
.then(([stream]) => {
|
||||
this.useVideoStream(stream);
|
||||
console.log('switched local video device');
|
||||
APP.settings.setCameraDeviceId(cameraDeviceId);
|
||||
APP.settings.setCameraDeviceId(cameraDeviceId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
APP.UI.showDeviceErrorDialog(null, err);
|
||||
@@ -1438,7 +1475,7 @@ export default {
|
||||
.then(([stream]) => {
|
||||
this.useAudioStream(stream);
|
||||
console.log('switched local audio device');
|
||||
APP.settings.setMicDeviceId(micDeviceId);
|
||||
APP.settings.setMicDeviceId(micDeviceId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
APP.UI.showDeviceErrorDialog(err, null);
|
||||
@@ -1533,13 +1570,13 @@ export default {
|
||||
// storage and settings menu. This is a workaround until
|
||||
// getConstraints() method will be implemented in browsers.
|
||||
if (localAudio) {
|
||||
localAudio._setRealDeviceIdFromDeviceList(devices);
|
||||
APP.settings.setMicDeviceId(localAudio.getDeviceId());
|
||||
APP.settings.setMicDeviceId(
|
||||
localAudio.getDeviceId(), false);
|
||||
}
|
||||
|
||||
if (localVideo) {
|
||||
localVideo._setRealDeviceIdFromDeviceList(devices);
|
||||
APP.settings.setCameraDeviceId(localVideo.getDeviceId());
|
||||
APP.settings.setCameraDeviceId(
|
||||
localVideo.getDeviceId(), false);
|
||||
}
|
||||
|
||||
mediaDeviceHelper.setCurrentMediaDevices(devices);
|
||||
@@ -1640,6 +1677,8 @@ export default {
|
||||
setRaisedHand(raisedHand) {
|
||||
if (raisedHand !== this.isHandRaised)
|
||||
{
|
||||
APP.UI.onLocalRaiseHandChanged(raisedHand);
|
||||
|
||||
this.isHandRaised = raisedHand;
|
||||
// Advertise the updated status
|
||||
room.setLocalParticipantProperty("raisedHand", raisedHand);
|
||||
|
||||
BIN
css/.DS_Store
vendored
BIN
css/.DS_Store
vendored
Binary file not shown.
@@ -81,15 +81,6 @@ form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#downloadlog {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 5;
|
||||
left: 5;
|
||||
overflow: visible;
|
||||
color: rgba(255,255,255,.50);
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #00ccff;
|
||||
}
|
||||
@@ -175,4 +166,15 @@ form {
|
||||
display: -ms-flexbox !important;
|
||||
display: -webkit-flex !important;
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tipsy {
|
||||
z-index: $tooltipsZ;
|
||||
&-inner {
|
||||
background-color: $tooltipBg;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
border-color: $tooltipBg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +104,6 @@
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
#unreadMessages {
|
||||
font-size: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#chat_container .username {
|
||||
float: left;
|
||||
padding-left: 5px;
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
cursor: default;
|
||||
|
||||
> ul#contacts {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
font-size: 12px;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -20,13 +19,13 @@
|
||||
#contacts {
|
||||
|
||||
>li {
|
||||
color: $defaultSideBarFontColor;
|
||||
list-style-type: none;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
color: #FFF;
|
||||
font-size: 10pt;
|
||||
padding: 7px 10px;
|
||||
margin: 2px;
|
||||
padding: 6px 10%;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
|
||||
@@ -255,9 +255,6 @@
|
||||
.fa-road:before {
|
||||
content: "\f018";
|
||||
}
|
||||
.fa-download:before {
|
||||
content: "\f019";
|
||||
}
|
||||
.fa-arrow-circle-o-down:before {
|
||||
content: "\f01a";
|
||||
}
|
||||
@@ -842,9 +839,6 @@
|
||||
.fa-exchange:before {
|
||||
content: "\f0ec";
|
||||
}
|
||||
.fa-cloud-download:before {
|
||||
content: "\f0ed";
|
||||
}
|
||||
.fa-cloud-upload:before {
|
||||
content: "\f0ee";
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 0.75em;
|
||||
line-height: 1.22em;
|
||||
font-size: 1.22em;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
@@ -42,9 +42,6 @@
|
||||
.icon-chat:before {
|
||||
content: "\e906";
|
||||
}
|
||||
.icon-download:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-edit:before {
|
||||
content: "\e907";
|
||||
}
|
||||
@@ -57,12 +54,21 @@
|
||||
.icon-kick:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.icon-menu-up:before {
|
||||
content: "\e91f";
|
||||
}
|
||||
.icon-menu-down:before {
|
||||
content: "\e920";
|
||||
}
|
||||
.icon-full-screen:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.icon-exit-full-screen:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.icon-star:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.icon-star-full:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
@@ -84,6 +90,9 @@
|
||||
.icon-mic-disabled:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.icon-raised-hand:before {
|
||||
content: "\e91e";
|
||||
}
|
||||
.icon-contactList:before {
|
||||
content: "\e91b";
|
||||
}
|
||||
@@ -96,9 +105,6 @@
|
||||
.icon-settings:before {
|
||||
content: "\e915";
|
||||
}
|
||||
.icon-star:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.icon-share-desktop:before {
|
||||
content: "\e917";
|
||||
}
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin circle($diameter) {
|
||||
width: $diameter;
|
||||
height: $diameter;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@mixin absoluteAligning($sizeX, $sizeY) {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
@include transform(translate(-#{$sizeX / 2}, -#{$sizeY / 2}))
|
||||
}
|
||||
|
||||
@mixin transform($func) {
|
||||
-moz-transform: $func;
|
||||
-ms-transform: $func;
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
z-index: 800;
|
||||
overflow: hidden;
|
||||
letter-spacing: 1px;
|
||||
|
||||
/**
|
||||
* Labels inside the side panel.
|
||||
*/
|
||||
label {
|
||||
color: $defaultSemiDarkColor;
|
||||
color: $defaultColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,16 +71,16 @@
|
||||
*/
|
||||
> div.title,
|
||||
div.subTitle {
|
||||
color: $defaultColor !important;
|
||||
text-align: left;
|
||||
margin: 10px 0px 10px 0px;
|
||||
padding: 5px 10px 5px 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main title size.
|
||||
*/
|
||||
> div.title {
|
||||
color: $defaultColor !important;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -87,10 +88,10 @@
|
||||
* Subtitle specific properties.
|
||||
*/
|
||||
> div.subTitle {
|
||||
font-size: 12px;
|
||||
background: $inputSemiBackground !important;
|
||||
margin-top: 20px !important;
|
||||
margin-bottom: 8px !important;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: $defaultSideBarFontColor !important;
|
||||
margin-left: 10%;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,11 +56,11 @@
|
||||
}
|
||||
|
||||
#extendedToolbar {
|
||||
display: flex;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
width: $defaultToolbarSize;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
@@ -83,14 +83,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#numberOfParticipants {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
line-height: 13px;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#mainToolbar a.button:last-child::after {
|
||||
content: none;
|
||||
}
|
||||
@@ -118,6 +110,10 @@
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.toggled {
|
||||
color: $toolbarButtonToggled;
|
||||
}
|
||||
|
||||
a.button.unclickable:hover,
|
||||
a.button.unclickable:active,
|
||||
a.button.unclickable.selected{
|
||||
@@ -129,6 +125,7 @@ a.button:hover,
|
||||
a.button:active,
|
||||
a.button.selected {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
// sum opacity with background layer should give us 0.8
|
||||
background: $toolbarSelectBackground;
|
||||
}
|
||||
@@ -144,6 +141,36 @@ a.button>#avatar {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Round badge.
|
||||
*/
|
||||
.badge-round {
|
||||
background-color: $toolbarBadgeBackground;
|
||||
color: $toolbarBadgeColor;
|
||||
font-size: 9px;
|
||||
line-height: 13px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
min-width: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
// Do not inherit the font-family from the toolbar button, because it's an
|
||||
// icon style.
|
||||
font-family: $baseFontFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar specific round badge.
|
||||
*/
|
||||
.toolbar .badge-round {
|
||||
position: absolute;
|
||||
right: 9px;
|
||||
bottom: 9px;
|
||||
}
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* Style variables
|
||||
*/
|
||||
$baseFontFamily: 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
@@ -11,20 +10,47 @@ $hangupFontSize: 2em;
|
||||
*/
|
||||
$defaultToolbarSize: 50px;
|
||||
|
||||
// Video layout.
|
||||
$thumbnailIndicatorSize: 23px;
|
||||
$thumbnailIndicatorBorder: 0px;
|
||||
$thumbnailVideoMargin: 2px;
|
||||
$thumbnailToolbarHeight: 25px;
|
||||
|
||||
/**
|
||||
* Color variables.
|
||||
*/
|
||||
$defaultColor: #F1F1F1;
|
||||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultSideBarFontColor: #44A5FF;
|
||||
$defaultDarkColor: #4F4F4F;
|
||||
$defaultBackground: #474747;
|
||||
$tooltipBg: rgba(0,0,0, 0.7);
|
||||
|
||||
// Toolbar
|
||||
$toolbarSelectBackground: rgba(0, 0, 0, .6);
|
||||
$toolbarButtonToggled: #44A5FF;
|
||||
|
||||
$toolbarBadgeBackground: #165ECC;
|
||||
$toolbarBadgeColor: #FFFFFF;
|
||||
|
||||
// Main controls
|
||||
$inputBackground: rgba(132, 132, 132, .5);
|
||||
$inputSemiBackground: rgba(132, 132, 132, .8);
|
||||
$inputLightBackground: #EBEBEB;
|
||||
$inputBorderColor: #EBEBEB;
|
||||
$buttonBackground: #44A5FF;
|
||||
|
||||
// Video layout.
|
||||
$videoThumbnailHovered: #44A5FF;
|
||||
$videoThumbnailSelected: #165ECC;
|
||||
$participantNameColor: #fff;
|
||||
$thumbnailPictogramColor: #fff;
|
||||
$dominantSpeakerBg: #165ecc;
|
||||
$raiseHandBg: #D6D61E;
|
||||
|
||||
$rateStarDefault: #ccc;
|
||||
$rateStarActivity: #165ecc;
|
||||
$rateStarLabelColor: #333;
|
||||
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
@@ -34,9 +60,17 @@ $defaultWatermarkLink: '../images/watermark.png';
|
||||
/**
|
||||
* Z-indexes. TODO: Replace this by a function.
|
||||
*/
|
||||
$tooltipsZ: 901;
|
||||
$toolbarZ: 900;
|
||||
$overlayZ: 800;
|
||||
|
||||
$rateStarDefault: #ccc;
|
||||
$rateStarActivity: #f6c342;
|
||||
$rateStarLabelColor: #333;
|
||||
/**
|
||||
* Font Colors TODO: Change colors when general dialogs are implemented.
|
||||
*/
|
||||
$defaultFontColor: #777;
|
||||
$defaultLightFontColor: #F1F1F1;
|
||||
$defaultDarkFontColor: #000;
|
||||
$buttonFontColor: #777;
|
||||
$buttonHoverFontColor: #287ade;
|
||||
$linkFontColor: #489afe;
|
||||
$linkHoverFontColor: #287ade;
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: row-reverse;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-start;
|
||||
|
||||
position:absolute;
|
||||
text-align:right;
|
||||
height:196px;
|
||||
padding: 18px;
|
||||
padding: 10px 10px 10px 5px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 20px;
|
||||
right: 0;
|
||||
width:auto;
|
||||
border:1px solid transparent;
|
||||
z-index: 5;
|
||||
@@ -43,12 +43,33 @@
|
||||
|
||||
#remoteVideos .videocontainer {
|
||||
display: none;
|
||||
position: relative;
|
||||
background-color: black;
|
||||
background-size: contain;
|
||||
border-radius:1px;
|
||||
border: 1px solid #212425;
|
||||
margin: 0 $thumbnailVideoMargin;
|
||||
border: 1px solid $defaultDarkColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The toolbar of the video thumbnail.
|
||||
*/
|
||||
.videocontainer__toolbar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box; // Includes the padding in the 100% width.
|
||||
height: $thumbnailToolbarHeight;
|
||||
max-height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focused video thumbnail.
|
||||
*/
|
||||
#remoteVideos .videocontainer.videoContainerFocused {
|
||||
cursor: hand;
|
||||
transition-duration: 0.5s;
|
||||
@@ -56,20 +77,12 @@
|
||||
-webkit-animation-name: greyPulse;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: 1;
|
||||
border: 1px solid $videoThumbnailSelected;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer:hover {
|
||||
border: 1px solid #c1c1c1;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer.videoContainerFocused {
|
||||
box-shadow: inset 0 0 28px #006d91;
|
||||
border: 1px solid #006d91;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer:hover,
|
||||
#remoteVideos .videocontainer.videoContainerFocused:hover {
|
||||
box-shadow: inset 0 0 5px #c1c1c1, 0 0 10px #c1c1c1, inset 0 0 60px #006d91;
|
||||
border: 1px solid #c1c1c1;
|
||||
border: 1px solid $videoThumbnailHovered;
|
||||
}
|
||||
|
||||
#localVideoWrapper {
|
||||
@@ -141,47 +154,36 @@
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer>span.focusindicator,
|
||||
#remoteVideos .videocontainer>div.remotevideomenu {
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 5px 0px;
|
||||
width: 25px;
|
||||
font-size: 11pt;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
border: 0px;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer>span.focusindicator {
|
||||
/**
|
||||
* Positions video thumbnail display name and editor.
|
||||
*/
|
||||
.videocontainer .displayname,
|
||||
.videocontainer .editdisplayname {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#remoteVideos .videocontainer>div.remotevideomenu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.videocontainer>span.displayname,
|
||||
.videocontainer>input.displayname {
|
||||
display: none;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
background: rgba(0,0,0,.7);
|
||||
left: 30%;
|
||||
width: 40%;
|
||||
color: $participantNameColor;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
width: 70%;
|
||||
height: 20%;
|
||||
left: 15%;
|
||||
top: 40%;
|
||||
padding: 5px;
|
||||
font-size: 11pt;
|
||||
font-size: 12px;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
z-index: 2;
|
||||
border-radius:3px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions video thumbnail display name editor.
|
||||
*/
|
||||
.videocontainer .editdisplayname {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.videocontainer>span.status {
|
||||
@@ -257,16 +259,16 @@
|
||||
}
|
||||
|
||||
#localVideoContainer>span.status:hover,
|
||||
#localVideoContainer>span.displayname:hover {
|
||||
#localVideoContainer .displayname:hover {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.videocontainer>span.status,
|
||||
.videocontainer>span.displayname {
|
||||
.videocontainer .displayname {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.videocontainer>input.displayname {
|
||||
.videocontainer .editdisplayname {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -287,53 +289,63 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.videocontainer>span.audioMuted {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
top: 0;
|
||||
padding: 8px 5px;
|
||||
width: 25px;
|
||||
/**
|
||||
* Video thumbnail toolbar icon.
|
||||
*/
|
||||
.videocontainer .toolbar-icon {
|
||||
font-size: 8pt;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
border: 0px;
|
||||
z-index: 3;
|
||||
text-align: center;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
color: #FFFFFF;
|
||||
width: 12px;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
height: $thumbnailToolbarHeight;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0px 5px 0px 0px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.videocontainer>span.videoMuted {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 8px 5px;
|
||||
width: 25px;
|
||||
font-size: 8pt;
|
||||
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
|
||||
border: 0px;
|
||||
z-index: 3;
|
||||
/**
|
||||
* Toolbar icon internal i elements (font icons).
|
||||
*/
|
||||
.toolbar-icon>i {
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar icons positioned on the right.
|
||||
*/
|
||||
.toolbar-icon.right {
|
||||
float: right;
|
||||
margin: 0px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.videocontainer>span.indicator {
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
@include circle($thumbnailIndicatorSize);
|
||||
box-sizing: border-box;
|
||||
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
z-index: 3;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
background: #21B9FC;
|
||||
margin: 5px;
|
||||
background: $dominantSpeakerBg;
|
||||
margin: 7px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
color: #FFFFFF;
|
||||
font-size: 11pt;
|
||||
border: 0px;
|
||||
color: $thumbnailPictogramColor;
|
||||
font-size: 8pt;
|
||||
border: $thumbnailIndicatorBorder solid $thumbnailPictogramColor;
|
||||
}
|
||||
|
||||
.videocontainer>#raisehandindicator {
|
||||
background: $raiseHandBg;
|
||||
}
|
||||
|
||||
#indicatoricon {
|
||||
padding-top: 5px;
|
||||
width: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
line-height: $thumbnailIndicatorSize - 2*$thumbnailIndicatorBorder;
|
||||
}
|
||||
|
||||
#reloadPresentation {
|
||||
@@ -395,10 +407,8 @@
|
||||
}
|
||||
|
||||
.userAvatar {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
border-radius: 2px;
|
||||
@include circle(60px);
|
||||
@include absoluteAligning(60px, 60px);
|
||||
}
|
||||
|
||||
.sharedVideoAvatar {
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
@import 'toastr';
|
||||
@import 'base';
|
||||
@import 'overlay/overlay';
|
||||
@import 'modals/dialog';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'videolayout_default';
|
||||
@import 'jquery-impromptu';
|
||||
@import 'modaldialog';
|
||||
@@ -38,7 +40,6 @@
|
||||
@import 'toolbars';
|
||||
@import 'side_toolbar_container';
|
||||
@import 'device_settings_dialog';
|
||||
@import 'feedback';
|
||||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
|
||||
|
||||
37
css/modals/_dialog.scss
Normal file
37
css/modals/_dialog.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
.dialog{
|
||||
visibility: visible;
|
||||
height: auto;
|
||||
|
||||
p {
|
||||
color: $defaultDarkFontColor;
|
||||
}
|
||||
.aui-dialog2-content:last-child {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
.aui-dialog2-content:first-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
.aui-dialog2-footer{
|
||||
padding-top: 0;
|
||||
}
|
||||
.aui-button {
|
||||
height: 36px;
|
||||
padding-top: 12px;
|
||||
border: none;
|
||||
background-color: transparent!important;
|
||||
border-left: solid 1px #e4e4e4;
|
||||
font-weight: 700;
|
||||
|
||||
&_close {
|
||||
color: $defaultFontColor;
|
||||
}
|
||||
&_submit {
|
||||
color: $linkFontColor;
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@
|
||||
}
|
||||
|
||||
.shake-rotate {
|
||||
display: inline-block;
|
||||
|
||||
-webkit-animation-duration: .4s;
|
||||
animation-duration: .4s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
@@ -43,65 +45,65 @@
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feedbackDetails textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.feedback-rating {
|
||||
line-height: 1.2;
|
||||
padding: 20px 0;
|
||||
|
||||
.feedback {
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
padding: auto;
|
||||
margin: auto;
|
||||
border: none;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
margin-left: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-right: 0px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.star-label {
|
||||
font-size: 16px;
|
||||
color: $rateStarLabelColor;
|
||||
&__content {
|
||||
text-align: center;
|
||||
}
|
||||
&__footer {
|
||||
|
||||
.star-btn {
|
||||
color: $rateStarDefault;
|
||||
font-size: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
@include transition(all .2s ease);
|
||||
|
||||
&.starHover,
|
||||
&.active,
|
||||
&:hover {
|
||||
color: $rateStarActivity;
|
||||
color: #287ade;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
&__rating {
|
||||
line-height: 1.2;
|
||||
padding: 20px 0;
|
||||
|
||||
.fa {
|
||||
top: -6px;
|
||||
}
|
||||
};
|
||||
|
||||
&.rated:hover .fa {
|
||||
top: 0;
|
||||
p {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.fa {
|
||||
.star-label {
|
||||
font-size: 16px;
|
||||
color: $rateStarLabelColor;
|
||||
}
|
||||
|
||||
.star-btn {
|
||||
color: $rateStarDefault;
|
||||
font-size: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
@include transition(all .2s ease);
|
||||
|
||||
&.starHover,
|
||||
&.active,
|
||||
&:hover {
|
||||
color: $rateStarActivity;
|
||||
> i:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
&__details {
|
||||
text-align: left;
|
||||
textarea {
|
||||
min-height: 100px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,11 @@
|
||||
background: linear-gradient(transparent, #000);
|
||||
opacity: 0.8;
|
||||
|
||||
&.solidBG {
|
||||
background: $defaultBackground;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&__content {
|
||||
position: absolute;
|
||||
width: 400px;
|
||||
@@ -33,4 +38,4 @@
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
fonts/jitsi.eot
BIN
fonts/jitsi.eot
Binary file not shown.
@@ -41,4 +41,7 @@
|
||||
<glyph unicode="" glyph-name="contactList" d="M704 746c-46 0-86-38-86-84s40-86 86-86 86 40 86 86-40 84-86 84zM704 512c-82 0-150 68-150 150s68 148 150 148 150-66 150-148-68-150-150-150zM320 746c-46 0-86-38-86-84s40-86 86-86 86 40 86 86-40 84-86 84zM320 512c-82 0-150 68-150 150s68 148 150 148 150-66 150-148-68-150-150-150zM918 278v52c0 24-110 76-214 76-46 0-90-12-128-24 14-16 22-32 22-52v-52h320zM534 278v52c0 24-110 76-214 76s-214-52-214-76v-52h428zM704 470c92 0 278-48 278-140v-116h-940v116c0 92 186 140 278 140 52 0 130-16 192-44 62 28 140 44 192 44z" />
|
||||
<glyph unicode="" glyph-name="toggle-filmstrip" d="M896 896h-768c-46.933 0-85.333-38.4-85.333-85.333v-597.333c0-46.933 38.4-85.333 85.333-85.333h768c46.933 0 85.333 38.4 85.333 85.333v597.333c0 46.933-38.4 85.333-85.333 85.333zM896 213.333h-768v128h768v-128z" />
|
||||
<glyph unicode="" glyph-name="feedback" d="M42.667 128h170.667v512h-170.667v-512zM981.333 597.333c0 46.933-38.4 85.333-85.333 85.333h-269.227l40.533 194.987 1.28 13.653c0 17.493-7.253 33.707-18.773 45.227l-45.227 44.8-280.747-281.173c-15.787-15.36-25.173-36.693-25.173-60.16v-426.667c0-46.933 38.4-85.333 85.333-85.333h384c35.413 0 65.707 21.333 78.507 52.053l128.853 300.8c3.84 9.813 5.973 20.053 5.973 31.147v81.493l-0.427 0.427 0.427 3.413z" />
|
||||
<glyph unicode="" glyph-name="raised-hand" d="M982 790v-620c0-94-78-170-172-170h-310c-46 0-90 18-122 50l-336 342s54 52 56 52c10 8 22 12 34 12 10 0 18-2 26-6 2 0 184-104 184-104v508c0 36 28 64 64 64s64-28 64-64v-300h42v406c0 36 28 64 64 64s64-28 64-64v-406h42v364c0 36 28 64 64 64s64-28 64-64v-364h44v236c0 36 28 64 64 64s64-28 64-64z" />
|
||||
<glyph unicode="" glyph-name="menu-up" d="M512 682l256-256-60-60-196 196-196-196-60 60z" />
|
||||
<glyph unicode="" glyph-name="menu-down" d="M708 658l60-60-256-256-256 256 60 60 196-196z" />
|
||||
</font></defs></svg>
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.ttf
Binary file not shown.
BIN
fonts/jitsi.woff
BIN
fonts/jitsi.woff
Binary file not shown.
@@ -69,8 +69,10 @@
|
||||
"tags": [
|
||||
"account_circle"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 11,
|
||||
"order": 60,
|
||||
@@ -93,8 +95,10 @@
|
||||
"tags": [
|
||||
"autorenew"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 68,
|
||||
"order": 84,
|
||||
@@ -117,8 +121,10 @@
|
||||
"tags": [
|
||||
"call_end"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 122,
|
||||
"order": 63,
|
||||
@@ -141,8 +147,10 @@
|
||||
"tags": [
|
||||
"chat_bubble_outline"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 148,
|
||||
"order": 61,
|
||||
@@ -165,8 +173,10 @@
|
||||
"tags": [
|
||||
"cloud_download"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 164,
|
||||
"order": 99,
|
||||
@@ -189,8 +199,10 @@
|
||||
"tags": [
|
||||
"mode_edit"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 185,
|
||||
"order": 89,
|
||||
@@ -213,8 +225,10 @@
|
||||
"tags": [
|
||||
"description"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 206,
|
||||
"order": 85,
|
||||
@@ -237,8 +251,10 @@
|
||||
"tags": [
|
||||
"dialer_sip"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 215,
|
||||
"order": 95,
|
||||
@@ -261,8 +277,10 @@
|
||||
"tags": [
|
||||
"eject"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 242,
|
||||
"order": 98,
|
||||
@@ -275,6 +293,58 @@
|
||||
"setId": 2,
|
||||
"iconIdx": 243
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M512 342l256 256-60 60-196-196-196 196-60-60z"
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"expand_less"
|
||||
],
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 256,
|
||||
"order": 106,
|
||||
"ligatures": "expand_less",
|
||||
"prevSize": 32,
|
||||
"code": 59679,
|
||||
"name": "menu-up"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 257
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M708 366l60 60-256 256-256-256 60-60 196 196z"
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"expand_more"
|
||||
],
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 257,
|
||||
"order": 107,
|
||||
"ligatures": "expand_more",
|
||||
"prevSize": 32,
|
||||
"code": 59680,
|
||||
"name": "menu-down"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 258
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -285,8 +355,10 @@
|
||||
"tags": [
|
||||
"fullscreen"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 350,
|
||||
"order": 94,
|
||||
@@ -309,8 +381,10 @@
|
||||
"tags": [
|
||||
"fullscreen_exit"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 351,
|
||||
"order": 92,
|
||||
@@ -333,8 +407,10 @@
|
||||
"tags": [
|
||||
"star"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 363,
|
||||
"order": 101,
|
||||
@@ -357,8 +433,10 @@
|
||||
"tags": [
|
||||
"lock_open"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 473,
|
||||
"order": 66,
|
||||
@@ -381,8 +459,10 @@
|
||||
"tags": [
|
||||
"lock_outline"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 474,
|
||||
"order": 65,
|
||||
@@ -405,8 +485,10 @@
|
||||
"tags": [
|
||||
"sync"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 482,
|
||||
"order": 67,
|
||||
@@ -429,8 +511,10 @@
|
||||
"tags": [
|
||||
"mic"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 492,
|
||||
"order": 68,
|
||||
@@ -453,8 +537,10 @@
|
||||
"tags": [
|
||||
"mic_none"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 493,
|
||||
"order": 69,
|
||||
@@ -477,8 +563,10 @@
|
||||
"tags": [
|
||||
"mic_off"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 494,
|
||||
"order": 70,
|
||||
@@ -491,6 +579,32 @@
|
||||
"setId": 2,
|
||||
"iconIdx": 495
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M982 234v620c0 94-78 170-172 170h-310c-46 0-90-18-122-50l-336-342s54-52 56-52c10-8 22-12 34-12 10 0 18 2 26 6 2 0 184 104 184 104v-508c0-36 28-64 64-64s64 28 64 64v300h42v-406c0-36 28-64 64-64s64 28 64 64v406h42v-364c0-36 28-64 64-64s64 28 64 64v364h44v-236c0-36 28-64 64-64s64 28 64 64z"
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"tags": [
|
||||
"pan_tool"
|
||||
],
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 539,
|
||||
"order": 105,
|
||||
"ligatures": "pan_tool",
|
||||
"prevSize": 32,
|
||||
"code": 59678,
|
||||
"name": "raised-hand"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"setId": 2,
|
||||
"iconIdx": 540
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
@@ -501,8 +615,10 @@
|
||||
"tags": [
|
||||
"people_outline"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 549,
|
||||
"order": 100,
|
||||
@@ -525,8 +641,10 @@
|
||||
"tags": [
|
||||
"person_add"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 559,
|
||||
"order": 87,
|
||||
@@ -549,8 +667,10 @@
|
||||
"tags": [
|
||||
"play_circle_outline"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 590,
|
||||
"order": 82,
|
||||
@@ -573,8 +693,10 @@
|
||||
"tags": [
|
||||
"settings"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 665,
|
||||
"order": 81,
|
||||
@@ -597,8 +719,10 @@
|
||||
"tags": [
|
||||
"star_border"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 717,
|
||||
"order": 76,
|
||||
@@ -621,8 +745,10 @@
|
||||
"tags": [
|
||||
"tv"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 783,
|
||||
"order": 93,
|
||||
@@ -645,8 +771,10 @@
|
||||
"tags": [
|
||||
"videocam"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 798,
|
||||
"order": 77,
|
||||
@@ -669,8 +797,10 @@
|
||||
"tags": [
|
||||
"videocam_off"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 799,
|
||||
"order": 78,
|
||||
@@ -693,8 +823,10 @@
|
||||
"tags": [
|
||||
"volume_up"
|
||||
],
|
||||
"grid": 0
|
||||
"grid": 0,
|
||||
"attrs": []
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 821,
|
||||
"order": 79,
|
||||
|
||||
39
index.html
39
index.html
@@ -120,26 +120,32 @@
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
<a class="button icon-contactList" id="toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="right" shortcut="contactlistpopover" data-i18n="[content]bottomtoolbar.contactlist" content="Open / close contact list">
|
||||
<span id="numberOfParticipants"></span>
|
||||
<a class="button icon-contactList" id="toolbar_contact_list" shortcut="contactlistpopover">
|
||||
<span class="badge-round">
|
||||
<span id="numberOfParticipants"></span>
|
||||
</span>
|
||||
</a>
|
||||
<a class="button icon-chat" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="right" data-i18n="[content]toolbar.chat" content="Open / close chat">
|
||||
<span id="unreadMessages"></span>
|
||||
<!--a class="button icon-link" id="toolbar_button_link"></a-->
|
||||
<a class="button icon-chat" id="toolbar_button_chat" shortcut="toggleChatPopover">
|
||||
<span class="badge-round">
|
||||
<span id="unreadMessages"></span>
|
||||
</span>
|
||||
</a>
|
||||
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="right" style="display: none"></a>
|
||||
<a class="button icon-security" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[content]toolbar.lock" content="Lock / unlock room"></a>
|
||||
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="right" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
|
||||
<a class="button icon-shared-video" id="toolbar_button_sharedvideo" data-container="body" data-toggle="popover" data-placement="right" content="Share a YouTube video" data-i18n="[content]toolbar.sharedvideo" style="display: none">
|
||||
<a class="button" id="toolbar_button_record" style="display: none"></a>
|
||||
<a class="button icon-security" id="toolbar_button_security"></a>
|
||||
<a class="button icon-share-doc" id="toolbar_button_etherpad"></a>
|
||||
<a class="button icon-shared-video" id="toolbar_button_sharedvideo" style="display: none">
|
||||
<ul id="sharedVideoMutedPopup" class="loginmenu extendedToolbarPopup">
|
||||
<li data-i18n="[html]toolbar.sharedVideoMutedPopup"></li>
|
||||
</ul>
|
||||
</a>
|
||||
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="right" 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="right" 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="right" content="Settings" data-i18n="[content]toolbar.Settings"></a>
|
||||
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="right" shortcut="toggleFullscreenPopover" data-i18n="[content]toolbar.fullscreen" content="Enter / Exit Full Screen"></a>
|
||||
<a class="button icon-toggle-filmstrip" id="toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="right" data-i18n="[content]toolbar.filmstrip" content="Show / hide videos"></a>
|
||||
<a class="button icon-feedback" id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[content]feedback"></a>
|
||||
<a class="button icon-telephone" id="toolbar_button_sip" style="display: none"></a>
|
||||
<a class="button icon-dialpad" id="toolbar_button_dialpad" style="display: none"></a>
|
||||
<a class="button icon-settings" id="toolbar_button_settings"></a>
|
||||
<a class="button icon-raised-hand" id="toolbar_button_raisehand" shortcut="raiseHandPopover"></a>
|
||||
<a class="button icon-full-screen" id="toolbar_button_fullScreen" shortcut="toggleFullscreenPopover"></a>
|
||||
<a class="button icon-toggle-filmstrip" id="toolbar_film_strip" data-container="body" shortcut="filmstripPopover"></a>
|
||||
<a class="button icon-feedback" id="feedbackButton"></a>
|
||||
<div id="sideToolbarContainer">
|
||||
<div id="profile_container" class="sideToolbarContainer__inner">
|
||||
<div class="title" data-i18n="profile.title"></div>
|
||||
@@ -206,7 +212,6 @@
|
||||
<input type="checkbox" id="followMeCheckBox">
|
||||
<label class="followMeLabel" for="followMeCheckBox" data-i18n="settings.followMe"></label>
|
||||
</div>
|
||||
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="icon-download"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -236,12 +241,13 @@
|
||||
</div>
|
||||
|
||||
<div id="remoteVideos">
|
||||
<span id="localVideoContainer" class="videocontainer">
|
||||
<span id="localVideoContainer" class="videocontainer videocontainer_small">
|
||||
<span id="localVideoWrapper">
|
||||
<!--<video id="localVideo" autoplay muted></video> - is now per stream generated -->
|
||||
</span>
|
||||
<audio id="localAudio" autoplay muted></audio>
|
||||
<span class="focusindicator"></span>
|
||||
<div class="videocontainer__toolbar"></div>
|
||||
</span>
|
||||
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
|
||||
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
|
||||
@@ -255,5 +261,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="aui-feedback-dialog" class="dialog feedback aui-layer aui-dialog2 aui-dialog2-medium" style="display: none;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -20,18 +20,25 @@ var interfaceConfig = {
|
||||
// the toolbar buttons line is intentionally left in one line, to be able
|
||||
// to easily override values or remove them using regex
|
||||
MAIN_TOOLBAR_BUTTONS: ['microphone', 'camera', 'desktop', 'invite', 'hangup'], // jshint ignore:line
|
||||
TOOLBAR_BUTTONS: ['profile', 'authentication', 'microphone', 'camera', 'desktop', 'recording', 'security', 'invite', 'chat', 'etherpad', 'sharedvideo', 'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip', 'contacts'], // jshint ignore:line
|
||||
TOOLBAR_BUTTONS: ['profile', 'authentication', 'microphone', 'camera', 'desktop', 'recording', 'security', 'raisehand', 'chat', 'etherpad', 'sharedvideo', 'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip', 'contacts'], // jshint ignore:line
|
||||
SETTINGS_SECTIONS: ['language', 'devices', 'moderator'],
|
||||
// Determines how the video would fit the screen. 'both' would fit the whole
|
||||
// screen, 'height' would fit the original video height to the height of the
|
||||
// screen, 'width' would fit the original video width to the width of the
|
||||
// screen respecting ratio.
|
||||
VIDEO_LAYOUT_FIT: 'both',
|
||||
SHOW_CONTACTLIST_AVATARS: false,
|
||||
/**
|
||||
* Whether to only show the filmstrip (and hide the toolbar).
|
||||
*/
|
||||
filmStripOnly: false,
|
||||
RANDOM_AVATAR_URL_PREFIX: false,
|
||||
RANDOM_AVATAR_URL_SUFFIX: false,
|
||||
FILM_STRIP_MAX_HEIGHT: 120
|
||||
};
|
||||
FILM_STRIP_MAX_HEIGHT: 120,
|
||||
LOCAL_THUMBNAIL_RATIO_WIDTH: 16,
|
||||
LOCAL_THUMBNAIL_RATIO_HEIGHT: 9,
|
||||
REMOTE_THUMBNAIL_RATIO_WIDTH: 1,
|
||||
REMOTE_THUMBNAIL_RATIO_HEIGHT: 1,
|
||||
// Enables feedback star animation.
|
||||
ENABLE_FEEDBACK_ANIMATION: false
|
||||
};
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
{
|
||||
"contactlist": "ON CALL (__participants__)",
|
||||
"contactlist": "On Call",
|
||||
"connectionsettings": "Connection Settings",
|
||||
"poweredby": "powered by",
|
||||
"downloadlogs": "Download logs",
|
||||
"feedback": "Give us your feedback",
|
||||
"roomUrlDefaultMsg": "Your conference is currently being created...",
|
||||
"participant": "Participant",
|
||||
"me": "me",
|
||||
"speaker": "Speaker",
|
||||
"raisedHand": "Would like to speak",
|
||||
@@ -94,7 +92,8 @@
|
||||
"unableToUnmutePopup": "You cannot un-mute while the shared video is on.",
|
||||
"cameraDisabled": "Camera is not available",
|
||||
"micDisabled": "Microphone is not available",
|
||||
"filmstrip": "Show / hide videos"
|
||||
"filmstrip": "Show / hide videos",
|
||||
"raiseHand": "Raise hand to speak"
|
||||
},
|
||||
"bottomtoolbar": {
|
||||
"chat": "Open / close chat",
|
||||
@@ -110,21 +109,21 @@
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"title": "SETTINGS",
|
||||
"title": "Settings",
|
||||
"update": "Update",
|
||||
"name": "Name",
|
||||
"startAudioMuted": "Everyone starts muted",
|
||||
"startVideoMuted": "Everyone starts hidden",
|
||||
"selectCamera": "Select camera",
|
||||
"selectMic": "Select microphone",
|
||||
"selectAudioOutput": "Select audio output",
|
||||
"selectCamera": "Camera",
|
||||
"selectMic": "Microphone",
|
||||
"selectAudioOutput": "Audio output",
|
||||
"followMe": "Everyone follows me",
|
||||
"noDevice": "None",
|
||||
"noPermission": "Permission to use device is not granted",
|
||||
"cameraAndMic": "Camera and microphone",
|
||||
"moderator": "MODERATOR",
|
||||
"password": "SET PASSWORD",
|
||||
"audioVideo": "AUDIO / VIDEO",
|
||||
"audioVideo": "AUDIO AND VIDEO",
|
||||
"setPasswordLabel": "Lock your room with a password."
|
||||
},
|
||||
"profile": {
|
||||
@@ -136,7 +135,7 @@
|
||||
{
|
||||
"editnickname": "Click to edit your<br/>display name",
|
||||
"moderator": "The owner of<br/>this conference",
|
||||
"videomute": "Participant has<br/>stopped the camera.",
|
||||
"videomute": "Participant has<br/>stopped the camera",
|
||||
"mute": "Participant is muted",
|
||||
"kick": "Kick out",
|
||||
"muted": "Muted",
|
||||
@@ -197,8 +196,8 @@
|
||||
"lockMessage": "Failed to lock the conference.",
|
||||
"warning": "Warning",
|
||||
"passwordNotSupported": "Room passwords are currently not supported.",
|
||||
"sorry": "Sorry",
|
||||
"internalError": "Internal application error [setRemoteDescription]",
|
||||
"internalErrorTitle": "Internal error",
|
||||
"internalError": "Oups! Something went wrong. The following error occurred: [setRemoteDescription]",
|
||||
"unableToSwitch": "Unable to switch video stream.",
|
||||
"SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
|
||||
"SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
|
||||
@@ -246,13 +245,14 @@
|
||||
"password": "Enter password",
|
||||
"userPassword": "user password",
|
||||
"token": "token",
|
||||
"tokenAuthFailed": "Failed to authenticate with XMPP server: invalid token",
|
||||
"tokenAuthFailedTitle": "Authentication problem",
|
||||
"tokenAuthFailed": "Sorry, you're not allowed to join this call.",
|
||||
"displayNameRequired": "Please enter your display name",
|
||||
"extensionRequired": "Extension required:",
|
||||
"firefoxExtensionPrompt": "You need to install a Firefox extension in order to use screen sharing. Please try again after you <a href='__url__'>get it from here</a>!",
|
||||
"rateExperience": "Please rate your meeting experience.",
|
||||
"feedbackHelp": "Your feedback will help us to improve our video experience.",
|
||||
"feedbackQuestion": "How was your call?",
|
||||
"feedbackQuestion": "Tell us about your call!",
|
||||
"thankYou": "Thank you for using __appName__!",
|
||||
"sorryFeedback": "We're sorry to hear that. Would you like to tell us more?",
|
||||
"liveStreaming": "Live Streaming",
|
||||
@@ -277,6 +277,7 @@
|
||||
"micNotFoundError": "Microphone was not found.",
|
||||
"micConstraintFailedError": "Yor microphone does not satisfy some of required constraints.",
|
||||
"micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to restart the application.",
|
||||
"cameraNotSendingData": "We are unable to access your camera. Please check if another application is using this device, select another device from the settings menu or try to restart the application.",
|
||||
"goToStore": "Go to the webstore",
|
||||
"externalInstallationTitle": "Extension required",
|
||||
"externalInstallationMsg": "You need to install our desktop sharing extension."
|
||||
|
||||
@@ -1,321 +0,0 @@
|
||||
/* global $, APP, config, interfaceConfig, JitsiMeetJS */
|
||||
import UIEvents from "../../service/UI/UIEvents";
|
||||
|
||||
/**
|
||||
* Constructs the html for the overall feedback window.
|
||||
*
|
||||
* @returns {string} the constructed html string
|
||||
*/
|
||||
var constructOverallFeedbackHtml = function() {
|
||||
var feedbackQuestion = (Feedback.feedbackScore < 0)
|
||||
? '<br/><br/>' + APP.translation
|
||||
.translateString("dialog.feedbackQuestion")
|
||||
: '';
|
||||
|
||||
var message = '<div class="feedback"><div>' +
|
||||
'<div class="feedbackTitle">' +
|
||||
APP.translation.translateString("dialog.thankYou",
|
||||
{appName:interfaceConfig.APP_NAME}) +
|
||||
'</div>' +
|
||||
feedbackQuestion +
|
||||
'</div><br/><br/>' +
|
||||
'<div id="stars">' +
|
||||
'<a><i class="icon-star icon-star-full"></i></a>' +
|
||||
'<a><i class="icon-star icon-star-full"></i></a>' +
|
||||
'<a><i class="icon-star icon-star-full"></i></a>' +
|
||||
'<a><i class="icon-star icon-star-full"></i></a>' +
|
||||
'<a><i class="icon-star icon-star-full"></i></a>' +
|
||||
'</div></div>';
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs the html for the detailed feedback window.
|
||||
*
|
||||
* @returns {string} the contructed html string
|
||||
*/
|
||||
var constructDetailedFeedbackHtml = function() {
|
||||
// Construct the html, which will be served as a dialog message.
|
||||
var message = '<div class="feedback">' +
|
||||
'<div class="feedbackTitle">' +
|
||||
APP.translation.translateString("dialog.sorryFeedback") +
|
||||
'</div><br/><br/>' +
|
||||
'<div class="feedbackDetails">' +
|
||||
'<textarea id="feedbackTextArea" rows="10" cols="50" autofocus>' +
|
||||
'</textarea>' +
|
||||
'</div></div>';
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
var createRateFeedbackHTML = function () {
|
||||
var rate = APP.translation.translateString('dialog.rateExperience'),
|
||||
help = APP.translation.translateString('dialog.feedbackHelp');
|
||||
|
||||
return `
|
||||
<div class="feedback-rating text-center">
|
||||
<h2>${ rate }</h2>
|
||||
<p class="star-label"> </p>
|
||||
<div id="stars" class="feedback-stars">
|
||||
<a class="star-btn">
|
||||
<i class="fa fa-star shake-rotate"></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class="fa fa-star shake-rotate"></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class="fa fa-star shake-rotate"></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class="fa fa-star shake-rotate"></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class="fa fa-star shake-rotate"></i>
|
||||
</a>
|
||||
</div>
|
||||
<p> </p>
|
||||
<p>${ help }</p>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback function corresponding to the openFeedbackWindow parameter.
|
||||
*
|
||||
* @type {function}
|
||||
*/
|
||||
var feedbackWindowCallback = null;
|
||||
|
||||
/**
|
||||
* Shows / hides the feedback button.
|
||||
* @private
|
||||
*/
|
||||
function _toggleFeedbackIcon() {
|
||||
$('#feedbackButtonDiv').toggleClass("hidden");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows / hides the feedback button.
|
||||
* @param {show} set to {true} to show the feedback button or to {false}
|
||||
* to hide it
|
||||
* @private
|
||||
*/
|
||||
function _showFeedbackButton (show) {
|
||||
var feedbackButton = $("#feedbackButtonDiv");
|
||||
|
||||
if (show)
|
||||
feedbackButton.css("display", "block");
|
||||
else
|
||||
feedbackButton.css("display", "none");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines all methods in connection to the Feedback window.
|
||||
*
|
||||
* @type {{feedbackScore: number, openFeedbackWindow: Function,
|
||||
* toggleStars: Function, hoverStars: Function, unhoverStars: Function}}
|
||||
*/
|
||||
var Feedback = {
|
||||
/**
|
||||
* The feedback score. -1 indicates no score has been given for now.
|
||||
*/
|
||||
feedbackScore: -1,
|
||||
|
||||
/**
|
||||
* Initialise the Feedback functionality.
|
||||
* @param emitter the EventEmitter to associate with the Feedback.
|
||||
*/
|
||||
init: function (emitter) {
|
||||
// CallStats is the way we send feedback, so we don't have to initialise
|
||||
// if callstats isn't enabled.
|
||||
if (!APP.conference.isCallstatsEnabled())
|
||||
return;
|
||||
|
||||
// If enabled property is still undefined, i.e. it hasn't been set from
|
||||
// some other module already, we set it to true by default.
|
||||
if (typeof this.enabled == "undefined")
|
||||
this.enabled = true;
|
||||
|
||||
_showFeedbackButton(this.enabled);
|
||||
|
||||
$("#feedbackButton").click(function (event) {
|
||||
Feedback.openFeedbackWindow();
|
||||
});
|
||||
|
||||
// Show / hide the feedback button whenever the film strip is
|
||||
// shown / hidden.
|
||||
emitter.addListener(UIEvents.TOGGLE_FILM_STRIP, function () {
|
||||
_toggleFeedbackIcon();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Enables/ disabled the feedback feature.
|
||||
*/
|
||||
enableFeedback: function (enable) {
|
||||
if (this.enabled !== enable)
|
||||
_showFeedbackButton(enable);
|
||||
this.enabled = enable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if the feedback functionality is enabled.
|
||||
*
|
||||
* @return true if the feedback functionality is enabled, false otherwise.
|
||||
*/
|
||||
isEnabled: function() {
|
||||
return this.enabled && APP.conference.isCallstatsEnabled();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the feedback window is currently visible and false
|
||||
* otherwise.
|
||||
* @return {boolean} true if the feedback window is visible, false
|
||||
* otherwise
|
||||
*/
|
||||
isVisible: function() {
|
||||
return $(".feedback").is(":visible");
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the feedback window.
|
||||
*/
|
||||
openFeedbackWindow: function (callback) {
|
||||
feedbackWindowCallback = callback;
|
||||
// Add all mouse and click listeners.
|
||||
var onLoadFunction = function (event) {
|
||||
$('#stars >a').each(function(index) {
|
||||
// On star mouse over.
|
||||
$(this).get(0).onmouseover = function(){
|
||||
Feedback.hoverStars(index);
|
||||
};
|
||||
// On star mouse leave.
|
||||
$(this).get(0).onmouseleave = function(){
|
||||
Feedback.unhoverStars(index);
|
||||
};
|
||||
// On star click.
|
||||
$(this).get(0).onclick = function(){
|
||||
Feedback.toggleStars(index);
|
||||
Feedback.feedbackScore = index+1;
|
||||
|
||||
// If the feedback is less than 3 stars we're going to
|
||||
// ask the user for more information.
|
||||
if (Feedback.feedbackScore > 3) {
|
||||
APP.conference.sendFeedback(Feedback.feedbackScore, "");
|
||||
if (feedbackWindowCallback)
|
||||
feedbackWindowCallback();
|
||||
else
|
||||
APP.UI.messageHandler.closeDialog();
|
||||
}
|
||||
else {
|
||||
feedbackDialog.goToState('detailed_feedback');
|
||||
}
|
||||
};
|
||||
// Init stars to correspond to previously entered feedback.
|
||||
if (Feedback.feedbackScore > 0
|
||||
&& index < Feedback.feedbackScore) {
|
||||
Feedback.hoverStars(index);
|
||||
Feedback.toggleStars(index);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Defines the different states of the feedback window.
|
||||
var states = {
|
||||
overall_feedback: {
|
||||
html: createRateFeedbackHTML(),
|
||||
persistent: false,
|
||||
buttons: {},
|
||||
closeText: '',
|
||||
focus: "div[id='stars']",
|
||||
position: {width: 500}
|
||||
},
|
||||
detailed_feedback: {
|
||||
html: constructDetailedFeedbackHtml(),
|
||||
buttons: {"Submit": true, "Cancel": false},
|
||||
closeText: '',
|
||||
focus: "textarea[id='feedbackTextArea']",
|
||||
position: {width: 500},
|
||||
submit: function(e,v,m,f) {
|
||||
e.preventDefault();
|
||||
if (v) {
|
||||
var feedbackDetails
|
||||
= document.getElementById("feedbackTextArea").value;
|
||||
|
||||
if (feedbackDetails && feedbackDetails.length > 0) {
|
||||
APP.conference.sendFeedback( Feedback.feedbackScore,
|
||||
feedbackDetails);
|
||||
}
|
||||
|
||||
if (feedbackWindowCallback)
|
||||
feedbackWindowCallback();
|
||||
else
|
||||
APP.UI.messageHandler.closeDialog();
|
||||
} else {
|
||||
// User cancelled
|
||||
if (feedbackWindowCallback)
|
||||
feedbackWindowCallback();
|
||||
else
|
||||
APP.UI.messageHandler.closeDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create the feedback dialog.
|
||||
var feedbackDialog
|
||||
= APP.UI.messageHandler.openDialogWithStates(
|
||||
states,
|
||||
{ persistent: false,
|
||||
buttons: {},
|
||||
closeText: '',
|
||||
loaded: onLoadFunction,
|
||||
position: {width: 500}}, null);
|
||||
JitsiMeetJS.analytics.sendEvent('feedback.open');
|
||||
},
|
||||
/**
|
||||
* Toggles the appropriate css class for the given number of stars, to
|
||||
* indicate that those stars have been clicked/selected.
|
||||
*
|
||||
* @param starCount the number of stars, for which to toggle the css class
|
||||
*/
|
||||
toggleStars: function (starCount)
|
||||
{
|
||||
$('#stars >a >i').each(function(index) {
|
||||
if (index <= starCount) {
|
||||
$(this).removeClass("icon-star");
|
||||
}
|
||||
else
|
||||
$(this).addClass("icon-star");
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Toggles the appropriate css class for the given number of stars, to
|
||||
* indicate that those stars have been hovered.
|
||||
*
|
||||
* @param starCount the number of stars, for which to toggle the css class
|
||||
*/
|
||||
hoverStars: function (starCount)
|
||||
{
|
||||
$('#stars >a >i').each(function(index) {
|
||||
if (index <= starCount)
|
||||
$(this).addClass("starHover");
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Toggles the appropriate css class for the given number of stars, to
|
||||
* indicate that those stars have been un-hovered.
|
||||
*
|
||||
* @param starCount the number of stars, for which to toggle the css class
|
||||
*/
|
||||
unhoverStars: function (starCount)
|
||||
{
|
||||
$('#stars >a >i').each(function(index) {
|
||||
if (index <= starCount && $(this).hasClass("icon-star"))
|
||||
$(this).removeClass("starHover");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Exports the Feedback class.
|
||||
module.exports = Feedback;
|
||||
@@ -29,7 +29,7 @@ var EventEmitter = require("events");
|
||||
UI.messageHandler = require("./util/MessageHandler");
|
||||
var messageHandler = UI.messageHandler;
|
||||
var JitsiPopover = require("./util/JitsiPopover");
|
||||
var Feedback = require("./Feedback");
|
||||
var Feedback = require("./feedback/Feedback");
|
||||
|
||||
import FollowMe from "../FollowMe";
|
||||
|
||||
@@ -60,6 +60,8 @@ JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[TrackErrors.NOT_FOUND]
|
||||
= "dialog.cameraNotFoundError";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[TrackErrors.CONSTRAINT_FAILED]
|
||||
= "dialog.cameraConstraintFailedError";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[TrackErrors.NO_DATA_FROM_SOURCE]
|
||||
= "dialog.cameraNotSendingData";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.GENERAL]
|
||||
= "dialog.micUnknownError";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.PERMISSION_DENIED]
|
||||
@@ -68,6 +70,8 @@ JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.NOT_FOUND]
|
||||
= "dialog.micNotFoundError";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.CONSTRAINT_FAILED]
|
||||
= "dialog.micConstraintFailedError";
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[TrackErrors.NO_DATA_FROM_SOURCE]
|
||||
= "dialog.micNotSendingData";
|
||||
|
||||
/**
|
||||
* Prompt user for nickname.
|
||||
@@ -292,7 +296,7 @@ UI.initConference = function () {
|
||||
}
|
||||
|
||||
// Add myself to the contact list.
|
||||
ContactList.addContact(id);
|
||||
ContactList.addContact(id, true);
|
||||
|
||||
//update default button states before showing the toolbar
|
||||
//if local role changes buttons state will be again updated
|
||||
@@ -339,6 +343,22 @@ UI.handleToggleFilmStrip = () => {
|
||||
VideoLayout.resizeVideoArea(true, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets tooltip defaults.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function _setTooltipDefaults() {
|
||||
$.fn.tooltip.defaults = {
|
||||
opacity: 1, //defaults to 1
|
||||
offset: 1,
|
||||
delayIn: 0, //defaults to 500
|
||||
hoverable: true,
|
||||
hideOnClick: true,
|
||||
aria: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup some UI event listeners.
|
||||
*/
|
||||
@@ -431,6 +451,9 @@ UI.start = function () {
|
||||
// Set the defaults for prompt dialogs.
|
||||
$.prompt.setDefaults({persistent: false});
|
||||
|
||||
// Set the defaults for tooltips.
|
||||
_setTooltipDefaults();
|
||||
|
||||
registerListeners();
|
||||
|
||||
ToolbarToggler.init();
|
||||
@@ -463,20 +486,10 @@ UI.start = function () {
|
||||
$('#noticeText').text(config.noticeMessage);
|
||||
$('#notice').css({display: 'block'});
|
||||
}
|
||||
$("#downloadlog").click(function (event) {
|
||||
let logs = APP.conference.getLogs();
|
||||
let data = encodeURIComponent(JSON.stringify(logs, null, ' '));
|
||||
|
||||
let elem = event.target.parentNode;
|
||||
elem.download = 'meetlog.json';
|
||||
elem.href = 'data:application/json;charset=utf-8,\n' + data;
|
||||
});
|
||||
} else {
|
||||
$("#mainToolbarContainer").css("display", "none");
|
||||
$("#downloadlog").css("display", "none");
|
||||
FilmStrip.setupFilmStripOnly();
|
||||
messageHandler.enableNotifications(false);
|
||||
$('body').popover("disable");
|
||||
JitsiPopover.enabled = false;
|
||||
}
|
||||
|
||||
@@ -1108,7 +1121,7 @@ UI.requestFeedback = function () {
|
||||
if (Feedback.isEnabled()) {
|
||||
// If the user has already entered feedback, we'll show the
|
||||
// window and immidiately start the conference dispose timeout.
|
||||
if (Feedback.feedbackScore > 0) {
|
||||
if (Feedback.getFeedbackScore() > 0) {
|
||||
Feedback.openFeedbackWindow();
|
||||
resolve();
|
||||
|
||||
@@ -1134,11 +1147,13 @@ UI.updateRecordingState = function (state) {
|
||||
};
|
||||
|
||||
UI.notifyTokenAuthFailed = function () {
|
||||
messageHandler.showError("dialog.error", "dialog.tokenAuthFailed");
|
||||
messageHandler.showError( "dialog.tokenAuthFailedTitle",
|
||||
"dialog.tokenAuthFailed");
|
||||
};
|
||||
|
||||
UI.notifyInternalError = function () {
|
||||
messageHandler.showError("dialog.sorry", "dialog.internalError");
|
||||
messageHandler.showError( "dialog.internalErrorTitle",
|
||||
"dialog.internalError");
|
||||
};
|
||||
|
||||
UI.notifyFocusDisconnected = function (focus, retrySec) {
|
||||
@@ -1193,6 +1208,16 @@ UI.onStartMutedChanged = function (startAudioMuted, startVideoMuted) {
|
||||
SettingsMenu.updateStartMutedBox(startAudioMuted, startVideoMuted);
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies interested listeners that the raise hand property has changed.
|
||||
*
|
||||
* @param {boolean} isRaisedHand indicates the current state of the
|
||||
* "raised hand"
|
||||
*/
|
||||
UI.onLocalRaiseHandChanged = function (isRaisedHand) {
|
||||
eventEmitter.emit(UIEvents.LOCAL_RAISE_HAND_CHANGED, isRaisedHand);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update list of available physical devices.
|
||||
* @param {object[]} devices new list of available devices
|
||||
@@ -1415,12 +1440,13 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
|
||||
|
||||
/**
|
||||
* Shows error dialog that informs the user that no data is received from the
|
||||
* microphone.
|
||||
* device.
|
||||
*/
|
||||
UI.showAudioNotWorkingDialog = function () {
|
||||
UI.showTrackNotWorkingDialog = function (stream) {
|
||||
messageHandler.openMessageDialog(
|
||||
"dialog.error",
|
||||
"dialog.micNotSendingData",
|
||||
stream.isAudioTrack()? "dialog.micNotSendingData" :
|
||||
"dialog.cameraNotSendingData",
|
||||
null,
|
||||
null);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ let ASDrawContext = null;
|
||||
let audioLevelCanvasCache = {};
|
||||
let dominantSpeakerAudioElement = null;
|
||||
|
||||
function initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) {
|
||||
function _initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) {
|
||||
let ASRadius = dominantSpeakerAvatarSize / 2;
|
||||
let ASCenter = (dominantSpeakerAvatarSize + ASRadius) / 2;
|
||||
|
||||
@@ -28,7 +28,9 @@ function initDominantSpeakerAudioLevels(dominantSpeakerAvatarSize) {
|
||||
/**
|
||||
* Resizes the given audio level canvas to match the given thumbnail size.
|
||||
*/
|
||||
function resizeAudioLevelCanvas(audioLevelCanvas, thumbnailWidth, thumbnailHeight) {
|
||||
function _resizeAudioLevelCanvas( audioLevelCanvas,
|
||||
thumbnailWidth,
|
||||
thumbnailHeight) {
|
||||
audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
|
||||
audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
|
||||
}
|
||||
@@ -138,18 +140,18 @@ const AudioLevels = {
|
||||
dominantSpeakerAudioElement.height = dominantSpeakerHeight;
|
||||
|
||||
let dominantSpeakerAvatar = $("#dominantSpeakerAvatar");
|
||||
initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width());
|
||||
_initDominantSpeakerAudioLevels(dominantSpeakerAvatar.width());
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the audio level canvas for the given id. If the canvas
|
||||
* didn't exist we create it.
|
||||
*/
|
||||
updateAudioLevelCanvas (id, thumbWidth, thumbHeight) {
|
||||
let videoSpanId = 'localVideoContainer';
|
||||
if (id) {
|
||||
videoSpanId = `participant_${id}`;
|
||||
}
|
||||
createAudioLevelCanvas (id, thumbWidth, thumbHeight) {
|
||||
|
||||
let videoSpanId = (id === "local")
|
||||
? "localVideoContainer"
|
||||
: `participant_${id}`;
|
||||
|
||||
let videoSpan = document.getElementById(videoSpanId);
|
||||
|
||||
@@ -172,13 +174,13 @@ const AudioLevels = {
|
||||
= `-${interfaceConfig.CANVAS_EXTRA/2}px`;
|
||||
audioLevelCanvas.style.left
|
||||
= `-${interfaceConfig.CANVAS_EXTRA/2}px`;
|
||||
resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
|
||||
_resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
|
||||
|
||||
videoSpan.appendChild(audioLevelCanvas);
|
||||
} else {
|
||||
audioLevelCanvas = audioLevelCanvas.get(0);
|
||||
|
||||
resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
|
||||
_resizeAudioLevelCanvas(audioLevelCanvas, thumbWidth, thumbHeight);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -242,19 +244,29 @@ const AudioLevels = {
|
||||
ASDrawContext.fill();
|
||||
},
|
||||
|
||||
updateCanvasSize (thumbWidth, thumbHeight) {
|
||||
let canvasWidth = thumbWidth + interfaceConfig.CANVAS_EXTRA;
|
||||
let canvasHeight = thumbHeight + interfaceConfig.CANVAS_EXTRA;
|
||||
updateCanvasSize (localVideo, remoteVideo) {
|
||||
let localCanvasWidth
|
||||
= localVideo.thumbWidth + interfaceConfig.CANVAS_EXTRA;
|
||||
let localCanvasHeight
|
||||
= localVideo.thumbHeight + interfaceConfig.CANVAS_EXTRA;
|
||||
let remoteCanvasWidth
|
||||
= remoteVideo.thumbWidth + interfaceConfig.CANVAS_EXTRA;
|
||||
let remoteCanvasHeight
|
||||
= remoteVideo.thumbHeight + interfaceConfig.CANVAS_EXTRA;
|
||||
|
||||
FilmStrip.getThumbs().children('canvas').each(function () {
|
||||
$(this).attr('width', canvasWidth);
|
||||
$(this).attr('height', canvasHeight);
|
||||
let { remoteThumbs, localThumb } = FilmStrip.getThumbs();
|
||||
|
||||
remoteThumbs.children('canvas').each(function () {
|
||||
$(this).attr('width', remoteCanvasWidth);
|
||||
$(this).attr('height', remoteCanvasHeight);
|
||||
});
|
||||
|
||||
Object.keys(audioLevelCanvasCache).forEach(function (id) {
|
||||
audioLevelCanvasCache[id].width = canvasWidth;
|
||||
audioLevelCanvasCache[id].height = canvasHeight;
|
||||
});
|
||||
if(localThumb) {
|
||||
localThumb.children('canvas').each(function () {
|
||||
$(this).attr('width', localCanvasWidth);
|
||||
$(this).attr('height', localCanvasHeight);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -185,6 +185,10 @@ export default function createRoomLocker (room) {
|
||||
newPass => { password = newPass; }
|
||||
).catch(
|
||||
reason => {
|
||||
// user canceled, no pass was entered.
|
||||
// clear, as if we use the same instance several times
|
||||
// pass stays between attempts
|
||||
password = null;
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
}
|
||||
|
||||
105
modules/UI/feedback/Feedback.js
Normal file
105
modules/UI/feedback/Feedback.js
Normal file
@@ -0,0 +1,105 @@
|
||||
/* global $, APP, config, interfaceConfig, JitsiMeetJS */
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import FeedabckWindow from "./FeedbackWindow";
|
||||
|
||||
/**
|
||||
* Shows / hides the feedback button.
|
||||
* @private
|
||||
*/
|
||||
function _toggleFeedbackIcon() {
|
||||
$('#feedbackButtonDiv').toggleClass("hidden");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows / hides the feedback button.
|
||||
* @param {show} set to {true} to show the feedback button or to {false}
|
||||
* to hide it
|
||||
* @private
|
||||
*/
|
||||
function _showFeedbackButton (show) {
|
||||
var feedbackButton = $("#feedbackButtonDiv");
|
||||
|
||||
if (show)
|
||||
feedbackButton.css("display", "block");
|
||||
else
|
||||
feedbackButton.css("display", "none");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines all methods in connection to the Feedback window.
|
||||
*
|
||||
* @type {{openFeedbackWindow: Function}}
|
||||
*/
|
||||
var Feedback = {
|
||||
|
||||
/**
|
||||
* Initialise the Feedback functionality.
|
||||
* @param emitter the EventEmitter to associate with the Feedback.
|
||||
*/
|
||||
init: function (emitter) {
|
||||
// CallStats is the way we send feedback, so we don't have to initialise
|
||||
// if callstats isn't enabled.
|
||||
if (!APP.conference.isCallstatsEnabled())
|
||||
return;
|
||||
|
||||
// If enabled property is still undefined, i.e. it hasn't been set from
|
||||
// some other module already, we set it to true by default.
|
||||
if (typeof this.enabled == "undefined")
|
||||
this.enabled = true;
|
||||
|
||||
_showFeedbackButton(this.enabled);
|
||||
|
||||
this.window = new FeedabckWindow({});
|
||||
|
||||
$("#feedbackButton").click(Feedback.openFeedbackWindow);
|
||||
|
||||
// Show / hide the feedback button whenever the film strip is
|
||||
// shown / hidden.
|
||||
emitter.addListener(UIEvents.TOGGLE_FILM_STRIP, function () {
|
||||
_toggleFeedbackIcon();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Enables/ disabled the feedback feature.
|
||||
*/
|
||||
enableFeedback: function (enable) {
|
||||
if (this.enabled !== enable)
|
||||
_showFeedbackButton(enable);
|
||||
this.enabled = enable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates if the feedback functionality is enabled.
|
||||
*
|
||||
* @return true if the feedback functionality is enabled, false otherwise.
|
||||
*/
|
||||
isEnabled: function() {
|
||||
return this.enabled && APP.conference.isCallstatsEnabled();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the feedback window is currently visible and false
|
||||
* otherwise.
|
||||
* @return {boolean} true if the feedback window is visible, false
|
||||
* otherwise
|
||||
*/
|
||||
isVisible: function() {
|
||||
return $(".feedback").is(":visible");
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the feedback window.
|
||||
*/
|
||||
openFeedbackWindow: function (callback) {
|
||||
Feedback.window.show(callback);
|
||||
|
||||
JitsiMeetJS.analytics.sendEvent('feedback.open');
|
||||
},
|
||||
|
||||
getFeedbackScore: function() {
|
||||
return Feedback.window.feedbackScore;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = Feedback;
|
||||
223
modules/UI/feedback/FeedbackWindow.js
Normal file
223
modules/UI/feedback/FeedbackWindow.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/* global $, APP, interfaceConfig, AJS */
|
||||
/* jshint -W101 */
|
||||
|
||||
const selector = '#aui-feedback-dialog';
|
||||
|
||||
/**
|
||||
* Toggles the appropriate css class for the given number of stars, to
|
||||
* indicate that those stars have been clicked/selected.
|
||||
*
|
||||
* @param starCount the number of stars, for which to toggle the css class
|
||||
*/
|
||||
let toggleStars = function(starCount) {
|
||||
$('#stars > a').each(function(index, el) {
|
||||
if (index <= starCount) {
|
||||
el.classList.add("starHover");
|
||||
} else
|
||||
el.classList.remove("starHover");
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs the html for the detailed feedback window.
|
||||
*
|
||||
* @returns {string} the contructed html string
|
||||
*/
|
||||
let constructDetailedFeedbackHtml = function() {
|
||||
|
||||
return `
|
||||
<div class="aui-dialog2-content feedback__content">
|
||||
<div class="feedback__details">
|
||||
<p>${APP.translation.translateString("dialog.sorryFeedback")}</p>
|
||||
<br/><br/>
|
||||
<textarea id="feedbackTextArea" rows="10" cols="50" autofocus></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="aui-dialog2-footer feedback__footer">
|
||||
<div class="aui-dialog2-footer-actions">
|
||||
<button id="dialog-close-button" class="aui-button aui-button_close">Close</button>
|
||||
<button id="dialog-submit-button" class="aui-button aui-button_submit">Submit</button>
|
||||
</div>
|
||||
</footer>
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs the html for the rated feedback window.
|
||||
*
|
||||
* @returns {string} the contructed html string
|
||||
*/
|
||||
let createRateFeedbackHTML = function (Feedback) {
|
||||
let rateExperience
|
||||
= APP.translation.translateString('dialog.rateExperience'),
|
||||
feedbackHelp = APP.translation.translateString('dialog.feedbackHelp'),
|
||||
feedbackQuestion = (Feedback.feedbackScore < 0)
|
||||
? `<p><br/>${APP.translation.translateString('dialog.feedbackQuestion')}</p>`
|
||||
: '';
|
||||
|
||||
let starClassName = (interfaceConfig.ENABLE_FEEDBACK_ANIMATION)
|
||||
? "icon-star shake-rotate"
|
||||
: "icon-star";
|
||||
|
||||
return `
|
||||
<div class="aui-dialog2-content feedback__content">
|
||||
${feedbackQuestion}
|
||||
<form action="javascript:false;" onsubmit="return false;">
|
||||
<div class="feedback__rating">
|
||||
<h2>${ rateExperience }</h2>
|
||||
<p class="star-label"> </p>
|
||||
<div id="stars" class="feedback-stars">
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
</div>
|
||||
<p> </p>
|
||||
<p>${ feedbackHelp }</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for Rate Feedback
|
||||
*
|
||||
* @param Feedback
|
||||
*/
|
||||
let onLoadRateFunction = function (Feedback) {
|
||||
$('#stars > a').each((index, el) => {
|
||||
el.onmouseover = function(){
|
||||
toggleStars(index);
|
||||
};
|
||||
el.onmouseleave = function(){
|
||||
toggleStars(Feedback.feedbackScore - 1);
|
||||
};
|
||||
el.onclick = function(){
|
||||
Feedback.feedbackScore = index + 1;
|
||||
|
||||
// If the feedback is less than 3 stars we're going to
|
||||
// ask the user for more information.
|
||||
if (Feedback.feedbackScore > 3) {
|
||||
APP.conference.sendFeedback(Feedback.feedbackScore, "");
|
||||
Feedback.hide();
|
||||
} else {
|
||||
Feedback.setState('detailed_feedback');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Init stars to correspond to previously entered feedback.
|
||||
if (Feedback.feedbackScore > 0) {
|
||||
toggleStars(Feedback.feedbackScore - 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for Detailed Feedback
|
||||
*
|
||||
* @param Feedback
|
||||
*/
|
||||
let onLoadDetailedFunction = function(Feedback) {
|
||||
let submitBtn = Feedback.$el.find('#dialog-submit-button');
|
||||
let closeBtn = Feedback.$el.find('#dialog-close-button');
|
||||
|
||||
if (submitBtn && submitBtn.length) {
|
||||
submitBtn.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
Feedback.onFeedbackSubmitted();
|
||||
});
|
||||
}
|
||||
if (closeBtn && closeBtn.length) {
|
||||
closeBtn.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
Feedback.hide();
|
||||
});
|
||||
}
|
||||
|
||||
$('#feedbackTextArea').focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Dialog
|
||||
*
|
||||
*/
|
||||
export default class Dialog {
|
||||
|
||||
constructor(options) {
|
||||
this.feedbackScore = -1;
|
||||
this.onCloseCallback = null;
|
||||
|
||||
this.states = {
|
||||
rate_feedback: {
|
||||
getHtml: createRateFeedbackHTML,
|
||||
onLoad: onLoadRateFunction
|
||||
},
|
||||
detailed_feedback: {
|
||||
getHtml: constructDetailedFeedbackHtml,
|
||||
onLoad: onLoadDetailedFunction
|
||||
}
|
||||
};
|
||||
this.state = options.state || 'rate_feedback';
|
||||
|
||||
this.window = AJS.dialog2(selector, {
|
||||
closeOnOutsideClick: true
|
||||
});
|
||||
this.$el = this.window.$el;
|
||||
|
||||
this.setState();
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
let newState = state || this.state;
|
||||
|
||||
let htmlStr = this.states[newState].getHtml(this);
|
||||
|
||||
this.$el.html(htmlStr);
|
||||
|
||||
this.states[newState].onLoad(this);
|
||||
}
|
||||
|
||||
show(cb) {
|
||||
this.setState('rate_feedback');
|
||||
if (typeof cb == 'function') {
|
||||
this.onCloseCallback = cb;
|
||||
}
|
||||
|
||||
this.window.show();
|
||||
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.window.hide();
|
||||
|
||||
if (this.onCloseCallback) {
|
||||
this.onCloseCallback();
|
||||
this.onCloseCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
onFeedbackSubmitted() {
|
||||
let message = this.$el.find('textarea').val();
|
||||
let self = this;
|
||||
|
||||
if (message && message.length > 0) {
|
||||
APP.conference.sendFeedback(
|
||||
self.feedbackScore,
|
||||
message);
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import VideoLayout from '../videolayout/VideoLayout';
|
||||
import Feedback from '../Feedback.js';
|
||||
import Feedback from '../feedback/Feedback.js';
|
||||
import Toolbar from '../toolbars/Toolbar';
|
||||
|
||||
/**
|
||||
@@ -270,6 +270,9 @@ var Recording = {
|
||||
initRecordingButton(recordingType) {
|
||||
let selector = $('#toolbar_button_record');
|
||||
|
||||
let button = selector.get(0);
|
||||
UIUtil.setTooltip(button, 'liveStreaming.buttonTooltip', 'right');
|
||||
|
||||
if (recordingType === 'jibri') {
|
||||
this.baseClass = "fa fa-play-circle";
|
||||
this.recordingTitle = "dialog.liveStreaming";
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
/* global $ */
|
||||
/* global $, APP */
|
||||
/* jshint -W101 */
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
|
||||
/**
|
||||
* Store the current ring overlay instance.
|
||||
* Note: We want to have only 1 instance at a time.
|
||||
*/
|
||||
let overlay = null;
|
||||
|
||||
/**
|
||||
* Handler for UIEvents.LARGE_VIDEO_AVATAR_DISPLAYED event.
|
||||
* @param {boolean} shown indicates whether the avatar on the large video is
|
||||
* currently displayed or not.
|
||||
*/
|
||||
function onAvatarDisplayed(shown) {
|
||||
overlay._changeBackground(shown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows ring overlay
|
||||
@@ -11,25 +27,46 @@ class RingOverlay {
|
||||
constructor(callee) {
|
||||
this._containerId = 'ringOverlay';
|
||||
this._audioContainerId = 'ringOverlayRinging';
|
||||
|
||||
this.isRinging = true;
|
||||
this.callee = callee;
|
||||
this.render();
|
||||
this.audio = document.getElementById(this._audioContainerId);
|
||||
this.audio.play();
|
||||
this._setAudioTimeout();
|
||||
this._timeout = setTimeout(() => {
|
||||
this.destroy();
|
||||
this.render();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chagnes the background of the ring overlay.
|
||||
* @param {boolean} solid - if true the new background will be the solid
|
||||
* one, otherwise the background will be default one.
|
||||
* NOTE: The method just toggles solidBG css class.
|
||||
*/
|
||||
_changeBackground(solid) {
|
||||
const container = $("#" + this._containerId);
|
||||
if(solid) {
|
||||
container.addClass("solidBG");
|
||||
} else {
|
||||
container.removeClass("solidBG");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and appends the ring overlay to the html document
|
||||
*/
|
||||
_getHtmlStr(callee) {
|
||||
let callingLabel = this.isRinging? "<p>Calling...</p>" : "";
|
||||
let callerStateLabel = this.isRinging? "" : " isn't available";
|
||||
return `
|
||||
<div id="${this._containerId}" class='ringing' >
|
||||
<div class='ringing__content'>
|
||||
<p>Calling...</p>
|
||||
${callingLabel}
|
||||
<img class='ringing__avatar' src="${callee.getAvatarUrl()}" />
|
||||
<div class="ringing__caller-info">
|
||||
<p>${callee.getName()}</p>
|
||||
<p>${callee.getName()}${callerStateLabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
<audio id="${this._audioContainerId}" src="/sounds/ring.ogg" />
|
||||
@@ -49,10 +86,7 @@ class RingOverlay {
|
||||
* related to the ring overlay.
|
||||
*/
|
||||
destroy() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
this._stopAudio();
|
||||
this._detach();
|
||||
}
|
||||
|
||||
@@ -64,6 +98,16 @@ class RingOverlay {
|
||||
$(`#${this._containerId}`).remove();
|
||||
}
|
||||
|
||||
_stopAudio() {
|
||||
this.isRinging = false;
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
if(this._timeout) {
|
||||
clearTimeout(this._timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interval that is going to play the ringing sound.
|
||||
*/
|
||||
@@ -74,12 +118,6 @@ class RingOverlay {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current ring overlay instance.
|
||||
* Note: We want to have only 1 instance at a time.
|
||||
*/
|
||||
let overlay = null;
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Shows the ring overlay for the passed callee.
|
||||
@@ -92,6 +130,8 @@ export default {
|
||||
}
|
||||
|
||||
overlay = new RingOverlay(callee);
|
||||
APP.UI.addListener(UIEvents.LARGE_VIDEO_AVATAR_DISPLAYED,
|
||||
onAvatarDisplayed);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -104,6 +144,8 @@ export default {
|
||||
}
|
||||
overlay.destroy();
|
||||
overlay = null;
|
||||
APP.UI.removeListener(UIEvents.LARGE_VIDEO_AVATAR_DISPLAYED,
|
||||
onAvatarDisplayed);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
@@ -3,20 +3,24 @@
|
||||
import {processReplacements, linkify} from './Replacement';
|
||||
import CommandsProcessor from './Commands';
|
||||
import ToolbarToggler from '../../toolbars/ToolbarToggler';
|
||||
import VideoLayout from "../../videolayout/VideoLayout";
|
||||
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
var smileys = require("./smileys.json").smileys;
|
||||
import { smileys } from './smileys';
|
||||
|
||||
var notificationInterval = false;
|
||||
var unreadMessages = 0;
|
||||
|
||||
/**
|
||||
* The container id, which is and the element id.
|
||||
*/
|
||||
var CHAT_CONTAINER_ID = "chat_container";
|
||||
|
||||
/**
|
||||
* Shows/hides a visual notification, indicating that a message has arrived.
|
||||
* Updates visual notification, indicating that a message has arrived.
|
||||
*/
|
||||
function setVisualNotification(show) {
|
||||
function updateVisualNotification() {
|
||||
var unreadMsgElement = document.getElementById('unreadMessages');
|
||||
|
||||
var glower = $('#toolbar_button_chat');
|
||||
@@ -37,28 +41,12 @@ function setVisualNotification(show) {
|
||||
'style',
|
||||
'top:' + topIndent +
|
||||
'; left:' + leftIndent + ';');
|
||||
|
||||
if (!glower.hasClass('icon-chat-simple')) {
|
||||
glower.removeClass('icon-chat');
|
||||
glower.addClass('icon-chat-simple');
|
||||
}
|
||||
}
|
||||
else {
|
||||
unreadMsgElement.innerHTML = '';
|
||||
glower.removeClass('icon-chat-simple');
|
||||
glower.addClass('icon-chat');
|
||||
}
|
||||
|
||||
if (show && !notificationInterval) {
|
||||
notificationInterval = window.setInterval(function () {
|
||||
glower.toggleClass('active');
|
||||
}, 800);
|
||||
}
|
||||
else if (!show && notificationInterval) {
|
||||
window.clearInterval(notificationInterval);
|
||||
notificationInterval = false;
|
||||
glower.removeClass('active');
|
||||
}
|
||||
$(unreadMsgElement).parent()[unreadMessages > 0 ? 'show' : 'hide']();
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +119,7 @@ function addSmileys() {
|
||||
*/
|
||||
function resizeChatConversation() {
|
||||
var msgareaHeight = $('#usermsg').outerHeight();
|
||||
var chatspace = $('#chat_container');
|
||||
var chatspace = $('#' + CHAT_CONTAINER_ID);
|
||||
var width = chatspace.width();
|
||||
var chat = $('#chatconversation');
|
||||
var smileys = $('#smileysarea');
|
||||
@@ -187,13 +175,30 @@ var Chat = {
|
||||
};
|
||||
usermsg.autosize({callback: onTextAreaResize});
|
||||
|
||||
$("#chat_container").bind("shown",
|
||||
function () {
|
||||
eventEmitter.on(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
|
||||
function(containerId, isVisible) {
|
||||
if (containerId !== CHAT_CONTAINER_ID || !isVisible)
|
||||
return;
|
||||
|
||||
unreadMessages = 0;
|
||||
setVisualNotification(false);
|
||||
updateVisualNotification();
|
||||
|
||||
// Undock the toolbar when the chat is shown and if we're in a
|
||||
// video mode.
|
||||
if (VideoLayout.isLargeVideoVisible()) {
|
||||
ToolbarToggler.dockToolbar(false);
|
||||
}
|
||||
|
||||
// if we are in conversation mode focus on the text input
|
||||
// if we are not, focus on the display name input
|
||||
if (APP.settings.getDisplayName())
|
||||
$('#usermsg').focus();
|
||||
else
|
||||
$('#nickinput').focus();
|
||||
});
|
||||
|
||||
addSmileys();
|
||||
updateVisualNotification();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -210,7 +215,7 @@ var Chat = {
|
||||
if (!Chat.isVisible()) {
|
||||
unreadMessages++;
|
||||
UIUtil.playSoundNotification('chatNotification');
|
||||
setVisualNotification(true);
|
||||
updateVisualNotification();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,12 +276,18 @@ var Chat = {
|
||||
|
||||
/**
|
||||
* Sets the chat conversation mode.
|
||||
* Conversation mode is the normal chat mode, non conversation mode is
|
||||
* where we ask user to input its display name.
|
||||
* @param {boolean} isConversationMode if chat should be in
|
||||
* conversation mode or not.
|
||||
*/
|
||||
setChatConversationMode (isConversationMode) {
|
||||
$('#chat_container')
|
||||
$('#' + CHAT_CONTAINER_ID)
|
||||
.toggleClass('is-conversation-mode', isConversationMode);
|
||||
|
||||
// this is needed when we transition from no conversation mode to
|
||||
// conversation mode. When user enters his nickname and hits enter,
|
||||
// to focus on the write area.
|
||||
if (isConversationMode) {
|
||||
$('#usermsg').focus();
|
||||
}
|
||||
@@ -286,7 +297,7 @@ var Chat = {
|
||||
* Resizes the chat area.
|
||||
*/
|
||||
resizeChat (width, height) {
|
||||
$('#chat_container').width(width).height(height);
|
||||
$('#' + CHAT_CONTAINER_ID).width(width).height(height);
|
||||
|
||||
resizeChatConversation();
|
||||
},
|
||||
@@ -296,7 +307,7 @@ var Chat = {
|
||||
*/
|
||||
isVisible () {
|
||||
return UIUtil.isVisible(
|
||||
document.getElementById("chat_container"));
|
||||
document.getElementById(CHAT_CONTAINER_ID));
|
||||
},
|
||||
/**
|
||||
* Shows and hides the window with the smileys
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* jshint -W101 */
|
||||
var Smileys = require("./smileys.json");
|
||||
import { regexes } from './smileys';
|
||||
|
||||
/**
|
||||
* Processes links and smileys in "body"
|
||||
@@ -29,7 +29,7 @@ export function linkify(inputText) {
|
||||
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
|
||||
replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');
|
||||
|
||||
//Change email addresses to mailto:: links.
|
||||
//Change email addresses to mailto: links.
|
||||
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
|
||||
replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
|
||||
|
||||
@@ -44,10 +44,9 @@ function smilify(body) {
|
||||
return body;
|
||||
}
|
||||
|
||||
var regexs = Smileys.regexs;
|
||||
for(var smiley in regexs) {
|
||||
if(regexs.hasOwnProperty(smiley)) {
|
||||
body = body.replace(regexs[smiley],
|
||||
for(var smiley in regexes) {
|
||||
if(regexes.hasOwnProperty(smiley)) {
|
||||
body = body.replace(regexes[smiley],
|
||||
'<img class="smiley" src="images/smileys/' + smiley + '.svg">');
|
||||
}
|
||||
}
|
||||
|
||||
47
modules/UI/side_pannels/chat/smileys.js
Normal file
47
modules/UI/side_pannels/chat/smileys.js
Normal file
@@ -0,0 +1,47 @@
|
||||
export const smileys = {
|
||||
smiley1: ":)",
|
||||
smiley2: ":(",
|
||||
smiley3: ":D",
|
||||
smiley4: "(y)",
|
||||
smiley5: " :P",
|
||||
smiley6: "(wave)",
|
||||
smiley7: "(blush)",
|
||||
smiley8: "(chuckle)",
|
||||
smiley9: "(shocked)",
|
||||
smiley10: ":*",
|
||||
smiley11: "(n)",
|
||||
smiley12: "(search)",
|
||||
smiley13: " <3",
|
||||
smiley14: "(oops)",
|
||||
smiley15: "(angry)",
|
||||
smiley16: "(angel)",
|
||||
smiley17: "(sick)",
|
||||
smiley18: ";(",
|
||||
smiley19: "(bomb)",
|
||||
smiley20: "(clap)",
|
||||
smiley21: " ;)"
|
||||
};
|
||||
|
||||
export const regexes = {
|
||||
smiley2: /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi,
|
||||
smiley3: /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi,
|
||||
smiley1: /(:-\)|:\))/gi,
|
||||
smiley4: /(\(y\)|\(Y\)|\(ok\))/gi,
|
||||
smiley5: /(:-P|:P|:-p|:p)/gi,
|
||||
smiley6: /(\(wave\))/gi,
|
||||
smiley7: /(\(blush\))/gi,
|
||||
smiley8: /(\(chuckle\))/gi,
|
||||
smiley9: /(:-0|\(shocked\))/gi,
|
||||
smiley10: /(:-\*|:\*|\(kiss\))/gi,
|
||||
smiley11: /(\(n\))/gi,
|
||||
smiley12: /(\(search\))/g,
|
||||
smiley13: /(<3|<3|&lt;3|\(L\)|\(l\)|\(H\)|\(h\))/gi,
|
||||
smiley14: /(\(oops\))/gi,
|
||||
smiley15: /(\(angry\))/gi,
|
||||
smiley16: /(\(angel\))/gi,
|
||||
smiley17: /(\(sick\))/gi,
|
||||
smiley18: /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi,
|
||||
smiley19: /(\(bomb\))/gi,
|
||||
smiley20: /(\(clap\))/gi,
|
||||
smiley21: /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"smileys": {
|
||||
"smiley1": ":)",
|
||||
"smiley2": ":(",
|
||||
"smiley3": ":D",
|
||||
"smiley4": "(y)",
|
||||
"smiley5": " :P",
|
||||
"smiley6": "(wave)",
|
||||
"smiley7": "(blush)",
|
||||
"smiley8": "(chuckle)",
|
||||
"smiley9": "(shocked)",
|
||||
"smiley10": ":*",
|
||||
"smiley11": "(n)",
|
||||
"smiley12": "(search)",
|
||||
"smiley13": " <3",
|
||||
"smiley14": "(oops)",
|
||||
"smiley15": "(angry)",
|
||||
"smiley16": "(angel)",
|
||||
"smiley17": "(sick)",
|
||||
"smiley18": ";(",
|
||||
"smiley19": "(bomb)",
|
||||
"smiley20": "(clap)",
|
||||
"smiley21": " ;)"
|
||||
},
|
||||
"regexs": {
|
||||
"smiley2": /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi,
|
||||
"smiley3": /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi,
|
||||
"smiley1": /(:-\)|:\))/gi,
|
||||
"smiley4": /(\(y\)|\(Y\)|\(ok\))/gi,
|
||||
"smiley5": /(:-P|:P|:-p|:p)/gi,
|
||||
"smiley6": /(\(wave\))/gi,
|
||||
"smiley7": /(\(blush\))/gi,
|
||||
"smiley8": /(\(chuckle\))/gi,
|
||||
"smiley9": /(:-0|\(shocked\))/gi,
|
||||
"smiley10": /(:-\*|:\*|\(kiss\))/gi,
|
||||
"smiley11": /(\(n\))/gi,
|
||||
"smiley12": /(\(search\))/g,
|
||||
"smiley13": /(<3|<3|&lt;3|\(L\)|\(l\)|\(H\)|\(h\))/gi,
|
||||
"smiley14": /(\(oops\))/gi,
|
||||
"smiley15": /(\(angry\))/gi,
|
||||
"smiley16": /(\(angel\))/gi,
|
||||
"smiley17": /(\(sick\))/gi,
|
||||
"smiley18": /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi,
|
||||
"smiley19": /(\(bomb\))/gi,
|
||||
"smiley20": /(\(clap\))/gi,
|
||||
"smiley21": /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, APP */
|
||||
/* global $, APP, interfaceConfig */
|
||||
import Avatar from '../../avatar/Avatar';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
@@ -21,12 +21,13 @@ function updateNumberOfParticipants(delta) {
|
||||
}
|
||||
|
||||
let buttonIndicatorText = (numberOfContacts === 1) ? '' : numberOfContacts;
|
||||
$("#numberOfParticipants").text(buttonIndicatorText);
|
||||
$("#numberOfParticipants")
|
||||
.text(buttonIndicatorText)
|
||||
.parent()[numberOfContacts > 1 ? 'show' : 'hide']();
|
||||
|
||||
$("#contacts_container>div.title").text(
|
||||
APP.translation.translateString(
|
||||
"contactlist", {participants: numberOfContacts}
|
||||
));
|
||||
APP.translation.translateString("contactlist")
|
||||
+ ' (' + numberOfContacts + ')');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,9 +97,9 @@ var ContactList = {
|
||||
|
||||
/**
|
||||
* Adds a contact for the given id.
|
||||
*
|
||||
* @param isLocal is an id for the local user.
|
||||
*/
|
||||
addContact (id) {
|
||||
addContact (id, isLocal) {
|
||||
let contactlist = $('#contacts');
|
||||
|
||||
let newContact = document.createElement('li');
|
||||
@@ -110,8 +111,13 @@ var ContactList = {
|
||||
}
|
||||
};
|
||||
|
||||
newContact.appendChild(createAvatar(id));
|
||||
newContact.appendChild(createDisplayNameParagraph("participant"));
|
||||
if (interfaceConfig.SHOW_CONTACTLIST_AVATARS)
|
||||
newContact.appendChild(createAvatar(id));
|
||||
|
||||
newContact.appendChild(
|
||||
createDisplayNameParagraph(
|
||||
isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
|
||||
isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
|
||||
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
contactlist.prepend(newContact);
|
||||
|
||||
@@ -74,10 +74,7 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
// Only show the subtitle if this isn't the only setting section.
|
||||
if (interfaceConfig.SETTINGS_SECTIONS.length > 1)
|
||||
UIUtil.showElement("deviceOptionsTitle");
|
||||
|
||||
UIUtil.showElement("deviceOptionsTitle");
|
||||
UIUtil.showElement("devicesOptions");
|
||||
}
|
||||
|
||||
@@ -150,8 +147,7 @@ export default {
|
||||
showStartMutedOptions (show) {
|
||||
if (show && UIUtil.isSettingEnabled('moderator')) {
|
||||
// Only show the subtitle if this isn't the only setting section.
|
||||
if (!$("#moderatorOptionsTitle").is(":visible")
|
||||
&& interfaceConfig.SETTINGS_SECTIONS.length > 1)
|
||||
if (!$("#moderatorOptionsTitle").is(":visible"))
|
||||
UIUtil.showElement("moderatorOptionsTitle");
|
||||
|
||||
UIUtil.showElement("startMutedOptions");
|
||||
|
||||
@@ -7,7 +7,6 @@ import SideContainerToggler from "../side_pannels/SideContainerToggler";
|
||||
let roomUrl = null;
|
||||
let emitter = null;
|
||||
|
||||
|
||||
/**
|
||||
* Opens the invite link dialog.
|
||||
*/
|
||||
@@ -168,14 +167,20 @@ const buttonHandlers = {
|
||||
},
|
||||
"toolbar_film_strip": function () {
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'bottomtoolbar.filmstrip.toggled');
|
||||
'toolbar.filmstrip.toggled');
|
||||
emitter.emit(UIEvents.TOGGLE_FILM_STRIP);
|
||||
},
|
||||
"toolbar_button_raisehand": function () {
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'toolbar.raiseHand.clicked');
|
||||
APP.conference.maybeToggleRaisedHand();
|
||||
}
|
||||
};
|
||||
|
||||
const defaultToolbarButtons = {
|
||||
'microphone': {
|
||||
id: 'toolbar_button_mute',
|
||||
tooltipKey: 'toolbar.mute',
|
||||
className: "button icon-microphone",
|
||||
shortcut: 'M',
|
||||
shortcutAttr: 'mutePopover',
|
||||
@@ -201,6 +206,7 @@ const defaultToolbarButtons = {
|
||||
},
|
||||
'camera': {
|
||||
id: 'toolbar_button_camera',
|
||||
tooltipKey: 'toolbar.videomute',
|
||||
className: "button icon-camera",
|
||||
shortcut: 'V',
|
||||
shortcutAttr: 'toggleVideoPopover',
|
||||
@@ -214,6 +220,7 @@ const defaultToolbarButtons = {
|
||||
},
|
||||
'desktop': {
|
||||
id: 'toolbar_button_desktopsharing',
|
||||
tooltipKey: 'toolbar.sharescreen',
|
||||
className: 'button icon-share-desktop',
|
||||
shortcut: 'D',
|
||||
shortcutAttr: 'toggleDesktopSharingPopover',
|
||||
@@ -226,16 +233,19 @@ const defaultToolbarButtons = {
|
||||
i18n: '[content]toolbar.sharescreen'
|
||||
},
|
||||
'security': {
|
||||
id: 'toolbar_button_security'
|
||||
id: 'toolbar_button_security',
|
||||
tooltipKey: 'toolbar.lock'
|
||||
},
|
||||
'invite': {
|
||||
id: 'toolbar_button_link',
|
||||
tooltipKey: 'toolbar.invite',
|
||||
className: 'button icon-link',
|
||||
content: 'Invite others',
|
||||
i18n: '[content]toolbar.invite'
|
||||
},
|
||||
'chat': {
|
||||
id: 'toolbar_button_chat',
|
||||
tooltipKey: 'toolbar.chat',
|
||||
shortcut: 'C',
|
||||
shortcutAttr: 'toggleChatPopover',
|
||||
shortcutFunc: function() {
|
||||
@@ -247,17 +257,21 @@ const defaultToolbarButtons = {
|
||||
},
|
||||
'contacts': {
|
||||
id: 'toolbar_contact_list',
|
||||
tooltipKey: 'bottomtoolbar.contactlist',
|
||||
sideContainerId: 'contacts_container'
|
||||
},
|
||||
'profile': {
|
||||
id: 'toolbar_button_profile',
|
||||
tooltipKey: 'profile.setDisplayNameLabel',
|
||||
sideContainerId: 'profile_container'
|
||||
},
|
||||
'etherpad': {
|
||||
id: 'toolbar_button_etherpad'
|
||||
id: 'toolbar_button_etherpad',
|
||||
tooltipKey: 'toolbar.etherpad',
|
||||
},
|
||||
'fullscreen': {
|
||||
id: 'toolbar_button_fullScreen',
|
||||
tooltipKey: 'toolbar.fullscreen',
|
||||
className: "button icon-full-screen",
|
||||
shortcut: 'F',
|
||||
shortcutAttr: 'toggleFullscreenPopover',
|
||||
@@ -271,16 +285,19 @@ const defaultToolbarButtons = {
|
||||
},
|
||||
'settings': {
|
||||
id: 'toolbar_button_settings',
|
||||
tooltipKey: 'toolbar.Settings',
|
||||
sideContainerId: "settings_container"
|
||||
},
|
||||
'hangup': {
|
||||
id: 'toolbar_button_hangup',
|
||||
tooltipKey: 'toolbar.hangup',
|
||||
className: "button icon-hangup",
|
||||
content: "Hang Up",
|
||||
i18n: "[content]toolbar.hangup"
|
||||
},
|
||||
'filmstrip': {
|
||||
id: 'toolbar_film_strip',
|
||||
tooltipKey: 'toolbar.filmstrip',
|
||||
shortcut: "F",
|
||||
shortcutAttr: "filmstripPopover",
|
||||
shortcutFunc: function() {
|
||||
@@ -288,6 +305,20 @@ const defaultToolbarButtons = {
|
||||
APP.UI.toggleFilmStrip();
|
||||
},
|
||||
shortcutDescription: "keyboardShortcuts.toggleFilmstrip"
|
||||
},
|
||||
'raisehand': {
|
||||
id: "toolbar_button_raisehand",
|
||||
tooltipKey: 'toolbar.raiseHand',
|
||||
className: "button icon-raised-hand",
|
||||
shortcut: "R",
|
||||
shortcutAttr: "raiseHandPopover",
|
||||
shortcutFunc: function() {
|
||||
JitsiMeetJS.analytics.sendEvent("shortcut.raisehand.clicked");
|
||||
APP.conference.maybeToggleRaisedHand();
|
||||
},
|
||||
shortcutDescription: "keyboardShortcuts.raiseHand",
|
||||
content: "Raise Hand",
|
||||
i18n: "[content]toolbar.raiseHand"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -323,14 +354,28 @@ const Toolbar = {
|
||||
this.toolbarSelector = $("#mainToolbarContainer");
|
||||
this.extendedToolbarSelector = $("#extendedToolbar");
|
||||
|
||||
this._initMainToolbarButtons();
|
||||
|
||||
// First hide all disabled buttons in the extended toolbar.
|
||||
// TODO: Make the extended toolbar dynamically created.
|
||||
UIUtil.hideDisabledButtons(defaultToolbarButtons);
|
||||
|
||||
// Initialise the main toolbar. The main toolbar will only take into
|
||||
// account it's own configuration from interface_config.
|
||||
this._initMainToolbarButtons();
|
||||
|
||||
Object.keys(defaultToolbarButtons).forEach(
|
||||
id => {
|
||||
if (UIUtil.isButtonEnabled(id)) {
|
||||
var button = defaultToolbarButtons[id];
|
||||
let button = defaultToolbarButtons[id];
|
||||
let buttonElement = document.getElementById(button.id);
|
||||
|
||||
let tooltipPosition
|
||||
= (interfaceConfig.MAIN_TOOLBAR_BUTTONS
|
||||
.indexOf(id) > -1)
|
||||
? "bottom" : "right";
|
||||
|
||||
UIUtil.setTooltip( buttonElement,
|
||||
button.tooltipKey,
|
||||
tooltipPosition);
|
||||
|
||||
if (button.shortcut)
|
||||
APP.keyboardshortcut.registerShortcut(
|
||||
@@ -355,8 +400,15 @@ const Toolbar = {
|
||||
isVisible);
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.LOCAL_RAISE_HAND_CHANGED,
|
||||
function(isRaisedHand) {
|
||||
Toolbar._toggleRaiseHand(isRaisedHand);
|
||||
});
|
||||
|
||||
if(!APP.tokenData.isGuest) {
|
||||
$("#toolbar_button_profile").addClass("unclickable");
|
||||
UIUtil.removeTooltip(
|
||||
document.getElementById('toolbar_button_profile'));
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -431,9 +483,11 @@ const Toolbar = {
|
||||
|
||||
// Shows or hides the 'shared video' button.
|
||||
showSharedVideoButton () {
|
||||
let $element = $('#toolbar_button_sharedvideo');
|
||||
if (UIUtil.isButtonEnabled('sharedvideo')
|
||||
&& config.disableThirdPartyRequests !== true) {
|
||||
$('#toolbar_button_sharedvideo').css({display: "inline-block"});
|
||||
$element.css({display: "inline-block"});
|
||||
UIUtil.setTooltip($element.get(0), 'toolbar.sharedvideo', 'right');
|
||||
} else {
|
||||
$('#toolbar_button_sharedvideo').css({display: "none"});
|
||||
}
|
||||
@@ -518,6 +572,13 @@ const Toolbar = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles / untoggles the view for raised hand.
|
||||
*/
|
||||
_toggleRaiseHand(isRaisedHand) {
|
||||
$('#toolbar_button_raisehand').toggleClass("toggled", isRaisedHand);
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks video icon as muted or not.
|
||||
* @param {boolean} muted if icon should look like muted or not
|
||||
@@ -679,16 +740,9 @@ const Toolbar = {
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Fix mic popups
|
||||
* <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">
|
||||
* <ul id="micMutedPopup" class="loginmenu">
|
||||
* <li data-i18n="[html]toolbar.micMutedPopup"></li>
|
||||
* </ul>
|
||||
* <ul id="unableToUnmutePopup" class="loginmenu">
|
||||
* <li data-i18n="[html]toolbar.unableToUnmutePopup"></li>
|
||||
* </ul>
|
||||
* </a>
|
||||
* Initialise main toolbar buttons.
|
||||
*/
|
||||
_initMainToolbarButtons() {
|
||||
interfaceConfig.MAIN_TOOLBAR_BUTTONS.forEach((value, index) => {
|
||||
@@ -730,7 +784,6 @@ const Toolbar = {
|
||||
buttonElement.setAttribute("data-i18n", button.i18n);
|
||||
|
||||
buttonElement.setAttribute("data-container", "body");
|
||||
buttonElement.setAttribute("data-toggle", "popover");
|
||||
buttonElement.setAttribute("data-placement", "bottom");
|
||||
this._addPopups(buttonElement, button.popups);
|
||||
|
||||
@@ -751,4 +804,4 @@ const Toolbar = {
|
||||
}
|
||||
};
|
||||
|
||||
export default Toolbar;
|
||||
export default Toolbar;
|
||||
@@ -35,9 +35,11 @@ function hideToolbar(force) {
|
||||
clearTimeout(toolbarTimeoutObject);
|
||||
toolbarTimeoutObject = null;
|
||||
|
||||
if (Toolbar.isHovered() || APP.UI.isRingOverlayVisible()) {
|
||||
if (Toolbar.isHovered()
|
||||
|| APP.UI.isRingOverlayVisible()
|
||||
|| SideContainerToggler.isVisible()) {
|
||||
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
|
||||
} else if (!SideContainerToggler.isVisible() || force) {
|
||||
} else {
|
||||
Toolbar.hide();
|
||||
$('#subject').animate({top: "-=40"}, 300);
|
||||
}
|
||||
@@ -50,7 +52,8 @@ const ToolbarToggler = {
|
||||
init() {
|
||||
alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
|
||||
|
||||
this._registerWindowClickListeners();
|
||||
// disabled
|
||||
//this._registerWindowClickListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global $, config, interfaceConfig */
|
||||
/* global $, APP, config, AJS, interfaceConfig */
|
||||
|
||||
import KeyboardShortcut from '../../keyboardshortcut/keyboardshortcut';
|
||||
|
||||
/**
|
||||
* Created by hristo on 12/22/14.
|
||||
@@ -82,12 +84,59 @@
|
||||
context.putImageData(imgData, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the tooltip to the given element.
|
||||
*
|
||||
* @param element the element to set the tooltip to
|
||||
* @param key the tooltip data-i18n key
|
||||
* @param position the position of the tooltip in relation to the element
|
||||
*/
|
||||
setTooltip: function (element, key, position) {
|
||||
element.setAttribute("data-i18n", "[data-content]" + key);
|
||||
element.setAttribute("data-toggle", "popover");
|
||||
element.setAttribute("data-placement", position);
|
||||
element.setAttribute("data-html", true);
|
||||
element.setAttribute("data-container", "body");
|
||||
let positions = {
|
||||
'top': 's',
|
||||
'top-left': 'se',
|
||||
'left': 'e',
|
||||
'bottom-left': 'ne',
|
||||
'bottom': 'n',
|
||||
'bottom-right': 'nw',
|
||||
'right': 'w',
|
||||
'top-right': 'sw'
|
||||
};
|
||||
|
||||
element.setAttribute("data-i18n", "[content]" + key);
|
||||
APP.translation.translateElement($(element));
|
||||
|
||||
AJS.$(element).tooltip({
|
||||
gravity: positions[position],
|
||||
title: this._getTooltipText.bind(this, element),
|
||||
html: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the tooltip to the given element.
|
||||
*
|
||||
* @param element the element to remove the tooltip from
|
||||
*/
|
||||
removeTooltip: function (element) {
|
||||
AJS.$(element).tooltip('destroy');
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal util function for generating tooltip title.
|
||||
*
|
||||
* @param element
|
||||
* @returns {string|*}
|
||||
* @private
|
||||
*/
|
||||
_getTooltipText: function (element) {
|
||||
let title = element.getAttribute('content');
|
||||
let shortcut = element.getAttribute('shortcut');
|
||||
if(shortcut) {
|
||||
let shortcutString = KeyboardShortcut.getShortcutTooltip(shortcut);
|
||||
title += ` ${shortcutString}`;
|
||||
}
|
||||
return title;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -112,7 +161,8 @@
|
||||
* is enabled, {false} - otherwise
|
||||
*/
|
||||
isButtonEnabled: function (name) {
|
||||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1;
|
||||
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1
|
||||
|| interfaceConfig.MAIN_TOOLBAR_BUTTONS.indexOf(name) !== -1;
|
||||
},
|
||||
/**
|
||||
* Indicates if the setting section is enabled.
|
||||
@@ -232,6 +282,23 @@
|
||||
*/
|
||||
parseCssInt(cssValue) {
|
||||
return parseInt(cssValue) || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds href value to 'a' link jquery object. If link value is null,
|
||||
* undefined or empty string, disables the link.
|
||||
* @param {object} aLinkElement the jquery object
|
||||
* @param {string} link the link value
|
||||
*/
|
||||
setLinkHref(aLinkElement, link) {
|
||||
if (link) {
|
||||
aLinkElement.attr('href', link);
|
||||
} else {
|
||||
aLinkElement.css({
|
||||
"pointer-events": "none",
|
||||
"cursor": "default"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -312,7 +312,6 @@ ConnectionIndicator.prototype.updateConnectionQuality =
|
||||
} else {
|
||||
if(this.connectionIndicatorContainer.style.display == "none") {
|
||||
this.connectionIndicatorContainer.style.display = "block";
|
||||
this.videoContainer.updateIconPositions();
|
||||
}
|
||||
}
|
||||
this.bandwidth = object.bandwidth;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
const thumbAspectRatio = 1 / 1;
|
||||
|
||||
const FilmStrip = {
|
||||
/**
|
||||
*
|
||||
@@ -26,7 +24,7 @@ const FilmStrip = {
|
||||
*/
|
||||
toggleFilmStrip (visible) {
|
||||
if (typeof visible === 'boolean'
|
||||
&& this.isFilmStripVisible() == visible) {
|
||||
&& this.isFilmStripVisible() == visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,8 +34,8 @@ const FilmStrip = {
|
||||
var eventEmitter = this.eventEmitter;
|
||||
if (eventEmitter) {
|
||||
eventEmitter.emit(
|
||||
UIEvents.TOGGLED_FILM_STRIP,
|
||||
this.isFilmStripVisible());
|
||||
UIEvents.TOGGLED_FILM_STRIP,
|
||||
this.isFilmStripVisible());
|
||||
}
|
||||
},
|
||||
|
||||
@@ -66,13 +64,52 @@ const FilmStrip = {
|
||||
- parseInt(this.filmStrip.css('paddingRight'), 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the thumbnail size.
|
||||
*/
|
||||
calculateThumbnailSize () {
|
||||
let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT;
|
||||
calculateThumbnailSize() {
|
||||
let availableSizes = this.calculateAvailableSize();
|
||||
let width = availableSizes.availableWidth;
|
||||
let height = availableSizes.availableHeight;
|
||||
|
||||
let numvids = this.getThumbs(true).length;
|
||||
return this.calculateThumbnailSizeFromAvailable(width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalizes local and remote thumbnail ratios
|
||||
*/
|
||||
normalizeThumbnailRatio () {
|
||||
let remoteHeightRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_HEIGHT;
|
||||
let remoteWidthRatio = interfaceConfig.REMOTE_THUMBNAIL_RATIO_WIDTH;
|
||||
|
||||
let localHeightRatio = interfaceConfig.LOCAL_THUMBNAIL_RATIO_HEIGHT;
|
||||
let localWidthRatio = interfaceConfig.LOCAL_THUMBNAIL_RATIO_WIDTH;
|
||||
|
||||
let commonHeightRatio = remoteHeightRatio * localHeightRatio;
|
||||
|
||||
let localRatioCoefficient = localWidthRatio / localHeightRatio;
|
||||
let remoteRatioCoefficient = remoteWidthRatio / remoteHeightRatio;
|
||||
|
||||
remoteWidthRatio = commonHeightRatio * remoteRatioCoefficient;
|
||||
remoteHeightRatio = commonHeightRatio;
|
||||
|
||||
localWidthRatio = commonHeightRatio * localRatioCoefficient;
|
||||
localHeightRatio = commonHeightRatio;
|
||||
|
||||
let localRatio = {
|
||||
widthRatio: localWidthRatio,
|
||||
heightRatio: localHeightRatio
|
||||
};
|
||||
|
||||
let remoteRatio = {
|
||||
widthRatio: remoteWidthRatio,
|
||||
heightRatio: remoteHeightRatio
|
||||
};
|
||||
|
||||
return { localRatio, remoteRatio };
|
||||
},
|
||||
|
||||
calculateAvailableSize() {
|
||||
let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT;
|
||||
let thumbs = this.getThumbs(true);
|
||||
let numvids = thumbs.remoteThumbs.length;
|
||||
|
||||
let localVideoContainer = $("#localVideoContainer");
|
||||
|
||||
@@ -83,20 +120,19 @@ const FilmStrip = {
|
||||
*/
|
||||
let videoAreaAvailableWidth
|
||||
= UIUtil.getAvailableVideoWidth()
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('right'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('paddingLeft'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('paddingRight'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('borderLeftWidth'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('borderRightWidth'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('right'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('paddingLeft'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('paddingRight'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('borderLeftWidth'), 10)
|
||||
- UIUtil.parseCssInt(this.filmStrip.css('borderRightWidth'), 10)
|
||||
- 5;
|
||||
|
||||
let availableWidth = videoAreaAvailableWidth;
|
||||
|
||||
// If the number of videos is 0 or undefined we don't need to calculate
|
||||
// further.
|
||||
if (numvids)
|
||||
// If local thumb is not hidden
|
||||
if(thumbs.localThumb) {
|
||||
availableWidth = Math.floor(
|
||||
(videoAreaAvailableWidth - numvids * (
|
||||
(videoAreaAvailableWidth - (
|
||||
UIUtil.parseCssInt(
|
||||
localVideoContainer.css('borderLeftWidth'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
@@ -109,36 +145,90 @@ const FilmStrip = {
|
||||
localVideoContainer.css('marginLeft'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
localVideoContainer.css('marginRight'), 10)))
|
||||
/ numvids);
|
||||
);
|
||||
}
|
||||
|
||||
// If the number of videos is 0 or undefined we don't need to calculate
|
||||
// further.
|
||||
if (numvids) {
|
||||
let remoteVideoContainer = thumbs.remoteThumbs.eq(0);
|
||||
availableWidth = Math.floor(
|
||||
(videoAreaAvailableWidth - numvids * (
|
||||
UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('borderLeftWidth'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('borderRightWidth'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('paddingLeft'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('paddingRight'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('marginLeft'), 10)
|
||||
+ UIUtil.parseCssInt(
|
||||
remoteVideoContainer.css('marginRight'), 10)))
|
||||
);
|
||||
}
|
||||
|
||||
let maxHeight
|
||||
// If the MAX_HEIGHT property hasn't been specified
|
||||
// we have the static value.
|
||||
= Math.min( interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120,
|
||||
availableHeight);
|
||||
= Math.min(interfaceConfig.FILM_STRIP_MAX_HEIGHT || 120,
|
||||
availableHeight);
|
||||
|
||||
availableHeight
|
||||
= Math.min( maxHeight, window.innerHeight - 18);
|
||||
= Math.min(maxHeight, window.innerHeight - 18);
|
||||
|
||||
if (availableHeight < availableWidth) {
|
||||
availableWidth = availableHeight;
|
||||
return { availableWidth, availableHeight };
|
||||
},
|
||||
|
||||
calculateThumbnailSizeFromAvailable(availableWidth, availableHeight) {
|
||||
let { localRatio, remoteRatio } = this.normalizeThumbnailRatio();
|
||||
let { remoteThumbs } = this.getThumbs(true);
|
||||
let remoteProportion = remoteRatio.widthRatio * remoteThumbs.length;
|
||||
let widthProportion = remoteProportion + localRatio.widthRatio;
|
||||
|
||||
let heightUnit = availableHeight / localRatio.heightRatio;
|
||||
let widthUnit = availableWidth / widthProportion;
|
||||
|
||||
if (heightUnit < widthUnit) {
|
||||
widthUnit = heightUnit;
|
||||
}
|
||||
else
|
||||
availableHeight = availableWidth;
|
||||
heightUnit = widthUnit;
|
||||
|
||||
let localVideo = {
|
||||
thumbWidth: widthUnit * localRatio.widthRatio,
|
||||
thumbHeight: heightUnit * localRatio.heightRatio
|
||||
};
|
||||
let remoteVideo = {
|
||||
thumbWidth: widthUnit * remoteRatio.widthRatio,
|
||||
thumbHeight: widthUnit * remoteRatio.heightRatio
|
||||
};
|
||||
|
||||
return {
|
||||
thumbWidth: availableWidth,
|
||||
thumbHeight: availableHeight
|
||||
localVideo,
|
||||
remoteVideo
|
||||
};
|
||||
},
|
||||
|
||||
resizeThumbnails (thumbWidth, thumbHeight,
|
||||
resizeThumbnails (local, remote,
|
||||
animate = false, forceUpdate = false) {
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.getThumbs(!forceUpdate).animate({
|
||||
height: thumbHeight,
|
||||
width: thumbWidth
|
||||
let thumbs = this.getThumbs(!forceUpdate);
|
||||
|
||||
thumbs.localThumb.animate({
|
||||
height: local.thumbHeight,
|
||||
width: local.thumbWidth
|
||||
}, {
|
||||
queue: false,
|
||||
duration: animate ? 500 : 0,
|
||||
complete: resolve
|
||||
});
|
||||
|
||||
thumbs.remoteThumbs.animate({
|
||||
height: remote.thumbHeight,
|
||||
width: remote.thumbWidth
|
||||
}, {
|
||||
queue: false,
|
||||
duration: animate ? 500 : 0,
|
||||
@@ -147,7 +237,7 @@ const FilmStrip = {
|
||||
|
||||
this.filmStrip.animate({
|
||||
// adds 2 px because of small video 1px border
|
||||
height: thumbHeight + 2
|
||||
height: remote.thumbHeight + 2
|
||||
}, {
|
||||
queue: false,
|
||||
duration: animate ? 500 : 0
|
||||
@@ -165,13 +255,19 @@ const FilmStrip = {
|
||||
selector += ':visible';
|
||||
}
|
||||
|
||||
let localThumb = $("#localVideoContainer");
|
||||
let remoteThumbs = this.filmStrip.children(selector)
|
||||
.not("#localVideoContainer");
|
||||
|
||||
// Exclude the local video container if it has been hidden.
|
||||
if ($("#localVideoContainer").hasClass("hidden"))
|
||||
return this.filmStrip.children(selector)
|
||||
.not("#localVideoContainer");
|
||||
else
|
||||
return this.filmStrip.children(selector);
|
||||
}
|
||||
if (localThumb.hasClass("hidden")) {
|
||||
return { remoteThumbs };
|
||||
} else {
|
||||
return { remoteThumbs, localThumb };
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default FilmStrip;
|
||||
export default FilmStrip;
|
||||
310
modules/UI/videolayout/LargeVideoManager.js
Normal file
310
modules/UI/videolayout/LargeVideoManager.js
Normal file
@@ -0,0 +1,310 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
/* jshint -W101 */
|
||||
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import {createDeferred} from '../../util/helpers';
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import {VideoContainer, VIDEO_CONTAINER_TYPE} from "./VideoContainer";
|
||||
|
||||
/**
|
||||
* Manager for all Large containers.
|
||||
*/
|
||||
export default class LargeVideoManager {
|
||||
constructor (emitter) {
|
||||
this.containers = {};
|
||||
|
||||
this.state = VIDEO_CONTAINER_TYPE;
|
||||
this.videoContainer = new VideoContainer(
|
||||
() => this.resizeContainer(VIDEO_CONTAINER_TYPE), emitter);
|
||||
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
||||
|
||||
// use the same video container to handle and desktop tracks
|
||||
this.addContainer("desktop", this.videoContainer);
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
this.$container = $('#largeVideoContainer');
|
||||
|
||||
this.$container.css({
|
||||
display: 'inline-block'
|
||||
});
|
||||
|
||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
||||
let leftWatermarkDiv
|
||||
= this.$container.find("div.watermark.leftwatermark");
|
||||
|
||||
leftWatermarkDiv.css({display: 'block'});
|
||||
|
||||
UIUtil.setLinkHref(
|
||||
leftWatermarkDiv.parent(),
|
||||
interfaceConfig.JITSI_WATERMARK_LINK);
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
let rightWatermarkDiv
|
||||
= this.$container.find("div.watermark.rightwatermark");
|
||||
|
||||
rightWatermarkDiv.css({
|
||||
display: 'block',
|
||||
backgroundImage: 'url(images/rightwatermark.png)'
|
||||
});
|
||||
|
||||
UIUtil.setLinkHref(
|
||||
rightWatermarkDiv.parent(),
|
||||
interfaceConfig.BRAND_WATERMARK_LINK);
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_POWERED_BY) {
|
||||
this.$container.children("a.poweredby").css({display: 'block'});
|
||||
}
|
||||
|
||||
this.$container.hover(
|
||||
e => this.onHoverIn(e),
|
||||
e => this.onHoverOut(e)
|
||||
);
|
||||
}
|
||||
|
||||
onHoverIn (e) {
|
||||
if (!this.state) {
|
||||
return;
|
||||
}
|
||||
let container = this.getContainer(this.state);
|
||||
container.onHoverIn(e);
|
||||
}
|
||||
|
||||
onHoverOut (e) {
|
||||
if (!this.state) {
|
||||
return;
|
||||
}
|
||||
let container = this.getContainer(this.state);
|
||||
container.onHoverOut(e);
|
||||
}
|
||||
|
||||
get id () {
|
||||
let container = this.getContainer(this.state);
|
||||
return container.id;
|
||||
}
|
||||
|
||||
scheduleLargeVideoUpdate () {
|
||||
if (this.updateInProcess || !this.newStreamData) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateInProcess = true;
|
||||
|
||||
let container = this.getContainer(this.state);
|
||||
|
||||
// Include hide()/fadeOut only if we're switching between users
|
||||
let preUpdate;
|
||||
if (this.newStreamData.id != this.id) {
|
||||
preUpdate = container.hide();
|
||||
} else {
|
||||
preUpdate = Promise.resolve();
|
||||
}
|
||||
|
||||
preUpdate.then(() => {
|
||||
let {id, stream, videoType, resolve} = this.newStreamData;
|
||||
this.newStreamData = null;
|
||||
|
||||
console.info("hover in %s", id);
|
||||
this.state = videoType;
|
||||
let container = this.getContainer(this.state);
|
||||
container.setStream(stream, videoType);
|
||||
|
||||
// change the avatar url on large
|
||||
this.updateAvatar(Avatar.getAvatarUrl(id));
|
||||
|
||||
// If we the continer is VIDEO_CONTAINER_TYPE, we need to check
|
||||
// its stream whether exist and is muted to set isVideoMuted
|
||||
// in rest of the cases it is false
|
||||
let isVideoMuted = false;
|
||||
if (videoType == VIDEO_CONTAINER_TYPE)
|
||||
isVideoMuted = stream ? stream.isMuted() : true;
|
||||
|
||||
// show the avatar on large if needed
|
||||
container.showAvatar(isVideoMuted);
|
||||
|
||||
let promise;
|
||||
|
||||
// do not show stream if video is muted
|
||||
// but we still should show watermark
|
||||
if (isVideoMuted) {
|
||||
this.showWatermark(true);
|
||||
promise = Promise.resolve();
|
||||
} else {
|
||||
promise = container.show();
|
||||
}
|
||||
|
||||
// resolve updateLargeVideo promise after everything is done
|
||||
promise.then(resolve);
|
||||
|
||||
return promise;
|
||||
}).then(() => {
|
||||
// after everything is done check again if there are any pending
|
||||
// new streams.
|
||||
this.updateInProcess = false;
|
||||
this.scheduleLargeVideoUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update large video.
|
||||
* Switches to large video even if previously other container was visible.
|
||||
* @param userID the userID of the participant associated with the stream
|
||||
* @param {JitsiTrack?} stream new stream
|
||||
* @param {string?} videoType new video type
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateLargeVideo (userID, stream, videoType) {
|
||||
if (this.newStreamData) {
|
||||
this.newStreamData.reject();
|
||||
}
|
||||
|
||||
this.newStreamData = createDeferred();
|
||||
this.newStreamData.id = userID;
|
||||
this.newStreamData.stream = stream;
|
||||
this.newStreamData.videoType = videoType;
|
||||
|
||||
this.scheduleLargeVideoUpdate();
|
||||
|
||||
return this.newStreamData.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update container size.
|
||||
*/
|
||||
updateContainerSize () {
|
||||
this.width = UIUtil.getAvailableVideoWidth();
|
||||
this.height = window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize Large container of specified type.
|
||||
* @param {string} type type of container which should be resized.
|
||||
* @param {boolean} [animate=false] if resize process should be animated.
|
||||
*/
|
||||
resizeContainer (type, animate = false) {
|
||||
let container = this.getContainer(type);
|
||||
container.resize(this.width, this.height, animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize all Large containers.
|
||||
* @param {boolean} animate if resize process should be animated.
|
||||
*/
|
||||
resize (animate) {
|
||||
// resize all containers
|
||||
Object.keys(this.containers)
|
||||
.forEach(type => this.resizeContainer(type, animate));
|
||||
|
||||
this.$container.animate({
|
||||
width: this.width,
|
||||
height: this.height
|
||||
}, {
|
||||
queue: false,
|
||||
duration: animate ? 500 : 0
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the filter indicating a video problem to the user.
|
||||
*
|
||||
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
|
||||
*/
|
||||
enableVideoProblemFilter (enable) {
|
||||
let container = this.getContainer(this.state);
|
||||
container.$video.toggleClass("videoProblemFilter", enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the src of the dominant speaker avatar
|
||||
*/
|
||||
updateAvatar (avatarUrl) {
|
||||
$("#dominantSpeakerAvatar").attr('src', avatarUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide watermark.
|
||||
* @param {boolean} show
|
||||
*/
|
||||
showWatermark (show) {
|
||||
$('.watermark').css('visibility', show ? 'visible' : 'hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add container of specified type.
|
||||
* @param {string} type container type
|
||||
* @param {LargeContainer} container container to add.
|
||||
*/
|
||||
addContainer (type, container) {
|
||||
if (this.containers[type]) {
|
||||
throw new Error(`container of type ${type} already exist`);
|
||||
}
|
||||
|
||||
this.containers[type] = container;
|
||||
this.resizeContainer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Large container of specified type.
|
||||
* @param {string} type container type.
|
||||
* @returns {LargeContainer}
|
||||
*/
|
||||
getContainer (type) {
|
||||
let container = this.containers[type];
|
||||
|
||||
if (!container) {
|
||||
throw new Error(`container of type ${type} doesn't exist`);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Large container of specified type.
|
||||
* @param {string} type container type.
|
||||
*/
|
||||
removeContainer (type) {
|
||||
if (!this.containers[type]) {
|
||||
throw new Error(`container of type ${type} doesn't exist`);
|
||||
}
|
||||
|
||||
delete this.containers[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Large container of specified type.
|
||||
* Does nothing if such container is already visible.
|
||||
* @param {string} type container type.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
showContainer (type) {
|
||||
if (this.state === type) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let oldContainer = this.containers[this.state];
|
||||
if (this.state === VIDEO_CONTAINER_TYPE) {
|
||||
this.showWatermark(false);
|
||||
}
|
||||
oldContainer.hide();
|
||||
|
||||
this.state = type;
|
||||
let container = this.getContainer(type);
|
||||
|
||||
return container.show().then(() => {
|
||||
if (type === VIDEO_CONTAINER_TYPE) {
|
||||
this.showWatermark(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the flipX state of the local video.
|
||||
* @param val {boolean} true if flipped.
|
||||
*/
|
||||
onLocalFlipXChange(val) {
|
||||
this.videoContainer.setLocalFlipX(val);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ function LocalVideo(VideoLayout, emitter) {
|
||||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.localVideoId = null;
|
||||
this.bindHoverHandler();
|
||||
if(config.enableLocalVideoFlip)
|
||||
this._buildContextMenu();
|
||||
this.isLocal = true;
|
||||
@@ -44,7 +43,7 @@ function createEditDisplayNameButton() {
|
||||
editButton.className = 'displayname';
|
||||
UIUtil.setTooltip(editButton,
|
||||
"videothumbnail.editnickname",
|
||||
"top");
|
||||
"left");
|
||||
editButton.innerHTML = '<i class="icon-edit"></i>';
|
||||
|
||||
return editButton;
|
||||
@@ -61,7 +60,7 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
var nameSpan = $('#' + this.videoSpanId + ' .displayname');
|
||||
var defaultLocalDisplayName = APP.translation.generateTranslationHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
|
||||
@@ -72,7 +71,7 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
if (displayName && displayName.length > 0) {
|
||||
meHTML = APP.translation.generateTranslationHTML("me");
|
||||
$('#localDisplayName').html(
|
||||
UIUtil.escapeHtml(displayName) + ' (' + meHTML + ')'
|
||||
`${UIUtil.escapeHtml(displayName)} (${meHTML})`
|
||||
);
|
||||
} else {
|
||||
$('#localDisplayName').html(defaultLocalDisplayName);
|
||||
@@ -80,11 +79,11 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
}
|
||||
this.updateView();
|
||||
} else {
|
||||
var editButton = createEditDisplayNameButton();
|
||||
|
||||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
||||
document.getElementById(this.videoSpanId)
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(nameSpan);
|
||||
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
@@ -97,12 +96,11 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
|
||||
nameSpan.id = 'localDisplayName';
|
||||
this.container.appendChild(editButton);
|
||||
//translates popover of edit button
|
||||
APP.translation.translateElement($("a.displayname"));
|
||||
|
||||
var editableText = document.createElement('input');
|
||||
editableText.className = 'displayname';
|
||||
editableText.className = 'editdisplayname';
|
||||
editableText.type = 'text';
|
||||
editableText.id = 'editDisplayName';
|
||||
|
||||
@@ -119,26 +117,30 @@ LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
JSON.stringify({name: "Jane Pink"}));
|
||||
editableText.setAttribute("placeholder", defaultNickname);
|
||||
|
||||
this.container.appendChild(editableText);
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(editableText);
|
||||
|
||||
var self = this;
|
||||
$('#localVideoContainer .displayname')
|
||||
.bind("click", function (e) {
|
||||
let $editDisplayName = $('#editDisplayName');
|
||||
let $localDisplayName = $('#localDisplayName');
|
||||
|
||||
var editDisplayName = $('#editDisplayName');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$('#localDisplayName').hide();
|
||||
editDisplayName.show();
|
||||
editDisplayName.focus();
|
||||
editDisplayName.select();
|
||||
$localDisplayName.hide();
|
||||
$editDisplayName.show();
|
||||
$editDisplayName.focus();
|
||||
$editDisplayName.select();
|
||||
|
||||
editDisplayName.one("focusout", function (e) {
|
||||
$editDisplayName.one("focusout", function (e) {
|
||||
self.emitter.emit(UIEvents.NICKNAME_CHANGED, this.value);
|
||||
$('#editDisplayName').hide();
|
||||
$editDisplayName.hide();
|
||||
$localDisplayName.show();
|
||||
});
|
||||
|
||||
editDisplayName.on('keydown', function (e) {
|
||||
$editDisplayName.on('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
$('#editDisplayName').hide();
|
||||
|
||||
@@ -17,10 +17,8 @@ function RemoteVideo(id, VideoLayout, emitter) {
|
||||
this.addRemoteVideoContainer();
|
||||
this.connectionIndicator = new ConnectionIndicator(this, id);
|
||||
this.setDisplayName();
|
||||
this.bindHoverHandler();
|
||||
this.flipX = false;
|
||||
this.isLocal = false;
|
||||
this.isMuted = false;
|
||||
}
|
||||
|
||||
RemoteVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
@@ -34,8 +32,10 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||
if (APP.conference.isModerator) {
|
||||
this.addRemoteVideoMenu();
|
||||
}
|
||||
let {thumbWidth, thumbHeight} = this.VideoLayout.resizeThumbnails();
|
||||
AudioLevels.updateAudioLevelCanvas(this.id, thumbWidth, thumbHeight);
|
||||
|
||||
let { remoteVideo } = this.VideoLayout.resizeThumbnails();
|
||||
let { thumbHeight, thumbWidth } = remoteVideo;
|
||||
AudioLevels.createAudioLevelCanvas(this.id, thumbWidth, thumbHeight);
|
||||
|
||||
return this.container;
|
||||
};
|
||||
@@ -50,7 +50,7 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||
*/
|
||||
RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
|
||||
this.popover = new JitsiPopover(
|
||||
$("#" + this.videoSpanId + " > .remotevideomenu"),
|
||||
$("#" + this.videoSpanId + " .remotevideomenu"),
|
||||
{ content: popupMenuElement.outerHTML,
|
||||
skin: "black"});
|
||||
|
||||
@@ -60,7 +60,7 @@ RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
|
||||
this.popover.show = function () {
|
||||
// update content by forcing it, to finish even if popover
|
||||
// is not visible
|
||||
this.updateRemoteVideoMenu(this.isMuted, true);
|
||||
this.updateRemoteVideoMenu(this.isAudioMuted, true);
|
||||
// call the original show, passing its actual this
|
||||
origShowFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
@@ -96,7 +96,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
|
||||
|
||||
muteLinkItem.id = "mutelink_" + this.id;
|
||||
|
||||
if (this.isMuted) {
|
||||
if (this.isAudioMuted) {
|
||||
muteLinkItem.innerHTML = mutedHTML;
|
||||
muteLinkItem.className = 'mutelink disabled';
|
||||
}
|
||||
@@ -108,7 +108,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
|
||||
// Delegate event to the document.
|
||||
$(document).on("click", "#mutelink_" + this.id, function(){
|
||||
|
||||
if (this.isMuted)
|
||||
if (this.isAudioMuted)
|
||||
return;
|
||||
|
||||
this.emitter.emit(UIEvents.REMOTE_AUDIO_MUTED, this.id);
|
||||
@@ -152,7 +152,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
|
||||
*/
|
||||
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted, force) {
|
||||
|
||||
this.isMuted = isMuted;
|
||||
this.isAudioMuted = isMuted;
|
||||
|
||||
// generate content, translate it and add it to document only if
|
||||
// popover is visible or we force to do so.
|
||||
@@ -170,12 +170,16 @@ RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted, force) {
|
||||
*/
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
RemoteVideo.prototype.addRemoteVideoMenu = function () {
|
||||
var spanElement = document.createElement('div');
|
||||
spanElement.className = 'remotevideomenu';
|
||||
this.container.appendChild(spanElement);
|
||||
|
||||
var spanElement = document.createElement('span');
|
||||
spanElement.className = 'remotevideomenu toolbar-icon right';
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(spanElement);
|
||||
|
||||
var menuElement = document.createElement('i');
|
||||
menuElement.className = 'fa fa-angle-down';
|
||||
menuElement.className = 'icon-menu-up';
|
||||
menuElement.title = 'Remote user controls';
|
||||
spanElement.appendChild(menuElement);
|
||||
|
||||
@@ -381,7 +385,7 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
var nameSpan = $('#' + this.videoSpanId + ' .displayname');
|
||||
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
@@ -400,7 +404,9 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
} else {
|
||||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
||||
$('#' + this.videoSpanId)[0]
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(nameSpan);
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
$(nameSpan).text(displayName);
|
||||
@@ -418,7 +424,7 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
* @param videoElementId the id of local or remote video element.
|
||||
*/
|
||||
RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||
var menuSpan = $('#' + this.videoSpanId + '>span.remotevideomenu');
|
||||
var menuSpan = $('#' + this.videoSpanId + '> .remotevideomenu');
|
||||
if (menuSpan.length) {
|
||||
this.popover.forceHide();
|
||||
menuSpan.remove();
|
||||
@@ -427,12 +433,16 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||
};
|
||||
|
||||
RemoteVideo.createContainer = function (spanId) {
|
||||
var container = document.createElement('span');
|
||||
let container = document.createElement('span');
|
||||
container.id = spanId;
|
||||
container.className = 'videocontainer';
|
||||
|
||||
let toolbar = document.createElement('div');
|
||||
toolbar.className = "videocontainer__toolbar";
|
||||
container.appendChild(toolbar);
|
||||
|
||||
var remotes = document.getElementById('remoteVideos');
|
||||
return remotes.appendChild(container);
|
||||
};
|
||||
|
||||
|
||||
export default RemoteVideo;
|
||||
|
||||
@@ -7,7 +7,7 @@ import UIEvents from "../../../service/UI/UIEvents";
|
||||
const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper;
|
||||
|
||||
function SmallVideo(VideoLayout) {
|
||||
this.isMuted = false;
|
||||
this.isAudioMuted = false;
|
||||
this.hasAvatar = false;
|
||||
this.isVideoMuted = false;
|
||||
this.videoStream = null;
|
||||
@@ -40,7 +40,7 @@ SmallVideo.prototype.isVisible = function () {
|
||||
};
|
||||
|
||||
SmallVideo.prototype.showDisplayName = function(isShow) {
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname').get(0);
|
||||
var nameSpan = $('#' + this.videoSpanId + ' .displayname').get(0);
|
||||
if (isShow) {
|
||||
if (nameSpan && nameSpan.innerHTML && nameSpan.innerHTML.length)
|
||||
nameSpan.setAttribute("style", "display:inline-block;");
|
||||
@@ -171,26 +171,6 @@ SmallVideo.getStreamElementID = function (stream) {
|
||||
return (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 (!self.VideoLayout.isLargeVideoVisible() ||
|
||||
!self.VideoLayout.isCurrentlyOnLarge(self.id))
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the data for the indicator
|
||||
* @param id the id of the indicator
|
||||
@@ -209,86 +189,102 @@ SmallVideo.prototype.hideIndicator = function () {
|
||||
|
||||
|
||||
/**
|
||||
* Shows audio muted indicator over small videos.
|
||||
* @param {string} isMuted
|
||||
* Shows / hides the audio muted indicator over small videos.
|
||||
*
|
||||
* @param {boolean} isMuted indicates if the muted element should be shown
|
||||
* or hidden
|
||||
*/
|
||||
SmallVideo.prototype.showAudioIndicator = function(isMuted) {
|
||||
var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted');
|
||||
|
||||
var audioMutedIndicator = this.getAudioMutedIndicator();
|
||||
|
||||
if (!isMuted) {
|
||||
if (audioMutedSpan.length > 0) {
|
||||
audioMutedSpan.popover('hide');
|
||||
audioMutedSpan.remove();
|
||||
}
|
||||
audioMutedIndicator.hide();
|
||||
}
|
||||
else {
|
||||
if (!audioMutedSpan.length) {
|
||||
audioMutedSpan = document.createElement('span');
|
||||
audioMutedSpan.className = 'audioMuted';
|
||||
UIUtil.setTooltip(audioMutedSpan,
|
||||
"videothumbnail.mute",
|
||||
"top");
|
||||
|
||||
this.container.appendChild(audioMutedSpan);
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + " > span"));
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-mic-disabled';
|
||||
audioMutedSpan.appendChild(mutedIndicator);
|
||||
|
||||
}
|
||||
this.updateIconPositions();
|
||||
audioMutedIndicator.show();
|
||||
}
|
||||
this.isMuted = isMuted;
|
||||
this.isAudioMuted = isMuted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the audio muted indicator jquery object. If it doesn't exists -
|
||||
* creates it.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement} the audio muted indicator
|
||||
*/
|
||||
SmallVideo.prototype.getAudioMutedIndicator = function () {
|
||||
var audioMutedSpan = $('#' + this.videoSpanId + ' .audioMuted');
|
||||
|
||||
if (audioMutedSpan.length) {
|
||||
return audioMutedSpan;
|
||||
}
|
||||
|
||||
audioMutedSpan = document.createElement('span');
|
||||
audioMutedSpan.className = 'audioMuted toolbar-icon';
|
||||
|
||||
UIUtil.setTooltip(audioMutedSpan,
|
||||
"videothumbnail.mute",
|
||||
"top");
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(audioMutedSpan);
|
||||
|
||||
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-mic-disabled';
|
||||
audioMutedSpan.appendChild(mutedIndicator);
|
||||
|
||||
return $('#' + this.videoSpanId + ' .audioMuted');
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows video muted indicator over small videos and disables/enables avatar
|
||||
* if video muted.
|
||||
*
|
||||
* @param {boolean} isMuted indicates if we should set the view to muted view
|
||||
* or not
|
||||
*/
|
||||
SmallVideo.prototype.setMutedView = function(isMuted) {
|
||||
this.isVideoMuted = isMuted;
|
||||
this.updateView();
|
||||
|
||||
var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted');
|
||||
var videoMutedSpan = this.getVideoMutedIndicator();
|
||||
|
||||
if (isMuted === false) {
|
||||
if (videoMutedSpan.length > 0) {
|
||||
videoMutedSpan.remove();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!videoMutedSpan.length) {
|
||||
videoMutedSpan = document.createElement('span');
|
||||
videoMutedSpan.className = 'videoMuted';
|
||||
|
||||
this.container.appendChild(videoMutedSpan);
|
||||
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-camera-disabled';
|
||||
UIUtil.setTooltip(mutedIndicator,
|
||||
"videothumbnail.videomute",
|
||||
"top");
|
||||
videoMutedSpan.appendChild(mutedIndicator);
|
||||
//translate texts for muted indicator
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + " > span > i"));
|
||||
}
|
||||
|
||||
this.updateIconPositions();
|
||||
}
|
||||
videoMutedSpan[isMuted ? 'show' : 'hide']();
|
||||
};
|
||||
|
||||
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") {
|
||||
audioMutedSpan.css({right: "23px"});
|
||||
videoMutedSpan.css({right: ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"});
|
||||
} else {
|
||||
audioMutedSpan.css({right: "0px"});
|
||||
videoMutedSpan.css({right: (audioMutedSpan.length > 0? 30 : 0) + "px"});
|
||||
/**
|
||||
* Returns the video muted indicator jquery object. If it doesn't exists -
|
||||
* creates it.
|
||||
*
|
||||
* @returns {jQuery|HTMLElement} the video muted indicator
|
||||
*/
|
||||
SmallVideo.prototype.getVideoMutedIndicator = function () {
|
||||
var videoMutedSpan = $('#' + this.videoSpanId + ' .videoMuted');
|
||||
|
||||
if (videoMutedSpan.length) {
|
||||
return videoMutedSpan;
|
||||
}
|
||||
|
||||
videoMutedSpan = document.createElement('span');
|
||||
videoMutedSpan.className = 'videoMuted toolbar-icon';
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(videoMutedSpan);
|
||||
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-camera-disabled';
|
||||
|
||||
UIUtil.setTooltip(mutedIndicator,
|
||||
"videothumbnail.videomute",
|
||||
"top");
|
||||
|
||||
videoMutedSpan.appendChild(mutedIndicator);
|
||||
|
||||
return $('#' + this.videoSpanId + ' .videoMuted');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -298,26 +294,25 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
|
||||
// Show moderator indicator
|
||||
var indicatorSpan = $('#' + this.videoSpanId + ' .focusindicator');
|
||||
|
||||
if (!indicatorSpan || indicatorSpan.length === 0) {
|
||||
indicatorSpan = document.createElement('span');
|
||||
indicatorSpan.className = 'focusindicator';
|
||||
|
||||
this.container.appendChild(indicatorSpan);
|
||||
indicatorSpan = $('#' + this.videoSpanId + ' .focusindicator');
|
||||
if (indicatorSpan.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (indicatorSpan.children().length !== 0)
|
||||
return;
|
||||
indicatorSpan = document.createElement('span');
|
||||
indicatorSpan.className = 'focusindicator toolbar-icon right';
|
||||
|
||||
this.container
|
||||
.querySelector('.videocontainer__toolbar')
|
||||
.appendChild(indicatorSpan);
|
||||
|
||||
var moderatorIndicator = document.createElement('i');
|
||||
moderatorIndicator.className = 'icon-star';
|
||||
indicatorSpan[0].appendChild(moderatorIndicator);
|
||||
|
||||
UIUtil.setTooltip(indicatorSpan[0],
|
||||
UIUtil.setTooltip(moderatorIndicator,
|
||||
"videothumbnail.moderator",
|
||||
"top");
|
||||
"top-left");
|
||||
|
||||
//translates text in focus indicators
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + ' .focusindicator'));
|
||||
indicatorSpan.appendChild(moderatorIndicator);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -406,8 +401,6 @@ SmallVideo.prototype.updateView = function () {
|
||||
setVisibility(video, showVideo);
|
||||
}
|
||||
setVisibility(avatar, showAvatar);
|
||||
|
||||
this.showDisplayName(!showVideo && !showAvatar);
|
||||
};
|
||||
|
||||
SmallVideo.prototype.avatarChanged = function (avatarUrl) {
|
||||
@@ -445,7 +438,7 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
|
||||
indicatorSpan.innerHTML
|
||||
= "<i id='indicatoricon' class='fa fa-bullhorn'></i>";
|
||||
// adds a tooltip
|
||||
UIUtil.setTooltip(indicatorSpan, "speaker", "left");
|
||||
UIUtil.setTooltip(indicatorSpan, "speaker", "top");
|
||||
APP.translation.translateElement($(indicatorSpan));
|
||||
|
||||
$(indicatorSpan).css("visibility", show ? "visible" : "hidden");
|
||||
@@ -465,12 +458,11 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) {
|
||||
var indicatorSpanId = "raisehandindicator";
|
||||
var indicatorSpan = this.getIndicatorSpan(indicatorSpanId);
|
||||
|
||||
indicatorSpan.style.background = "#D6D61E";
|
||||
indicatorSpan.innerHTML
|
||||
= "<i id='indicatoricon' class='fa fa-hand-paper-o'></i>";
|
||||
= "<i id='indicatoricon' class='icon-raised-hand'></i>";
|
||||
|
||||
// adds a tooltip
|
||||
UIUtil.setTooltip(indicatorSpan, "raisedHand", "left");
|
||||
UIUtil.setTooltip(indicatorSpan, "raisedHand", "top");
|
||||
APP.translation.translateElement($(indicatorSpan));
|
||||
|
||||
$(indicatorSpan).css("visibility", show ? "visible" : "hidden");
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
/* jshint -W101 */
|
||||
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import LargeContainer from './LargeContainer';
|
||||
import FilmStrip from './FilmStrip';
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import {createDeferred} from '../../util/helpers';
|
||||
import LargeContainer from './LargeContainer';
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
// FIXME should be 'video'
|
||||
export const VIDEO_CONTAINER_TYPE = "camera";
|
||||
|
||||
const FADE_DURATION_MS = 300;
|
||||
|
||||
export const VIDEO_CONTAINER_TYPE = "camera";
|
||||
|
||||
/**
|
||||
* Get stream id.
|
||||
* @param {JitsiTrack?} stream
|
||||
@@ -20,7 +19,8 @@ function getStreamOwnerId(stream) {
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
if (stream.isLocal()) { // local stream doesn't have method "getParticipantId"
|
||||
// local stream doesn't have method "getParticipantId"
|
||||
if (stream.isLocal()) {
|
||||
return APP.conference.getMyUserId();
|
||||
} else {
|
||||
return stream.getParticipantId();
|
||||
@@ -154,7 +154,7 @@ function getDesktopVideoPosition(videoWidth,
|
||||
/**
|
||||
* Container for user video.
|
||||
*/
|
||||
class VideoContainer extends LargeContainer {
|
||||
export class VideoContainer extends LargeContainer {
|
||||
// FIXME: With Temasys we have to re-select everytime
|
||||
get $video () {
|
||||
return $('#largeVideo');
|
||||
@@ -164,11 +164,12 @@ class VideoContainer extends LargeContainer {
|
||||
return getStreamOwnerId(this.stream);
|
||||
}
|
||||
|
||||
constructor (onPlay) {
|
||||
constructor (onPlay, emitter) {
|
||||
super();
|
||||
this.stream = null;
|
||||
this.videoType = null;
|
||||
this.localFlipX = true;
|
||||
this.emitter = emitter;
|
||||
|
||||
this.isVisible = false;
|
||||
|
||||
@@ -205,14 +206,14 @@ class VideoContainer extends LargeContainer {
|
||||
let { width, height } = this.getStreamSize();
|
||||
if (this.stream && this.isScreenSharing()) {
|
||||
return getDesktopVideoSize( width,
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
} else {
|
||||
return getCameraVideoSize( width,
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,14 +229,14 @@ class VideoContainer extends LargeContainer {
|
||||
getVideoPosition (width, height, containerWidth, containerHeight) {
|
||||
if (this.stream && this.isScreenSharing()) {
|
||||
return getDesktopVideoPosition( width,
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
} else {
|
||||
return getCameraVideoPosition( width,
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
height,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +245,7 @@ class VideoContainer extends LargeContainer {
|
||||
= this.getVideoSize(containerWidth, containerHeight);
|
||||
let { horizontalIndent, verticalIndent }
|
||||
= this.getVideoPosition(width, height,
|
||||
containerWidth, containerHeight);
|
||||
containerWidth, containerHeight);
|
||||
|
||||
// update avatar position
|
||||
let top = containerHeight / 2 - this.avatarHeight / 4 * 3;
|
||||
@@ -327,6 +328,8 @@ class VideoContainer extends LargeContainer {
|
||||
(show) ? interfaceConfig.DEFAULT_BACKGROUND : "#000");
|
||||
|
||||
this.$avatar.css("visibility", show ? "visible" : "hidden");
|
||||
|
||||
this.emitter.emit(UIEvents.LARGE_VIDEO_AVATAR_DISPLAYED, show);
|
||||
}
|
||||
|
||||
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
||||
@@ -380,304 +383,3 @@ class VideoContainer extends LargeContainer {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manager for all Large containers.
|
||||
*/
|
||||
export default class LargeVideoManager {
|
||||
constructor () {
|
||||
this.containers = {};
|
||||
|
||||
this.state = VIDEO_CONTAINER_TYPE;
|
||||
this.videoContainer = new VideoContainer(
|
||||
() => this.resizeContainer(VIDEO_CONTAINER_TYPE));
|
||||
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
||||
|
||||
// use the same video container to handle and desktop tracks
|
||||
this.addContainer("desktop", this.videoContainer);
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
this.$container = $('#largeVideoContainer');
|
||||
|
||||
this.$container.css({
|
||||
display: 'inline-block'
|
||||
});
|
||||
|
||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
||||
let leftWatermarkDiv
|
||||
= this.$container.find("div.watermark.leftwatermark");
|
||||
|
||||
leftWatermarkDiv.css({display: 'block'});
|
||||
|
||||
leftWatermarkDiv.parent().attr(
|
||||
'href', interfaceConfig.JITSI_WATERMARK_LINK);
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
let rightWatermarkDiv
|
||||
= this.$container.find("div.watermark.rightwatermark");
|
||||
|
||||
rightWatermarkDiv.css({
|
||||
display: 'block',
|
||||
backgroundImage: 'url(images/rightwatermark.png)'
|
||||
});
|
||||
|
||||
rightWatermarkDiv.parent().attr(
|
||||
'href', interfaceConfig.BRAND_WATERMARK_LINK);
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_POWERED_BY) {
|
||||
this.$container.children("a.poweredby").css({display: 'block'});
|
||||
}
|
||||
|
||||
this.$container.hover(
|
||||
e => this.onHoverIn(e),
|
||||
e => this.onHoverOut(e)
|
||||
);
|
||||
}
|
||||
|
||||
onHoverIn (e) {
|
||||
if (!this.state) {
|
||||
return;
|
||||
}
|
||||
let container = this.getContainer(this.state);
|
||||
container.onHoverIn(e);
|
||||
}
|
||||
|
||||
onHoverOut (e) {
|
||||
if (!this.state) {
|
||||
return;
|
||||
}
|
||||
let container = this.getContainer(this.state);
|
||||
container.onHoverOut(e);
|
||||
}
|
||||
|
||||
get id () {
|
||||
let container = this.getContainer(this.state);
|
||||
return container.id;
|
||||
}
|
||||
|
||||
scheduleLargeVideoUpdate () {
|
||||
if (this.updateInProcess || !this.newStreamData) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateInProcess = true;
|
||||
|
||||
let container = this.getContainer(this.state);
|
||||
|
||||
// Include hide()/fadeOut only if we're switching between users
|
||||
let preUpdate;
|
||||
if (this.newStreamData.id != this.id) {
|
||||
preUpdate = container.hide();
|
||||
} else {
|
||||
preUpdate = Promise.resolve();
|
||||
}
|
||||
|
||||
preUpdate.then(() => {
|
||||
let {id, stream, videoType, resolve} = this.newStreamData;
|
||||
this.newStreamData = null;
|
||||
|
||||
console.info("hover in %s", id);
|
||||
this.state = videoType;
|
||||
let container = this.getContainer(this.state);
|
||||
container.setStream(stream, videoType);
|
||||
|
||||
// change the avatar url on large
|
||||
this.updateAvatar(Avatar.getAvatarUrl(id));
|
||||
|
||||
// If we the continer is VIDEO_CONTAINER_TYPE, we need to check
|
||||
// its stream whether exist and is muted to set isVideoMuted
|
||||
// in rest of the cases it is false
|
||||
let isVideoMuted = false;
|
||||
if (videoType == VIDEO_CONTAINER_TYPE)
|
||||
isVideoMuted = stream ? stream.isMuted() : true;
|
||||
|
||||
// show the avatar on large if needed
|
||||
container.showAvatar(isVideoMuted);
|
||||
|
||||
let promise;
|
||||
|
||||
// do not show stream if video is muted
|
||||
// but we still should show watermark
|
||||
if (isVideoMuted) {
|
||||
this.showWatermark(true);
|
||||
promise = Promise.resolve();
|
||||
} else {
|
||||
promise = container.show();
|
||||
}
|
||||
|
||||
// resolve updateLargeVideo promise after everything is done
|
||||
promise.then(resolve);
|
||||
|
||||
return promise;
|
||||
}).then(() => {
|
||||
// after everything is done check again if there are any pending
|
||||
// new streams.
|
||||
this.updateInProcess = false;
|
||||
this.scheduleLargeVideoUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update large video.
|
||||
* Switches to large video even if previously other container was visible.
|
||||
* @param userID the userID of the participant associated with the stream
|
||||
* @param {JitsiTrack?} stream new stream
|
||||
* @param {string?} videoType new video type
|
||||
* @returns {Promise}
|
||||
*/
|
||||
updateLargeVideo (userID, stream, videoType) {
|
||||
if (this.newStreamData) {
|
||||
this.newStreamData.reject();
|
||||
}
|
||||
|
||||
this.newStreamData = createDeferred();
|
||||
this.newStreamData.id = userID;
|
||||
this.newStreamData.stream = stream;
|
||||
this.newStreamData.videoType = videoType;
|
||||
|
||||
this.scheduleLargeVideoUpdate();
|
||||
|
||||
return this.newStreamData.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update container size.
|
||||
*/
|
||||
updateContainerSize () {
|
||||
this.width = UIUtil.getAvailableVideoWidth();
|
||||
this.height = window.innerHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize Large container of specified type.
|
||||
* @param {string} type type of container which should be resized.
|
||||
* @param {boolean} [animate=false] if resize process should be animated.
|
||||
*/
|
||||
resizeContainer (type, animate = false) {
|
||||
let container = this.getContainer(type);
|
||||
container.resize(this.width, this.height, animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize all Large containers.
|
||||
* @param {boolean} animate if resize process should be animated.
|
||||
*/
|
||||
resize (animate) {
|
||||
// resize all containers
|
||||
Object.keys(this.containers)
|
||||
.forEach(type => this.resizeContainer(type, animate));
|
||||
|
||||
this.$container.animate({
|
||||
width: this.width,
|
||||
height: this.height
|
||||
}, {
|
||||
queue: false,
|
||||
duration: animate ? 500 : 0
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the filter indicating a video problem to the user.
|
||||
*
|
||||
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
|
||||
*/
|
||||
enableVideoProblemFilter (enable) {
|
||||
let container = this.getContainer(this.state);
|
||||
container.$video.toggleClass("videoProblemFilter", enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the src of the dominant speaker avatar
|
||||
*/
|
||||
updateAvatar (avatarUrl) {
|
||||
$("#dominantSpeakerAvatar").attr('src', avatarUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide watermark.
|
||||
* @param {boolean} show
|
||||
*/
|
||||
showWatermark (show) {
|
||||
$('.watermark').css('visibility', show ? 'visible' : 'hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add container of specified type.
|
||||
* @param {string} type container type
|
||||
* @param {LargeContainer} container container to add.
|
||||
*/
|
||||
addContainer (type, container) {
|
||||
if (this.containers[type]) {
|
||||
throw new Error(`container of type ${type} already exist`);
|
||||
}
|
||||
|
||||
this.containers[type] = container;
|
||||
this.resizeContainer(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Large container of specified type.
|
||||
* @param {string} type container type.
|
||||
* @returns {LargeContainer}
|
||||
*/
|
||||
getContainer (type) {
|
||||
let container = this.containers[type];
|
||||
|
||||
if (!container) {
|
||||
throw new Error(`container of type ${type} doesn't exist`);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Large container of specified type.
|
||||
* @param {string} type container type.
|
||||
*/
|
||||
removeContainer (type) {
|
||||
if (!this.containers[type]) {
|
||||
throw new Error(`container of type ${type} doesn't exist`);
|
||||
}
|
||||
|
||||
delete this.containers[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Large container of specified type.
|
||||
* Does nothing if such container is already visible.
|
||||
* @param {string} type container type.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
showContainer (type) {
|
||||
if (this.state === type) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let oldContainer = this.containers[this.state];
|
||||
if (this.state === VIDEO_CONTAINER_TYPE) {
|
||||
this.showWatermark(false);
|
||||
}
|
||||
oldContainer.hide();
|
||||
|
||||
this.state = type;
|
||||
let container = this.getContainer(type);
|
||||
|
||||
return container.show().then(() => {
|
||||
if (type === VIDEO_CONTAINER_TYPE) {
|
||||
this.showWatermark(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the flipX state of the local video.
|
||||
* @param val {boolean} true if flipped.
|
||||
*/
|
||||
onLocalFlipXChange(val) {
|
||||
this.videoContainer.setLocalFlipX(val);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@ import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
import RemoteVideo from "./RemoteVideo";
|
||||
import LargeVideoManager, {VIDEO_CONTAINER_TYPE} from "./LargeVideo";
|
||||
import LargeVideoManager from "./LargeVideoManager";
|
||||
import {VIDEO_CONTAINER_TYPE} from "./VideoContainer";
|
||||
import {SHARED_VIDEO_CONTAINER_TYPE} from '../shared_video/SharedVideo';
|
||||
import LocalVideo from "./LocalVideo";
|
||||
|
||||
@@ -102,19 +103,20 @@ var VideoLayout = {
|
||||
});
|
||||
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
||||
// sets default video type of local video
|
||||
// FIXME container type is totally different thing from the video type
|
||||
localVideoThumbnail.setVideoType(VIDEO_CONTAINER_TYPE);
|
||||
// if we do not resize the thumbs here, if there is no video device
|
||||
// the local video thumb maybe one pixel
|
||||
let {thumbWidth, thumbHeight}
|
||||
= this.resizeThumbnails(false, true, false);
|
||||
AudioLevels.updateAudioLevelCanvas(null, thumbWidth, thumbHeight);
|
||||
let { localVideo } = this.resizeThumbnails(false, true);
|
||||
AudioLevels.createAudioLevelCanvas(
|
||||
"local", localVideo.thumbWidth, localVideo.thumbHeight);
|
||||
|
||||
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
|
||||
this.lastNCount = config.channelLastN;
|
||||
},
|
||||
|
||||
initLargeVideo () {
|
||||
largeVideo = new LargeVideoManager();
|
||||
largeVideo = new LargeVideoManager(eventEmitter);
|
||||
if(localFlipX) {
|
||||
largeVideo.onLocalFlipXChange(localFlipX);
|
||||
}
|
||||
@@ -255,7 +257,8 @@ var VideoLayout = {
|
||||
electLastVisibleVideo () {
|
||||
// pick the last visible video in the row
|
||||
// if nobody else is left, this picks the local video
|
||||
let thumbs = FilmStrip.getThumbs(true).filter('[id!="mixedstream"]');
|
||||
let remoteThumbs = FilmStrip.getThumbs(true).remoteThumbs;
|
||||
let thumbs = remoteThumbs.filter('[id!="mixedstream"]');
|
||||
|
||||
let lastVisible = thumbs.filter(':visible:last');
|
||||
if (lastVisible.length) {
|
||||
@@ -269,7 +272,7 @@ var VideoLayout = {
|
||||
}
|
||||
|
||||
console.info("Last visible video no longer exists");
|
||||
thumbs = FilmStrip.getThumbs();
|
||||
thumbs = FilmStrip.getThumbs().remoteThumbs;
|
||||
if (thumbs.length) {
|
||||
let id = getPeerContainerResourceId(thumbs[0]);
|
||||
if (remoteVideos[id]) {
|
||||
@@ -396,13 +399,14 @@ var VideoLayout = {
|
||||
let videoType = VideoLayout.getRemoteVideoType(id);
|
||||
if (!videoType) {
|
||||
// make video type the default one (camera)
|
||||
// FIXME container type is not a video type
|
||||
videoType = VIDEO_CONTAINER_TYPE;
|
||||
}
|
||||
remoteVideo.setVideoType(videoType);
|
||||
|
||||
// In case this is not currently in the last n we don't show it.
|
||||
if (localLastNCount && localLastNCount > 0 &&
|
||||
FilmStrip.getThumbs().length >= localLastNCount + 2) {
|
||||
FilmStrip.getThumbs().remoteThumbs.length >= localLastNCount + 2) {
|
||||
remoteVideo.showPeerContainer('hide');
|
||||
} else {
|
||||
VideoLayout.resizeThumbnails(false, true);
|
||||
@@ -414,7 +418,7 @@ var VideoLayout = {
|
||||
console.info(resourceJid + " video is now active", videoelem);
|
||||
|
||||
VideoLayout.resizeThumbnails(
|
||||
false, false, false, function() {$(videoelem).show();});
|
||||
false, false, function() {$(videoelem).show();});
|
||||
|
||||
// Update the large video to the last added video only if there's no
|
||||
// current dominant, focused speaker or update it to
|
||||
@@ -487,19 +491,19 @@ var VideoLayout = {
|
||||
forceUpdate = false,
|
||||
onComplete = null) {
|
||||
|
||||
let {thumbWidth, thumbHeight}
|
||||
let { localVideo, remoteVideo }
|
||||
= FilmStrip.calculateThumbnailSize();
|
||||
|
||||
$('.userAvatar').css('left', (thumbWidth - thumbHeight) / 2);
|
||||
let {thumbWidth, thumbHeight} = remoteVideo;
|
||||
|
||||
FilmStrip.resizeThumbnails(thumbWidth, thumbHeight,
|
||||
FilmStrip.resizeThumbnails(localVideo, remoteVideo,
|
||||
animate, forceUpdate)
|
||||
.then(function () {
|
||||
AudioLevels.updateCanvasSize(thumbWidth, thumbHeight);
|
||||
AudioLevels.updateCanvasSize(localVideo, remoteVideo);
|
||||
if (onComplete && typeof onComplete === "function")
|
||||
onComplete();
|
||||
});
|
||||
return {thumbWidth, thumbHeight};
|
||||
});
|
||||
return { localVideo, remoteVideo };
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -657,7 +661,7 @@ var VideoLayout = {
|
||||
var updateLargeVideo = false;
|
||||
|
||||
// Handle LastN/local LastN changes.
|
||||
FilmStrip.getThumbs().each(( index, element ) => {
|
||||
FilmStrip.getThumbs().remoteThumbs.each(( index, element ) => {
|
||||
var resourceJid = getPeerContainerResourceId(element);
|
||||
var smallVideo = remoteVideos[resourceJid];
|
||||
|
||||
@@ -995,6 +999,7 @@ var VideoLayout = {
|
||||
|
||||
if (!isOnLarge || forceUpdate) {
|
||||
let videoType = this.getRemoteVideoType(id);
|
||||
// FIXME video type is not the same thing as container type
|
||||
if (id !== currentId && videoType === VIDEO_CONTAINER_TYPE) {
|
||||
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
var animateTimeout, updateTimeout;
|
||||
|
||||
var RoomnameGenerator = require("../../util/RoomnameGenerator");
|
||||
import UIUtil from "../util/UIUtil";
|
||||
|
||||
function enter_room() {
|
||||
var val = $("#enter_room_field").val();
|
||||
@@ -39,10 +40,10 @@ function setupWelcomePage() {
|
||||
$("#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;
|
||||
UIUtil.setLinkHref(
|
||||
leftWatermarkDiv.parent(),
|
||||
interfaceConfig.JITSI_WATERMARK_LINK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
||||
@@ -50,8 +51,9 @@ function setupWelcomePage() {
|
||||
$("#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;
|
||||
UIUtil.setLinkHref(
|
||||
rightWatermarkDiv.parent(),
|
||||
interfaceConfig.BRAND_WATERMARK_LINK);
|
||||
rightWatermarkDiv.get(0).style.backgroundImage =
|
||||
"url(images/rightwatermark.png)";
|
||||
}
|
||||
|
||||
@@ -17,11 +17,6 @@ function initGlobalShortcuts() {
|
||||
APP.UI.toggleKeyboardShortcutsPanel();
|
||||
}, "keyboardShortcuts.toggleShortcuts");
|
||||
|
||||
KeyboardShortcut.registerShortcut("R", null, function() {
|
||||
JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
|
||||
APP.conference.maybeToggleRaisedHand();
|
||||
}, "keyboardShortcuts.raiseHand");
|
||||
|
||||
KeyboardShortcut.registerShortcut("T", null, function() {
|
||||
JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
|
||||
APP.conference.muteAudio(true);
|
||||
@@ -79,13 +74,6 @@ var KeyboardShortcut = {
|
||||
}
|
||||
}
|
||||
};
|
||||
$('body').popover({ selector: '[data-toggle=popover]',
|
||||
trigger: 'click hover',
|
||||
content: function() {
|
||||
return this.getAttribute("content")
|
||||
+ self._getShortcutTooltip(this.getAttribute("shortcut"));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -133,7 +121,7 @@ var KeyboardShortcut = {
|
||||
* or an empty string if the shortcutAttr is null, an empty string or not
|
||||
* found in the shortcut mapping
|
||||
*/
|
||||
_getShortcutTooltip: function (shortcutAttr) {
|
||||
getShortcutTooltip: function (shortcutAttr) {
|
||||
if (typeof shortcutAttr === "string" && shortcutAttr.length > 0) {
|
||||
for (var key in _shortcuts) {
|
||||
if (_shortcuts.hasOwnProperty(key)
|
||||
|
||||
@@ -174,10 +174,12 @@ export default {
|
||||
* Set device id of the camera which is currently in use.
|
||||
* Empty string stands for default device.
|
||||
* @param {string} newId new camera device id
|
||||
* @param {boolean} whether we need to store the value
|
||||
*/
|
||||
setCameraDeviceId: function (newId = '') {
|
||||
setCameraDeviceId: function (newId, store) {
|
||||
cameraDeviceId = newId;
|
||||
window.localStorage.cameraDeviceId = newId;
|
||||
if (store)
|
||||
window.localStorage.cameraDeviceId = newId;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -192,10 +194,12 @@ export default {
|
||||
* Set device id of the microphone which is currently in use.
|
||||
* Empty string stands for default device.
|
||||
* @param {string} newId new microphone device id
|
||||
* @param {boolean} whether we need to store the value
|
||||
*/
|
||||
setMicDeviceId: function (newId = '') {
|
||||
setMicDeviceId: function (newId, store) {
|
||||
micDeviceId = newId;
|
||||
window.localStorage.micDeviceId = newId;
|
||||
if (store)
|
||||
window.localStorage.micDeviceId = newId;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
20
package.json
20
package.json
@@ -16,23 +16,24 @@
|
||||
"readmeFilename": "README.md",
|
||||
"//": "Callstats.io does not work with recent versions of jsSHA (2.0.1 in particular)",
|
||||
"dependencies": {
|
||||
"@atlassian/aui": "^6.0.0",
|
||||
"async": "0.9.0",
|
||||
"autosize": "^1.18.13",
|
||||
"bootstrap": "3.1.1",
|
||||
"events": "*",
|
||||
"i18next-client": "1.7.7",
|
||||
"jquery": "~2.1.1",
|
||||
"jQuery-Impromptu": "git+https://github.com/trentrichardson/jQuery-Impromptu.git#v6.0.0",
|
||||
"lib-jitsi-meet": "git+https://github.com/jitsi/lib-jitsi-meet.git",
|
||||
"jquery": "~2.1.1",
|
||||
"jquery-contextmenu": "*",
|
||||
"jquery-ui": "1.10.5",
|
||||
"jssha": "1.5.0",
|
||||
"jws": "*",
|
||||
"lib-jitsi-meet": "git+https://github.com/jitsi/lib-jitsi-meet.git",
|
||||
"postis": "^2.2.0",
|
||||
"retry": "0.6.1",
|
||||
"strophe": "^1.2.2",
|
||||
"strophejs-plugins": "^0.0.6",
|
||||
"toastr": "^2.0.3",
|
||||
"postis": "^2.2.0",
|
||||
"jws": "*"
|
||||
"toastr": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-polyfill": "*",
|
||||
@@ -83,7 +84,11 @@
|
||||
"tooltip": "./node_modules/bootstrap/js/tooltip.js",
|
||||
"popover": "./node_modules/bootstrap/js/popover.js",
|
||||
"jQuery-Impromptu": "./node_modules/jQuery-Impromptu/dist/jquery-impromptu.js",
|
||||
"autosize": "./node_modules/autosize/build/jquery.autosize.js"
|
||||
"autosize": "./node_modules/autosize/build/jquery.autosize.js",
|
||||
"aui": "./node_modules/@atlassian/aui/dist/aui/js/aui.js",
|
||||
"aui-experimental": "./node_modules/@atlassian/aui/dist/aui/js/aui-experimental.js",
|
||||
"aui-css": "./node_modules/@atlassian/aui/dist/aui/css/aui.min.css",
|
||||
"aui-experimental-css": "./node_modules/@atlassian/aui/dist/aui/css/aui-experimental.min.css"
|
||||
},
|
||||
"browserify-shim": {
|
||||
"jquery": [
|
||||
@@ -109,6 +114,9 @@
|
||||
"jQuery-Impromptu": {
|
||||
"depends": "jquery:jQuery"
|
||||
},
|
||||
"aui-experimental": {
|
||||
"depends": "aui:AJS"
|
||||
},
|
||||
"jquery-contextmenu": {
|
||||
"depends": "jquery:jQuery"
|
||||
},
|
||||
|
||||
@@ -105,5 +105,15 @@ export default {
|
||||
* event must contain the identifier of the container that has been toggled
|
||||
* and information about toggle on or off.
|
||||
*/
|
||||
SIDE_TOOLBAR_CONTAINER_TOGGLED: "UI.side_container_toggled"
|
||||
SIDE_TOOLBAR_CONTAINER_TOGGLED: "UI.side_container_toggled",
|
||||
|
||||
/**
|
||||
* Notifies that the raise hand has been changed.
|
||||
*/
|
||||
LOCAL_RAISE_HAND_CHANGED: "UI.local_raise_hand_changed",
|
||||
|
||||
/**
|
||||
* Notifies that the avatar is displayed or not on the largeVideo.
|
||||
*/
|
||||
LARGE_VIDEO_AVATAR_DISPLAYED: "UI.large_video_avatar_displayed"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user