refactoring of Etherpad and Prezi
This commit is contained in:
parent
e494c3028d
commit
0fd0f5b633
51
app.js
51
app.js
|
@ -41,6 +41,9 @@ const Commands = {
|
||||||
CONNECTION_QUALITY: "connectionQuality",
|
CONNECTION_QUALITY: "connectionQuality",
|
||||||
EMAIL: "email",
|
EMAIL: "email",
|
||||||
VIDEO_TYPE: "videoType"
|
VIDEO_TYPE: "videoType"
|
||||||
|
ETHERPAD: "etherpad",
|
||||||
|
PREZI: "prezi",
|
||||||
|
STOP_PREZI: "stop-prezi"
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildRoomName () {
|
function buildRoomName () {
|
||||||
|
@ -218,16 +221,14 @@ function initConference(localTracks, connection) {
|
||||||
|
|
||||||
|
|
||||||
room.on(ConferenceEvents.USER_JOINED, function (id, user) {
|
room.on(ConferenceEvents.USER_JOINED, function (id, user) {
|
||||||
if (APP.conference.isLocalId(id)) {
|
console.error('USER %s connnected', id, user);
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.error('USER %s connnected', id);
|
|
||||||
// FIXME email???
|
// FIXME email???
|
||||||
APP.UI.addUser(id, user.getDisplayName());
|
APP.UI.addUser(id, user.getDisplayName());
|
||||||
});
|
});
|
||||||
room.on(ConferenceEvents.USER_LEFT, function (id, user) {
|
room.on(ConferenceEvents.USER_LEFT, function (id, user) {
|
||||||
console.error('USER LEFT', id);
|
console.error('USER %s LEFT', id, user);
|
||||||
APP.UI.removeUser(id, user.getDisplayName());
|
APP.UI.removeUser(id, user.getDisplayName());
|
||||||
|
APP.UI.stopPrezi(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -348,9 +349,12 @@ function initConference(localTracks, connection) {
|
||||||
room.removeCommand(Commands.CONNECTION_QUALITY);
|
room.removeCommand(Commands.CONNECTION_QUALITY);
|
||||||
});
|
});
|
||||||
// listen to remote stats
|
// listen to remote stats
|
||||||
room.addCommandListener(Commands.CONNECTION_QUALITY, function (data) {
|
room.addCommandListener(
|
||||||
APP.connectionquality.updateRemoteStats(data.attributes.id, data.value);
|
Commands.CONNECTION_QUALITY,
|
||||||
});
|
function ({value, attributes}) {
|
||||||
|
APP.connectionquality.updateRemoteStats(attributes.id, value);
|
||||||
|
}
|
||||||
|
);
|
||||||
APP.connectionquality.addListener(
|
APP.connectionquality.addListener(
|
||||||
CQEvents.REMOTESTATS_UPDATED,
|
CQEvents.REMOTESTATS_UPDATED,
|
||||||
function (id, percent, stats) {
|
function (id, percent, stats) {
|
||||||
|
@ -358,6 +362,37 @@ function initConference(localTracks, connection) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
room.addCommandListener(Commands.ETHERPAD, function ({value}) {
|
||||||
|
APP.UI.initEtherpad(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
room.addCommandListener(Commands.PREZI, function ({value, attributes}) {
|
||||||
|
APP.UI.showPrezi(attributes.id, value, attributes.slide);
|
||||||
|
});
|
||||||
|
room.addCommandListener(Commands.STOP_PREZI, function ({attributes}) {
|
||||||
|
APP.UI.stopPrezi(attributes.id);
|
||||||
|
});
|
||||||
|
APP.UI.addListener(UIEvents.SHARE_PREZI, function (url, slide) {
|
||||||
|
console.log('Sharing Prezi %s slide %s', url, slide);
|
||||||
|
room.removeCommand(Commands.PREZI);
|
||||||
|
room.sendCommand(Commands.PREZI, {
|
||||||
|
value: url,
|
||||||
|
attributes: {
|
||||||
|
id: room.myUserId(),
|
||||||
|
slide
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
APP.UI.addListener(UIEvents.STOP_SHARING_PREZI, function () {
|
||||||
|
room.removeCommand(Commands.PREZI);
|
||||||
|
room.sendCommandOnce(Commands.STOP_PREZI, {
|
||||||
|
attributes: {
|
||||||
|
id: room.myUserId()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
room.addCommandListener(Commands.VIDEO_TYPE, (data, from) => {
|
room.addCommandListener(Commands.VIDEO_TYPE, (data, from) => {
|
||||||
APP.UI.onPeerVideoTypeChanged(from, data.value);
|
APP.UI.onPeerVideoTypeChanged(from, data.value);
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#remoteVideos .videocontainer {
|
#remoteVideos .videocontainer {
|
||||||
display: inline-block;
|
display: none;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
border-radius:8px;
|
border-radius:8px;
|
||||||
|
|
19
index.html
19
index.html
|
@ -140,6 +140,25 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
|
<div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
|
||||||
<div id="videospace">
|
<div id="videospace">
|
||||||
|
|
||||||
|
<div id="largeVideoContainer" class="videocontainer">
|
||||||
|
<div id="presentation"></div>
|
||||||
|
<div id="etherpad"></div>
|
||||||
|
<a target="_new"><div class="watermark leftwatermark"></div></a>
|
||||||
|
<a target="_new"><div class="watermark rightwatermark"></div></a>
|
||||||
|
<a class="poweredby" href="http://jitsi.org" target="_new">
|
||||||
|
<span data-i18n="poweredby"></span> jitsi.org
|
||||||
|
</a>
|
||||||
|
<div id="activeSpeaker">
|
||||||
|
<img id="activeSpeakerAvatar" src=""/>
|
||||||
|
<canvas id="activeSpeakerAudioLevel"></canvas>
|
||||||
|
</div>
|
||||||
|
<div id="largeVideoWrapper">
|
||||||
|
<video id="largeVideo" muted="true" autoplay oncontextmenu="return false;"></video>
|
||||||
|
</div>
|
||||||
|
<span id="videoConnectionMessage"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="remoteVideos">
|
<div id="remoteVideos">
|
||||||
<span id="localVideoContainer" class="videocontainer">
|
<span id="localVideoContainer" class="videocontainer">
|
||||||
<span id="localNick" class="nick"></span>
|
<span id="localNick" class="nick"></span>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,6 @@
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var UI = {};
|
var UI = {};
|
||||||
|
|
||||||
import AudioLevels from './audio_levels/AudioLevels';
|
|
||||||
import Chat from "./side_pannels/chat/Chat";
|
import Chat from "./side_pannels/chat/Chat";
|
||||||
import Toolbar from "./toolbars/Toolbar";
|
import Toolbar from "./toolbars/Toolbar";
|
||||||
import ToolbarToggler from "./toolbars/ToolbarToggler";
|
import ToolbarToggler from "./toolbars/ToolbarToggler";
|
||||||
|
@ -12,12 +11,12 @@ import Avatar from "./avatar/Avatar";
|
||||||
import PanelToggler from "./side_pannels/SidePanelToggler";
|
import PanelToggler from "./side_pannels/SidePanelToggler";
|
||||||
import UIUtil from "./util/UIUtil";
|
import UIUtil from "./util/UIUtil";
|
||||||
import UIEvents from "../../service/UI/UIEvents";
|
import UIEvents from "../../service/UI/UIEvents";
|
||||||
|
import PreziManager from './prezi/Prezi';
|
||||||
|
import EtherpadManager from './etherpad/Etherpad';
|
||||||
|
|
||||||
import VideoLayout from "./videolayout/VideoLayout";
|
import VideoLayout from "./videolayout/VideoLayout";
|
||||||
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
||||||
|
|
||||||
var Prezi = require("./prezi/Prezi");
|
|
||||||
var Etherpad = require("./etherpad/Etherpad");
|
|
||||||
var EventEmitter = require("events");
|
var EventEmitter = require("events");
|
||||||
var Settings = require("./../settings/Settings");
|
var Settings = require("./../settings/Settings");
|
||||||
UI.messageHandler = require("./util/MessageHandler");
|
UI.messageHandler = require("./util/MessageHandler");
|
||||||
|
@ -32,6 +31,9 @@ var Feedback = require("./Feedback");
|
||||||
var eventEmitter = new EventEmitter();
|
var eventEmitter = new EventEmitter();
|
||||||
UI.eventEmitter = eventEmitter;
|
UI.eventEmitter = eventEmitter;
|
||||||
|
|
||||||
|
let preziManager;
|
||||||
|
let etherpadManager;
|
||||||
|
|
||||||
function promptDisplayName() {
|
function promptDisplayName() {
|
||||||
let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
|
let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
|
||||||
let defaultNickMsg = APP.translation.translateString(
|
let defaultNickMsg = APP.translation.translateString(
|
||||||
|
@ -77,12 +79,6 @@ function promptDisplayName() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPrezi() {
|
|
||||||
$("#reloadPresentationLink").click(function() {
|
|
||||||
Prezi.reloadPresentation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupChat() {
|
function setupChat() {
|
||||||
Chat.init(eventEmitter);
|
Chat.init(eventEmitter);
|
||||||
$("#toggle_smileys").click(function() {
|
$("#toggle_smileys").click(function() {
|
||||||
|
@ -189,20 +185,18 @@ UI.initConference = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
function registerListeners() {
|
function registerListeners() {
|
||||||
UI.addListener(UIEvents.LARGEVIDEO_INIT, function () {
|
|
||||||
AudioLevels.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
|
UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
|
||||||
UI.setUserAvatar(APP.conference.localId, email);
|
UI.setUserAvatar(APP.conference.localId, email);
|
||||||
});
|
});
|
||||||
|
|
||||||
UI.addListener(UIEvents.PREZI_CLICKED, function () {
|
UI.addListener(UIEvents.PREZI_CLICKED, function () {
|
||||||
Prezi.openPreziDialog();
|
preziManager.handlePreziButtonClicked();
|
||||||
});
|
});
|
||||||
|
|
||||||
UI.addListener(UIEvents.ETHERPAD_CLICKED, function () {
|
UI.addListener(UIEvents.ETHERPAD_CLICKED, function () {
|
||||||
Etherpad.toggleEtherpad(0);
|
if (etherpadManager) {
|
||||||
|
etherpadManager.toggleEtherpad();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
|
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
|
||||||
|
@ -221,7 +215,7 @@ function registerListeners() {
|
||||||
function bindEvents() {
|
function bindEvents() {
|
||||||
function onResize() {
|
function onResize() {
|
||||||
PanelToggler.resizeChat();
|
PanelToggler.resizeChat();
|
||||||
VideoLayout.resizeLargeVideoContainer();
|
VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize and reposition videos in full screen mode.
|
// Resize and reposition videos in full screen mode.
|
||||||
|
@ -254,11 +248,17 @@ UI.start = function () {
|
||||||
registerListeners();
|
registerListeners();
|
||||||
|
|
||||||
VideoLayout.init(eventEmitter);
|
VideoLayout.init(eventEmitter);
|
||||||
|
if (!interfaceConfig.filmStripOnly) {
|
||||||
|
VideoLayout.initLargeVideo(PanelToggler.isVisible());
|
||||||
|
}
|
||||||
|
VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
|
||||||
|
|
||||||
ContactList.init(eventEmitter);
|
ContactList.init(eventEmitter);
|
||||||
|
|
||||||
bindEvents();
|
bindEvents();
|
||||||
setupPrezi();
|
preziManager = new PreziManager(eventEmitter);
|
||||||
if (!interfaceConfig.filmStripOnly) {
|
if (!interfaceConfig.filmStripOnly) {
|
||||||
|
|
||||||
$("#videospace").mousemove(function () {
|
$("#videospace").mousemove(function () {
|
||||||
return ToolbarToggler.showToolbar();
|
return ToolbarToggler.showToolbar();
|
||||||
});
|
});
|
||||||
|
@ -350,9 +350,14 @@ UI.setSubject = function (subject) {
|
||||||
Chat.setSubject(subject);
|
Chat.setSubject(subject);
|
||||||
};
|
};
|
||||||
|
|
||||||
function initEtherpad(name) {
|
UI.initEtherpad = function (name) {
|
||||||
Etherpad.init(name);
|
if (etherpadManager) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
console.log('Etherpad is enabled');
|
||||||
|
etherpadManager = new EtherpadManager(config.etherpad_base, name);
|
||||||
|
Toolbar.showEtherpadButton();
|
||||||
|
};
|
||||||
|
|
||||||
UI.addUser = function (id, displayName) {
|
UI.addUser = function (id, displayName) {
|
||||||
ContactList.addContact(id);
|
ContactList.addContact(id);
|
||||||
|
@ -443,7 +448,6 @@ UI.getSettings = function () {
|
||||||
|
|
||||||
UI.toggleFilmStrip = function () {
|
UI.toggleFilmStrip = function () {
|
||||||
BottomToolbar.toggleFilmStrip();
|
BottomToolbar.toggleFilmStrip();
|
||||||
VideoLayout.updateLargeVideoSize();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
UI.toggleChat = function () {
|
UI.toggleChat = function () {
|
||||||
|
@ -592,9 +596,7 @@ UI.handleLastNEndpoints = function (ids) {
|
||||||
};
|
};
|
||||||
|
|
||||||
UI.setAudioLevel = function (id, lvl) {
|
UI.setAudioLevel = function (id, lvl) {
|
||||||
AudioLevels.updateAudioLevel(
|
VideoLayout.setAudioLevel(id, lvl);
|
||||||
id, lvl, VideoLayout.getLargeVideoId()
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
UI.updateDesktopSharingButtons = function () {
|
UI.updateDesktopSharingButtons = function () {
|
||||||
|
@ -750,4 +752,14 @@ UI.updateAuthInfo = function (isAuthEnabled, login) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
UI.showPrezi = function (userId, url, slide) {
|
||||||
|
preziManager.showPrezi(userId, url, slide);
|
||||||
|
};
|
||||||
|
|
||||||
|
UI.stopPrezi = function (userId) {
|
||||||
|
if (preziManager.isSharing(userId)) {
|
||||||
|
preziManager.removePrezi(userId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = UI;
|
module.exports = UI;
|
||||||
|
|
|
@ -112,33 +112,6 @@ function getVideoSpanId(id) {
|
||||||
return videoSpanId;
|
return videoSpanId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that the remote video has been resized.
|
|
||||||
*/
|
|
||||||
$(document).bind('remotevideo.resized', function (event, width, height) {
|
|
||||||
let resized = false;
|
|
||||||
|
|
||||||
$('#remoteVideos>span>canvas').each(function() {
|
|
||||||
let canvas = $(this).get(0);
|
|
||||||
if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
|
|
||||||
canvas.width = width + interfaceConfig.CANVAS_EXTRA;
|
|
||||||
resized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canvas.height !== height + interfaceConfig.CANVAS_EXTRA) {
|
|
||||||
canvas.height = height + interfaceConfig.CANVAS_EXTRA;
|
|
||||||
resized = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (resized) {
|
|
||||||
Object.keys(audioLevelCanvasCache).forEach(function (id) {
|
|
||||||
audioLevelCanvasCache[id].width = width + interfaceConfig.CANVAS_EXTRA;
|
|
||||||
audioLevelCanvasCache[id].height = height + interfaceConfig.CANVAS_EXTRA;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The audio Levels plugin.
|
* The audio Levels plugin.
|
||||||
*/
|
*/
|
||||||
|
@ -248,6 +221,33 @@ const AudioLevels = {
|
||||||
|
|
||||||
// Fill the shape.
|
// Fill the shape.
|
||||||
ASDrawContext.fill();
|
ASDrawContext.fill();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the remote video has been resized.
|
||||||
|
*/
|
||||||
|
onRemoteVideoResized (width, height) {
|
||||||
|
let resized = false;
|
||||||
|
|
||||||
|
$('#remoteVideos>span>canvas').each(function() {
|
||||||
|
let canvas = $(this).get(0);
|
||||||
|
if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
|
||||||
|
canvas.width = width + interfaceConfig.CANVAS_EXTRA;
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas.height !== height + interfaceConfig.CANVAS_EXTRA) {
|
||||||
|
canvas.height = height + interfaceConfig.CANVAS_EXTRA;
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resized) {
|
||||||
|
Object.keys(audioLevelCanvasCache).forEach(function (id) {
|
||||||
|
audioLevelCanvasCache[id].width = width + interfaceConfig.CANVAS_EXTRA;
|
||||||
|
audioLevelCanvasCache[id].height = height + interfaceConfig.CANVAS_EXTRA;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,16 @@
|
||||||
/* global $, config,
|
/* global $ */
|
||||||
setLargeVideoVisible, Util */
|
|
||||||
|
|
||||||
var VideoLayout = require("../videolayout/VideoLayout");
|
import VideoLayout from "../videolayout/VideoLayout";
|
||||||
var Prezi = require("../prezi/Prezi");
|
import LargeContainer from '../videolayout/LargeContainer';
|
||||||
var UIUtil = require("../util/UIUtil");
|
import UIUtil from "../util/UIUtil";
|
||||||
|
import SidePanelToggler from "../side_pannels/SidePanelToggler";
|
||||||
|
|
||||||
var etherpadName = null;
|
const options = $.param({
|
||||||
var etherpadIFrame = null;
|
showControns: true,
|
||||||
var domain = null;
|
showChat: false,
|
||||||
var options = "?showControls=true&showChat=false&showLineNumbers=true" +
|
showLineNumbers: true,
|
||||||
"&useMonospaceFont=false";
|
useMonospaceFont: false
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resizes the etherpad.
|
|
||||||
*/
|
|
||||||
function resize() {
|
|
||||||
if ($('#etherpad>iframe').length) {
|
|
||||||
var remoteVideos = $('#remoteVideos');
|
|
||||||
var availableHeight
|
|
||||||
= window.innerHeight - remoteVideos.outerHeight();
|
|
||||||
var availableWidth = UIUtil.getAvailableVideoWidth();
|
|
||||||
|
|
||||||
$('#etherpad>iframe').width(availableWidth);
|
|
||||||
$('#etherpad>iframe').height(availableHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the Etherpad button and adds it to the toolbar.
|
|
||||||
*/
|
|
||||||
function enableEtherpadButton() {
|
|
||||||
if (!$('#toolbar_button_etherpad').is(":visible"))
|
|
||||||
$('#toolbar_button_etherpad').css({display: 'inline-block'});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the IFrame for the etherpad.
|
|
||||||
*/
|
|
||||||
function createIFrame() {
|
|
||||||
etherpadIFrame = VideoLayout.createEtherpadIframe(
|
|
||||||
domain + etherpadName + options, function() {
|
|
||||||
|
|
||||||
document.domain = document.domain;
|
|
||||||
bubbleIframeMouseMove(etherpadIFrame);
|
|
||||||
setTimeout(function() {
|
|
||||||
// the iframes inside of the etherpad are
|
|
||||||
// not yet loaded when the etherpad iframe is loaded
|
|
||||||
var outer = etherpadIFrame.
|
|
||||||
contentDocument.getElementsByName("ace_outer")[0];
|
|
||||||
bubbleIframeMouseMove(outer);
|
|
||||||
var inner = outer.
|
|
||||||
contentDocument.getElementsByName("ace_inner")[0];
|
|
||||||
bubbleIframeMouseMove(inner);
|
|
||||||
}, 2000);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function bubbleIframeMouseMove(iframe){
|
function bubbleIframeMouseMove(iframe){
|
||||||
var existingOnMouseMove = iframe.contentWindow.onmousemove;
|
var existingOnMouseMove = iframe.contentWindow.onmousemove;
|
||||||
|
@ -84,48 +39,123 @@ function bubbleIframeMouseMove(iframe){
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_WIDTH = 640;
|
||||||
|
const DEFAULT_HEIGHT = 480;
|
||||||
|
|
||||||
var Etherpad = {
|
const EtherpadContainerType = "etherpad";
|
||||||
/**
|
|
||||||
* Initializes the etherpad.
|
|
||||||
*/
|
|
||||||
init: function (name) {
|
|
||||||
|
|
||||||
if (config.etherpad_base && !etherpadName && name) {
|
class Etherpad extends LargeContainer {
|
||||||
|
constructor (domain, name) {
|
||||||
|
super();
|
||||||
|
|
||||||
domain = config.etherpad_base;
|
const iframe = document.createElement('iframe');
|
||||||
|
|
||||||
etherpadName = name;
|
iframe.src = domain + name + '?' + options;
|
||||||
|
iframe.frameBorder = 0;
|
||||||
|
iframe.scrolling = "no";
|
||||||
|
iframe.width = DEFAULT_WIDTH;
|
||||||
|
iframe.height = DEFAULT_HEIGHT;
|
||||||
|
iframe.setAttribute('style', 'visibility: hidden;');
|
||||||
|
|
||||||
enableEtherpadButton();
|
this.container.appendChild(iframe);
|
||||||
|
|
||||||
/**
|
iframe.onload = function() {
|
||||||
* Resizes the etherpad, when the window is resized.
|
document.domain = document.domain;
|
||||||
*/
|
bubbleIframeMouseMove(iframe);
|
||||||
$(window).resize(function () {
|
|
||||||
resize();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
setTimeout(function() {
|
||||||
* Opens/hides the Etherpad.
|
const doc = iframe.contentDocument;
|
||||||
*/
|
|
||||||
toggleEtherpad: function (isPresentation) {
|
|
||||||
if (!etherpadIFrame)
|
|
||||||
createIFrame();
|
|
||||||
|
|
||||||
|
// the iframes inside of the etherpad are
|
||||||
|
// not yet loaded when the etherpad iframe is loaded
|
||||||
|
const outer = doc.getElementsByName("ace_outer")[0];
|
||||||
|
bubbleIframeMouseMove(outer);
|
||||||
|
|
||||||
if(VideoLayout.getLargeVideoState() === "etherpad")
|
const inner = doc.getElementsByName("ace_inner")[0];
|
||||||
{
|
bubbleIframeMouseMove(inner);
|
||||||
VideoLayout.setLargeVideoState("video");
|
}, 2000);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VideoLayout.setLargeVideoState("etherpad");
|
|
||||||
}
|
|
||||||
resize();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Etherpad;
|
this.iframe = iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isOpen () {
|
||||||
|
return !!this.iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
get container () {
|
||||||
|
return document.getElementById('etherpad');
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (containerWidth, containerHeight, animate) {
|
||||||
|
let remoteVideos = $('#remoteVideos');
|
||||||
|
|
||||||
|
let height = containerHeight - remoteVideos.outerHeight();
|
||||||
|
let width = containerWidth;
|
||||||
|
|
||||||
|
$(this.iframe).width(width).height(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
const $iframe = $(this.iframe);
|
||||||
|
const $container = $(this.container);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
$iframe.fadeIn(300, function () {
|
||||||
|
document.body.style.background = '#eeeeee';
|
||||||
|
$iframe.css({visibility: 'visible'});
|
||||||
|
$container.css({zIndex: 2});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
const $iframe = $(this.iframe);
|
||||||
|
const $container = $(this.container);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
$iframe.fadeOut(300, function () {
|
||||||
|
$iframe.css({visibility: 'hidden'});
|
||||||
|
$container.css({zIndex: 0});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class EtherpadManager {
|
||||||
|
constructor (domain, name) {
|
||||||
|
if (!domain || !name) {
|
||||||
|
throw new Error("missing domain or name");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.domain = domain;
|
||||||
|
this.name = name;
|
||||||
|
this.etherpad = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isOpen () {
|
||||||
|
return !!this.etherpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
openEtherpad () {
|
||||||
|
this.etherpad = new Etherpad(this.domain, this.name);
|
||||||
|
VideoLayout.addLargeVideoContainer(
|
||||||
|
EtherpadContainerType,
|
||||||
|
this.etherpad
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEtherpad () {
|
||||||
|
if (!this.isOpen) {
|
||||||
|
this.openEtherpad();
|
||||||
|
}
|
||||||
|
|
||||||
|
let isVisible = VideoLayout.isLargeContainerTypeVisible(
|
||||||
|
EtherpadContainerType
|
||||||
|
);
|
||||||
|
|
||||||
|
VideoLayout.showLargeVideoContainer(EtherpadContainerType, !isVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,268 +1,21 @@
|
||||||
/* global $, APP */
|
/* global $, APP */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
import UIUtil from "../util/UIUtil";
|
|
||||||
import VideoLayout from "../videolayout/VideoLayout";
|
import VideoLayout from "../videolayout/VideoLayout";
|
||||||
|
import LargeContainer from '../videolayout/LargeContainer';
|
||||||
|
import PreziPlayer from './PreziPlayer';
|
||||||
|
import UIUtil from '../util/UIUtil';
|
||||||
|
import UIEvents from '../../../service/UI/UIEvents';
|
||||||
|
import messageHandler from '../util/MessageHandler';
|
||||||
|
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
||||||
|
import SidePanelToggler from "../side_pannels/SidePanelToggler";
|
||||||
|
|
||||||
var messageHandler = require("../util/MessageHandler");
|
const defaultPreziLink = "http://prezi.com/wz7vhjycl7e6/my-prezi";
|
||||||
var PreziPlayer = require("./PreziPlayer");
|
const alphanumRegex = /^[a-z0-9-_\/&\?=;]+$/i;
|
||||||
|
const aspectRatio = 16.0 / 9.0;
|
||||||
|
|
||||||
var preziPlayer = null;
|
const DEFAULT_WIDTH = 640;
|
||||||
|
const DEFAULT_HEIGHT = 480;
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows/hides a presentation.
|
|
||||||
*/
|
|
||||||
function setPresentationVisible(visible) {
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
VideoLayout.setLargeVideoState("prezi");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VideoLayout.setLargeVideoState("video");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var Prezi = {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads the current presentation.
|
|
||||||
*/
|
|
||||||
reloadPresentation: function() {
|
|
||||||
var iframe = document.getElementById(preziPlayer.options.preziId);
|
|
||||||
iframe.src = iframe.src;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns <tt>true</tt> if the presentation is visible, <tt>false</tt> -
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
isPresentationVisible: function () {
|
|
||||||
return ($('#presentation>iframe') != null
|
|
||||||
&& $('#presentation>iframe').css('opacity') == 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the Prezi dialog, from which the user could choose a presentation
|
|
||||||
* to load.
|
|
||||||
*/
|
|
||||||
openPreziDialog: function() {
|
|
||||||
var myprezi = APP.xmpp.getPrezi();
|
|
||||||
if (myprezi) {
|
|
||||||
messageHandler.openTwoButtonDialog("dialog.removePreziTitle",
|
|
||||||
null,
|
|
||||||
"dialog.removePreziMsg",
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
"dialog.Remove",
|
|
||||||
function(e,v,m,f) {
|
|
||||||
if(v) {
|
|
||||||
APP.xmpp.removePreziFromPresence();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (preziPlayer != null) {
|
|
||||||
messageHandler.openTwoButtonDialog("dialog.sharePreziTitle",
|
|
||||||
null, "dialog.sharePreziMsg",
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
"dialog.Ok",
|
|
||||||
function(e,v,m,f) {
|
|
||||||
$.prompt.close();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var html = APP.translation.generateTranslationHTML(
|
|
||||||
"dialog.sharePreziTitle");
|
|
||||||
var cancelButton = APP.translation.generateTranslationHTML(
|
|
||||||
"dialog.Cancel");
|
|
||||||
var shareButton = APP.translation.generateTranslationHTML(
|
|
||||||
"dialog.Share");
|
|
||||||
var backButton = APP.translation.generateTranslationHTML(
|
|
||||||
"dialog.Back");
|
|
||||||
var buttons = [];
|
|
||||||
var buttons1 = [];
|
|
||||||
// Cancel button to both states
|
|
||||||
buttons.push({title: cancelButton, value: false});
|
|
||||||
buttons1.push({title: cancelButton, value: false});
|
|
||||||
// Share button
|
|
||||||
buttons.push({title: shareButton, value: true});
|
|
||||||
// Back button
|
|
||||||
buttons1.push({title: backButton, value: true});
|
|
||||||
var linkError = APP.translation.generateTranslationHTML(
|
|
||||||
"dialog.preziLinkError");
|
|
||||||
var defaultUrl = APP.translation.translateString("defaultPreziLink",
|
|
||||||
{url: "http://prezi.com/wz7vhjycl7e6/my-prezi"});
|
|
||||||
var openPreziState = {
|
|
||||||
state0: {
|
|
||||||
html: '<h2>' + html + '</h2>' +
|
|
||||||
'<input name="preziUrl" type="text" ' +
|
|
||||||
'data-i18n="[placeholder]defaultPreziLink" data-i18n-options=\'' +
|
|
||||||
JSON.stringify({"url": "http://prezi.com/wz7vhjycl7e6/my-prezi"}) +
|
|
||||||
'\' placeholder="' + defaultUrl + '" autofocus>',
|
|
||||||
persistent: false,
|
|
||||||
buttons: buttons,
|
|
||||||
focus: ':input:first',
|
|
||||||
defaultButton: 0,
|
|
||||||
submit: function (e, v, m, f) {
|
|
||||||
e.preventDefault();
|
|
||||||
if(v)
|
|
||||||
{
|
|
||||||
var preziUrl = f.preziUrl;
|
|
||||||
|
|
||||||
if (preziUrl)
|
|
||||||
{
|
|
||||||
var urlValue
|
|
||||||
= encodeURI(UIUtil.escapeHtml(preziUrl));
|
|
||||||
|
|
||||||
if (urlValue.indexOf('http://prezi.com/') != 0
|
|
||||||
&& urlValue.indexOf('https://prezi.com/') != 0)
|
|
||||||
{
|
|
||||||
$.prompt.goToState('state1');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var presIdTmp = urlValue.substring(
|
|
||||||
urlValue.indexOf("prezi.com/") + 10);
|
|
||||||
if (!isAlphanumeric(presIdTmp)
|
|
||||||
|| presIdTmp.indexOf('/') < 2) {
|
|
||||||
$.prompt.goToState('state1');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
APP.xmpp.addToPresence("prezi", urlValue);
|
|
||||||
$.prompt.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$.prompt.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
state1: {
|
|
||||||
html: '<h2>' + html + '</h2>' +
|
|
||||||
linkError,
|
|
||||||
persistent: false,
|
|
||||||
buttons: buttons1,
|
|
||||||
focus: ':input:first',
|
|
||||||
defaultButton: 1,
|
|
||||||
submit: function (e, v, m, f) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (v === 0)
|
|
||||||
$.prompt.close();
|
|
||||||
else
|
|
||||||
$.prompt.goToState('state0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
messageHandler.openDialogWithStates(openPreziState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new presentation has been added.
|
|
||||||
*
|
|
||||||
* @param event the event indicating the add of a presentation
|
|
||||||
* @param jid the jid from which the presentation was added
|
|
||||||
* @param presUrl url of the presentation
|
|
||||||
* @param currentSlide the current slide to which we should move
|
|
||||||
*/
|
|
||||||
function presentationAdded(event, jid, presUrl, currentSlide) {
|
|
||||||
console.log("presentation added", presUrl);
|
|
||||||
|
|
||||||
var presId = getPresentationId(presUrl);
|
|
||||||
|
|
||||||
var elementId = 'participant_'
|
|
||||||
+ Strophe.getResourceFromJid(jid)
|
|
||||||
+ '_' + presId;
|
|
||||||
|
|
||||||
VideoLayout.addPreziContainer(elementId);
|
|
||||||
|
|
||||||
var controlsEnabled = false;
|
|
||||||
if (jid === APP.xmpp.myJid())
|
|
||||||
controlsEnabled = true;
|
|
||||||
|
|
||||||
setPresentationVisible(true);
|
|
||||||
VideoLayout.setLargeVideoHover(
|
|
||||||
function (event) {
|
|
||||||
if (Prezi.isPresentationVisible()) {
|
|
||||||
var reloadButtonRight = window.innerWidth
|
|
||||||
- $('#presentation>iframe').offset().left
|
|
||||||
- $('#presentation>iframe').width();
|
|
||||||
|
|
||||||
$('#reloadPresentation').css({ right: reloadButtonRight,
|
|
||||||
display:'inline-block'});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (event) {
|
|
||||||
if (!Prezi.isPresentationVisible())
|
|
||||||
$('#reloadPresentation').css({display:'none'});
|
|
||||||
else {
|
|
||||||
var e = event.toElement || event.relatedTarget;
|
|
||||||
|
|
||||||
if (e && e.id != 'reloadPresentation' && e.id != 'header')
|
|
||||||
$('#reloadPresentation').css({display:'none'});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
preziPlayer = new PreziPlayer(
|
|
||||||
'presentation',
|
|
||||||
{preziId: presId,
|
|
||||||
width: getPresentationWidth(),
|
|
||||||
height: getPresentationHeihgt(),
|
|
||||||
controls: controlsEnabled,
|
|
||||||
debug: true
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#presentation>iframe').attr('id', preziPlayer.options.preziId);
|
|
||||||
|
|
||||||
preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
|
|
||||||
console.log("prezi status", event.value);
|
|
||||||
if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
|
|
||||||
if (jid != APP.xmpp.myJid())
|
|
||||||
preziPlayer.flyToStep(currentSlide);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
|
|
||||||
console.log("event value", event.value);
|
|
||||||
APP.xmpp.addToPresence("preziSlide", event.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#" + elementId).css( 'background-image',
|
|
||||||
'url(../images/avatarprezi.png)');
|
|
||||||
$("#" + elementId).click(
|
|
||||||
function () {
|
|
||||||
setPresentationVisible(true);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A presentation has been removed.
|
|
||||||
*
|
|
||||||
* @param event the event indicating the remove of a presentation
|
|
||||||
* @param jid the jid for which the presentation was removed
|
|
||||||
* @param the url of the presentation
|
|
||||||
*/
|
|
||||||
function presentationRemoved(event, jid, presUrl) {
|
|
||||||
console.log('presentation removed', presUrl);
|
|
||||||
var presId = getPresentationId(presUrl);
|
|
||||||
setPresentationVisible(false);
|
|
||||||
$('#participant_'
|
|
||||||
+ Strophe.getResourceFromJid(jid)
|
|
||||||
+ '_' + presId).remove();
|
|
||||||
$('#presentation>iframe').remove();
|
|
||||||
if (preziPlayer != null) {
|
|
||||||
preziPlayer.destroy();
|
|
||||||
preziPlayer = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the given string is an alphanumeric string.
|
* Indicates if the given string is an alphanumeric string.
|
||||||
|
@ -270,76 +23,355 @@ function presentationRemoved(event, jid, presUrl) {
|
||||||
* purpose of checking URIs.
|
* purpose of checking URIs.
|
||||||
*/
|
*/
|
||||||
function isAlphanumeric(unsafeText) {
|
function isAlphanumeric(unsafeText) {
|
||||||
var regex = /^[a-z0-9-_\/&\?=;]+$/i;
|
return alphanumRegex.test(unsafeText);
|
||||||
return regex.test(unsafeText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the presentation id from the given url.
|
* Returns the presentation id from the given url.
|
||||||
*/
|
*/
|
||||||
function getPresentationId (presUrl) {
|
function getPresentationId (url) {
|
||||||
var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
|
let presId = url.substring(url.indexOf("prezi.com/") + 10);
|
||||||
return presIdTmp.substring(0, presIdTmp.indexOf('/'));
|
return presId.substring(0, presId.indexOf('/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function isPreziLink(url) {
|
||||||
* Returns the presentation width.
|
if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) {
|
||||||
*/
|
return false;
|
||||||
function getPresentationWidth() {
|
|
||||||
var availableWidth = UIUtil.getAvailableVideoWidth();
|
|
||||||
var availableHeight = getPresentationHeihgt();
|
|
||||||
|
|
||||||
var aspectRatio = 16.0 / 9.0;
|
|
||||||
if (availableHeight < availableWidth / aspectRatio) {
|
|
||||||
availableWidth = Math.floor(availableHeight * aspectRatio);
|
|
||||||
}
|
|
||||||
return availableWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
let presId = url.substring(url.indexOf("prezi.com/") + 10);
|
||||||
* Returns the presentation height.
|
if (!isAlphanumeric(presId) || presId.indexOf('/') < 2) {
|
||||||
*/
|
return false;
|
||||||
function getPresentationHeihgt() {
|
|
||||||
var remoteVideos = $('#remoteVideos');
|
|
||||||
return window.innerHeight - remoteVideos.outerHeight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return true;
|
||||||
* Resizes the presentation iframe.
|
|
||||||
*/
|
|
||||||
function resize() {
|
|
||||||
if ($('#presentation>iframe')) {
|
|
||||||
$('#presentation>iframe').width(getPresentationWidth());
|
|
||||||
$('#presentation>iframe').height(getPresentationHeihgt());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function notifyOtherIsSharingPrezi() {
|
||||||
* Presentation has been removed.
|
messageHandler.openMessageDialog(
|
||||||
*/
|
"dialog.sharePreziTitle",
|
||||||
$(document).bind('presentationremoved.muc', presentationRemoved);
|
"dialog.sharePreziMsg"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
function proposeToClosePrezi() {
|
||||||
* Presentation has been added.
|
return new Promise(function (resolve, reject) {
|
||||||
*/
|
messageHandler.openTwoButtonDialog(
|
||||||
$(document).bind('presentationadded.muc', presentationAdded);
|
"dialog.removePreziTitle",
|
||||||
|
null,
|
||||||
|
"dialog.removePreziMsg",
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
"dialog.Remove",
|
||||||
|
function(e,v,m,f) {
|
||||||
|
if (v) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
});
|
||||||
* Indicates presentation slide change.
|
}
|
||||||
*/
|
|
||||||
$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
|
|
||||||
if (preziPlayer && preziPlayer.getCurrentStep() != current) {
|
|
||||||
preziPlayer.flyToStep(current);
|
|
||||||
|
|
||||||
var animationStepsArray = preziPlayer.getAnimationCountOnSteps();
|
function requestPreziLink() {
|
||||||
for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
|
const title = APP.translation.generateTranslationHTML("dialog.sharePreziTitle");
|
||||||
preziPlayer.flyToStep(current, i);
|
const cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
|
||||||
|
const shareButton = APP.translation.generateTranslationHTML("dialog.Share");
|
||||||
|
const backButton = APP.translation.generateTranslationHTML("dialog.Back");
|
||||||
|
const linkError = APP.translation.generateTranslationHTML("dialog.preziLinkError");
|
||||||
|
const i18nOptions = {url: defaultPreziLink};
|
||||||
|
const defaultUrl = APP.translation.translateString(
|
||||||
|
"defaultPreziLink", i18nOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let dialog = messageHandler.openDialogWithStates({
|
||||||
|
state0: {
|
||||||
|
html: `
|
||||||
|
<h2>${title}</h2>
|
||||||
|
<input name="preziUrl" type="text"
|
||||||
|
data-i18n="[placeholder]defaultPreziLink"
|
||||||
|
data-i18n-options="${JSON.stringify(i18nOptions)}"
|
||||||
|
placeholder="${defaultUrl}" autofocus>`,
|
||||||
|
persistent: false,
|
||||||
|
buttons: [
|
||||||
|
{title: cancelButton, value: false},
|
||||||
|
{title: shareButton, value: true}
|
||||||
|
],
|
||||||
|
focus: ':input:first',
|
||||||
|
defaultButton: 1,
|
||||||
|
submit: function (e, v, m, f) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!v) {
|
||||||
|
reject('cancelled');
|
||||||
|
dialog.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let preziUrl = f.preziUrl;
|
||||||
|
if (!preziUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let urlValue = encodeURI(UIUtil.escapeHtml(preziUrl));
|
||||||
|
|
||||||
|
if (!isPreziLink(urlValue)) {
|
||||||
|
dialog.goToState('state1');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(urlValue);
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
state1: {
|
||||||
|
html: `<h2>${title}</h2> ${linkError}`,
|
||||||
|
persistent: false,
|
||||||
|
buttons: [
|
||||||
|
{title: cancelButton, value: false},
|
||||||
|
{title: backButton, value: true}
|
||||||
|
],
|
||||||
|
focus: ':input:first',
|
||||||
|
defaultButton: 1,
|
||||||
|
submit: function (e, v, m, f) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (v === 0) {
|
||||||
|
reject();
|
||||||
|
dialog.close();
|
||||||
|
} else {
|
||||||
|
dialog.goToState('state0');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).resize(function () {
|
});
|
||||||
resize();
|
}
|
||||||
|
|
||||||
|
export const PreziContainerType = "prezi";
|
||||||
|
|
||||||
|
class PreziContainer extends LargeContainer {
|
||||||
|
|
||||||
|
constructor ({preziId, isMy, slide, onSlideChanged}) {
|
||||||
|
super();
|
||||||
|
this.reloadBtn = $('#reloadPresentation');
|
||||||
|
|
||||||
|
let preziPlayer = new PreziPlayer(
|
||||||
|
'presentation', {
|
||||||
|
preziId,
|
||||||
|
width: DEFAULT_WIDTH,
|
||||||
|
height: DEFAULT_HEIGHT,
|
||||||
|
controls: isMy,
|
||||||
|
debug: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.preziPlayer = preziPlayer;
|
||||||
|
this.$iframe = $(preziPlayer.iframe);
|
||||||
|
|
||||||
|
this.$iframe.attr('id', preziId);
|
||||||
|
|
||||||
|
preziPlayer.on(PreziPlayer.EVENT_STATUS, function({value}) {
|
||||||
|
console.log("prezi status", value);
|
||||||
|
if (value == PreziPlayer.STATUS_CONTENT_READY && !isMy) {
|
||||||
|
preziPlayer.flyToStep(slide);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = Prezi;
|
preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function({value}) {
|
||||||
|
console.log("event value", value);
|
||||||
|
onSlideChanged(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
goToSlide (slide) {
|
||||||
|
if (this.preziPlayer.getCurrentStep() === slide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.preziPlayer.flyToStep(slide);
|
||||||
|
|
||||||
|
let animationStepsArray = this.preziPlayer.getAnimationCountOnSteps();
|
||||||
|
if (!animationStepsArray) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < parseInt(animationStepsArray[slide]); i += 1) {
|
||||||
|
this.preziPlayer.flyToStep(slide, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showReloadBtn (show) {
|
||||||
|
this.reloadBtn.css('display', show ? 'inline-block' : 'none');
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.$iframe.fadeIn(300, () => {
|
||||||
|
this.$iframe.css({opacity: 1});
|
||||||
|
ToolbarToggler.dockToolbar(true);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.$iframe.fadeOut(300, () => {
|
||||||
|
this.$iframe.css({opacity: 0});
|
||||||
|
this.showReloadBtn(false);
|
||||||
|
ToolbarToggler.dockToolbar(false);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoverIn () {
|
||||||
|
let rightOffset = window.innerWidth - this.$iframe.offset().left - this.$iframe.width();
|
||||||
|
|
||||||
|
this.showReloadBtn(true);
|
||||||
|
this.reloadBtn.css('right', rightOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoverOut (event) {
|
||||||
|
let e = event.toElement || event.relatedTarget;
|
||||||
|
|
||||||
|
if (e && e.id != 'reloadPresentation' && e.id != 'header') {
|
||||||
|
this.showReloadBtn(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (containerWidth, containerHeight) {
|
||||||
|
let remoteVideos = $('#remoteVideos');
|
||||||
|
let height = containerHeight - remoteVideos.outerHeight();
|
||||||
|
|
||||||
|
let width = containerWidth;
|
||||||
|
|
||||||
|
if (height < width / aspectRatio) {
|
||||||
|
width = Math.floor(height * aspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$iframe.width(width).height(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
close () {
|
||||||
|
this.showReloadBtn(false);
|
||||||
|
this.preziPlayer.destroy();
|
||||||
|
this.$iframe.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PreziManager {
|
||||||
|
constructor (emitter) {
|
||||||
|
this.emitter = emitter;
|
||||||
|
|
||||||
|
this.userId = null;
|
||||||
|
this.url = null;
|
||||||
|
this.prezi = null;
|
||||||
|
|
||||||
|
$("#reloadPresentationLink").click(this.reloadPresentation.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
get isPresenting () {
|
||||||
|
return !!this.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isMyPrezi () {
|
||||||
|
return this.userId === APP.conference.localId;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSharing (id) {
|
||||||
|
return this.userId === id;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreziButtonClicked () {
|
||||||
|
if (!this.isPresenting) {
|
||||||
|
requestPreziLink().then(
|
||||||
|
url => this.emitter.emit(UIEvents.SHARE_PREZI, url, 0),
|
||||||
|
err => console.error('PREZI CANCELED', err)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isMyPrezi) {
|
||||||
|
proposeToClosePrezi().then(() => this.emitter.emit(UIEvents.STOP_SHARING_PREZI));
|
||||||
|
} else {
|
||||||
|
notifyOtherIsSharingPrezi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPresentation () {
|
||||||
|
if (!this.prezi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let iframe = this.prezi.$iframe[0];
|
||||||
|
iframe.src = iframe.src;
|
||||||
|
}
|
||||||
|
|
||||||
|
showPrezi (id, url, slide) {
|
||||||
|
if (!this.isPresenting) {
|
||||||
|
this.createPrezi(id, url, slide);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.userId === id && this.url === url) {
|
||||||
|
this.prezi.goToSlide(slide);
|
||||||
|
} else {
|
||||||
|
console.error(this.userId, id);
|
||||||
|
console.error(this.url, url);
|
||||||
|
throw new Error("unexpected presentation change");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPrezi (id, url, slide) {
|
||||||
|
console.log("presentation added", url);
|
||||||
|
|
||||||
|
this.userId = id;
|
||||||
|
this.url = url;
|
||||||
|
|
||||||
|
let preziId = getPresentationId(url);
|
||||||
|
let elementId = `participant_${id}_${preziId}`;
|
||||||
|
|
||||||
|
this.$thumb = $(VideoLayout.addRemoteVideoContainer(elementId));
|
||||||
|
VideoLayout.resizeThumbnails();
|
||||||
|
this.$thumb.css({
|
||||||
|
'background-image': 'url(../images/avatarprezi.png)'
|
||||||
|
}).click(() => VideoLayout.showLargeVideoContainer(PreziContainerType, true));
|
||||||
|
|
||||||
|
this.prezi = new PreziContainer({
|
||||||
|
preziId,
|
||||||
|
isMy: this.isMyPrezi,
|
||||||
|
slide,
|
||||||
|
onSlideChanged: newSlide => {
|
||||||
|
if (this.isMyPrezi) {
|
||||||
|
this.emitter.emit(UIEvents.SHARE_PREZI, url, newSlide);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
VideoLayout.addLargeVideoContainer(PreziContainerType, this.prezi);
|
||||||
|
VideoLayout.showLargeVideoContainer(PreziContainerType, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
removePrezi (id) {
|
||||||
|
if (this.userId !== id) {
|
||||||
|
throw new Error(`cannot close presentation from ${this.userId} instead of ${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$thumb.remove();
|
||||||
|
this.$thumb = null;
|
||||||
|
|
||||||
|
// wait until Prezi is hidden, then remove it
|
||||||
|
VideoLayout.showLargeVideoContainer(PreziContainerType, false).then(() => {
|
||||||
|
console.log("presentation removed", this.url);
|
||||||
|
|
||||||
|
VideoLayout.removeLargeVideoContainer(PreziContainerType);
|
||||||
|
|
||||||
|
this.userId = null;
|
||||||
|
this.url = null;
|
||||||
|
this.prezi.close();
|
||||||
|
this.prezi = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
/* global PreziPlayer */
|
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
(function() {
|
|
||||||
"use strict";
|
|
||||||
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
|
||||||
|
|
||||||
window.PreziPlayer = (function() {
|
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||||
|
|
||||||
PreziPlayer.API_VERSION = 1;
|
PreziPlayer.API_VERSION = 1;
|
||||||
PreziPlayer.CURRENT_STEP = 'currentStep';
|
PreziPlayer.CURRENT_STEP = 'currentStep';
|
||||||
|
@ -289,10 +285,6 @@
|
||||||
window.attachEvent('onmessage', PreziPlayer.messageReceived);
|
window.attachEvent('onmessage', PreziPlayer.messageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PreziPlayer;
|
window.PreziPlayer = PreziPlayer;
|
||||||
|
|
||||||
})();
|
export default PreziPlayer;
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
module.exports = PreziPlayer;
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import SettingsMenu from "./settings/SettingsMenu";
|
||||||
import VideoLayout from "../videolayout/VideoLayout";
|
import VideoLayout from "../videolayout/VideoLayout";
|
||||||
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
||||||
import UIUtil from "../util/UIUtil";
|
import UIUtil from "../util/UIUtil";
|
||||||
import LargeVideo from "../videolayout/LargeVideo";
|
|
||||||
|
|
||||||
const buttons = {
|
const buttons = {
|
||||||
'#chatspace': '#chatBottomButton',
|
'#chatspace': '#chatBottomButton',
|
||||||
|
@ -47,7 +46,7 @@ function toggle (object, selector, onOpenComplete, onOpen, onClose) {
|
||||||
} else {
|
} else {
|
||||||
// Undock the toolbar when the chat is shown and if we're in a
|
// Undock the toolbar when the chat is shown and if we're in a
|
||||||
// video mode.
|
// video mode.
|
||||||
if (LargeVideo.isLargeVideoVisible()) {
|
if (VideoLayout.isLargeVideoVisible()) {
|
||||||
ToolbarToggler.dockToolbar(false);
|
ToolbarToggler.dockToolbar(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ function toggle (object, selector, onOpenComplete, onOpen, onClose) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#toast-container").animate({
|
$("#toast-container").animate({
|
||||||
right: (PanelToggler.getPanelSize()[0] + 5)
|
right: (UIUtil.getSidePanelSize()[0] + 5)
|
||||||
}, {
|
}, {
|
||||||
queue: false,
|
queue: false,
|
||||||
duration: 500
|
duration: 500
|
||||||
|
@ -116,7 +115,7 @@ var PanelToggler = {
|
||||||
},
|
},
|
||||||
|
|
||||||
resizeChat () {
|
resizeChat () {
|
||||||
let [width, height] = this.getPanelSize();
|
let [width, height] = UIUtil.getSidePanelSize();
|
||||||
Chat.resizeChat(width, height);
|
Chat.resizeChat(width, height);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -156,21 +155,6 @@ var PanelToggler = {
|
||||||
null);
|
null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the side panel.
|
|
||||||
*/
|
|
||||||
getPanelSize () {
|
|
||||||
var availableHeight = window.innerHeight;
|
|
||||||
var availableWidth = window.innerWidth;
|
|
||||||
|
|
||||||
var panelWidth = 200;
|
|
||||||
if (availableWidth * 0.2 < 200) {
|
|
||||||
panelWidth = availableWidth * 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [panelWidth, availableHeight];
|
|
||||||
},
|
|
||||||
|
|
||||||
isVisible () {
|
isVisible () {
|
||||||
return (Chat.isVisible() ||
|
return (Chat.isVisible() ||
|
||||||
ContactList.isVisible() ||
|
ContactList.isVisible() ||
|
||||||
|
|
|
@ -9,13 +9,6 @@ const defaultBottomToolbarButtons = {
|
||||||
'filmstrip': '#bottom_toolbar_film_strip'
|
'filmstrip': '#bottom_toolbar_film_strip'
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).bind("remotevideo.resized", function (event, width, height) {
|
|
||||||
let toolbar = $('#bottomToolbar');
|
|
||||||
let bottom = (height - toolbar.outerHeight())/2 + 18;
|
|
||||||
|
|
||||||
toolbar.css({bottom});
|
|
||||||
});
|
|
||||||
|
|
||||||
const BottomToolbar = {
|
const BottomToolbar = {
|
||||||
init (emitter) {
|
init (emitter) {
|
||||||
UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
|
UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
|
||||||
|
@ -42,6 +35,13 @@ const BottomToolbar = {
|
||||||
|
|
||||||
toggleFilmStrip () {
|
toggleFilmStrip () {
|
||||||
$("#remoteVideos").toggleClass("hidden");
|
$("#remoteVideos").toggleClass("hidden");
|
||||||
|
},
|
||||||
|
|
||||||
|
onRemoteVideoResized (width, height) {
|
||||||
|
let toolbar = $('#bottomToolbar');
|
||||||
|
let bottom = (height - toolbar.outerHeight())/2 + 18;
|
||||||
|
|
||||||
|
toolbar.css({bottom});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -250,7 +250,7 @@ const Toolbar = {
|
||||||
* Disables and enables some of the buttons.
|
* Disables and enables some of the buttons.
|
||||||
*/
|
*/
|
||||||
setupButtonsFromConfig () {
|
setupButtonsFromConfig () {
|
||||||
if (UIUtil.isButtonEnabled('prezi')) {
|
if (!UIUtil.isButtonEnabled('prezi')) {
|
||||||
$("#toolbar_button_prezi").css({display: "none"});
|
$("#toolbar_button_prezi").css({display: "none"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -283,6 +283,12 @@ const Toolbar = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showEtherpadButton () {
|
||||||
|
if (!$('#toolbar_button_etherpad').is(":visible")) {
|
||||||
|
$('#toolbar_button_etherpad').css({display: 'inline-block'});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Shows or hides the 'recording' button.
|
// Shows or hides the 'recording' button.
|
||||||
showRecordingButton (show) {
|
showRecordingButton (show) {
|
||||||
if (UIUtil.isButtonEnabled('recording') && show) {
|
if (UIUtil.isButtonEnabled('recording') && show) {
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
/* global $, config, interfaceConfig */
|
/* global $, config, interfaceConfig */
|
||||||
|
|
||||||
import PanelToggler from "../side_pannels/SidePanelToggler";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hristo on 12/22/14.
|
* Created by hristo on 12/22/14.
|
||||||
*/
|
*/
|
||||||
var UIUtil = {
|
var UIUtil = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the side panel.
|
||||||
|
*/
|
||||||
|
getSidePanelSize () {
|
||||||
|
var availableHeight = window.innerHeight;
|
||||||
|
var availableWidth = window.innerWidth;
|
||||||
|
|
||||||
|
var panelWidth = 200;
|
||||||
|
if (availableWidth * 0.2 < 200) {
|
||||||
|
panelWidth = availableWidth * 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [panelWidth, availableHeight];
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the available video width.
|
* Returns the available video width.
|
||||||
*/
|
*/
|
||||||
getAvailableVideoWidth: function (isVisible) {
|
getAvailableVideoWidth: function (isSidePanelVisible) {
|
||||||
if(typeof isVisible === "undefined" || isVisible === null)
|
let rightPanelWidth = 0;
|
||||||
isVisible = PanelToggler.isVisible();
|
|
||||||
var rightPanelWidth
|
if (isSidePanelVisible) {
|
||||||
= isVisible ? PanelToggler.getPanelSize()[0] : 0;
|
rightPanelWidth = UIUtil.getSidePanelSize()[0];
|
||||||
|
}
|
||||||
|
|
||||||
return window.innerWidth - rightPanelWidth;
|
return window.innerWidth - rightPanelWidth;
|
||||||
},
|
},
|
||||||
|
@ -118,6 +133,12 @@ import PanelToggler from "../side_pannels/SidePanelToggler";
|
||||||
|
|
||||||
redirect (url) {
|
redirect (url) {
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
isFullScreen () {
|
||||||
|
return document.fullScreen
|
||||||
|
|| document.mozFullScreen
|
||||||
|
|| document.webkitIsFullScreen;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
export default class LargeContainer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
show () {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
hide () {
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (containerWidth, containerHeight, animate) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoverIn (e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onHoverOut (e) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,98 +1,20 @@
|
||||||
/* global $, APP, interfaceConfig */
|
/* global $, APP, interfaceConfig */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
import Avatar from "../avatar/Avatar";
|
|
||||||
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
|
||||||
import UIUtil from "../util/UIUtil";
|
import UIUtil from "../util/UIUtil";
|
||||||
import UIEvents from "../../../service/UI/UIEvents";
|
import UIEvents from "../../../service/UI/UIEvents";
|
||||||
|
import LargeContainer from './LargeContainer';
|
||||||
|
|
||||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
const RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||||
|
|
||||||
// FIXME: With Temasys we have to re-select everytime
|
const avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
|
||||||
//var video = $('#largeVideo');
|
|
||||||
|
|
||||||
var currentVideoWidth = null;
|
function getStreamId(stream) {
|
||||||
var currentVideoHeight = null;
|
if (stream.isLocal()) {
|
||||||
// By default we use camera
|
return APP.conference.localId;
|
||||||
var getVideoSize = getCameraVideoSize;
|
|
||||||
var getVideoPosition = getCameraVideoPosition;
|
|
||||||
/**
|
|
||||||
* The small video instance that is displayed in the large video
|
|
||||||
* @type {SmallVideo}
|
|
||||||
*/
|
|
||||||
var currentSmallVideo = null;
|
|
||||||
/**
|
|
||||||
* Indicates whether the large video is enabled.
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
var isEnabled = true;
|
|
||||||
/**
|
|
||||||
* Current large video state.
|
|
||||||
* Possible values - video, prezi or etherpad.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
var state = "video";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the html element associated with the passed state of large video
|
|
||||||
* @param state the state.
|
|
||||||
* @returns {JQuery|*|jQuery|HTMLElement} the container.
|
|
||||||
*/
|
|
||||||
function getContainerByState(state) {
|
|
||||||
var selector = null;
|
|
||||||
switch (state) {
|
|
||||||
case "video":
|
|
||||||
selector = "#largeVideoWrapper";
|
|
||||||
break;
|
|
||||||
case "etherpad":
|
|
||||||
selector = "#etherpad>iframe";
|
|
||||||
break;
|
|
||||||
case "prezi":
|
|
||||||
selector = "#presentation>iframe";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return $(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the size and position of the given video element.
|
|
||||||
*
|
|
||||||
* @param video the video element to position
|
|
||||||
* @param width the desired video width
|
|
||||||
* @param height the desired video height
|
|
||||||
* @param horizontalIndent the left and right indent
|
|
||||||
* @param verticalIndent the top and bottom indent
|
|
||||||
*/
|
|
||||||
function positionVideo(video,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
horizontalIndent,
|
|
||||||
verticalIndent,
|
|
||||||
animate) {
|
|
||||||
if (animate) {
|
|
||||||
video.animate({
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
top: verticalIndent,
|
|
||||||
bottom: verticalIndent,
|
|
||||||
left: horizontalIndent,
|
|
||||||
right: horizontalIndent
|
|
||||||
}, {
|
|
||||||
queue: false,
|
|
||||||
duration: 500
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
video.width(width);
|
return stream.getParticipantId();
|
||||||
video.height(height);
|
|
||||||
video.css({
|
|
||||||
top: verticalIndent,
|
|
||||||
bottom: verticalIndent,
|
|
||||||
left: horizontalIndent,
|
|
||||||
right: horizontalIndent
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,80 +28,28 @@ function getDesktopVideoSize(videoWidth,
|
||||||
videoHeight,
|
videoHeight,
|
||||||
videoSpaceWidth,
|
videoSpaceWidth,
|
||||||
videoSpaceHeight) {
|
videoSpaceHeight) {
|
||||||
if (!videoWidth)
|
|
||||||
videoWidth = currentVideoWidth;
|
|
||||||
if (!videoHeight)
|
|
||||||
videoHeight = currentVideoHeight;
|
|
||||||
|
|
||||||
var aspectRatio = videoWidth / videoHeight;
|
let aspectRatio = videoWidth / videoHeight;
|
||||||
|
|
||||||
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
let availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
||||||
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
let availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
||||||
|
|
||||||
var filmstrip = $("#remoteVideos");
|
let filmstrip = $("#remoteVideos");
|
||||||
|
|
||||||
if (!filmstrip.hasClass("hidden"))
|
if (!filmstrip.hasClass("hidden"))
|
||||||
videoSpaceHeight -= filmstrip.outerHeight();
|
videoSpaceHeight -= filmstrip.outerHeight();
|
||||||
|
|
||||||
if (availableWidth / aspectRatio >= videoSpaceHeight)
|
if (availableWidth / aspectRatio >= videoSpaceHeight) {
|
||||||
{
|
|
||||||
availableHeight = videoSpaceHeight;
|
availableHeight = videoSpaceHeight;
|
||||||
availableWidth = availableHeight * aspectRatio;
|
availableWidth = availableHeight * aspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (availableHeight * aspectRatio >= videoSpaceWidth)
|
if (availableHeight * aspectRatio >= videoSpaceWidth) {
|
||||||
{
|
|
||||||
availableWidth = videoSpaceWidth;
|
availableWidth = videoSpaceWidth;
|
||||||
availableHeight = availableWidth / aspectRatio;
|
availableHeight = availableWidth / aspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [availableWidth, availableHeight];
|
return { availableWidth, availableHeight };
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of the video horizontal and vertical indents,
|
|
||||||
* so that if fits its parent.
|
|
||||||
*
|
|
||||||
* @return an array with 2 elements, the horizontal indent and the vertical
|
|
||||||
* indent
|
|
||||||
*/
|
|
||||||
function getCameraVideoPosition(videoWidth,
|
|
||||||
videoHeight,
|
|
||||||
videoSpaceWidth,
|
|
||||||
videoSpaceHeight) {
|
|
||||||
// Parent height isn't completely calculated when we position the video in
|
|
||||||
// full screen mode and this is why we use the screen height in this case.
|
|
||||||
// Need to think it further at some point and implement it properly.
|
|
||||||
var isFullScreen = document.fullScreen ||
|
|
||||||
document.mozFullScreen ||
|
|
||||||
document.webkitIsFullScreen;
|
|
||||||
if (isFullScreen)
|
|
||||||
videoSpaceHeight = window.innerHeight;
|
|
||||||
|
|
||||||
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
|
||||||
var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
|
|
||||||
|
|
||||||
return [horizontalIndent, verticalIndent];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of the video horizontal and vertical indents.
|
|
||||||
* Centers horizontally and top aligns vertically.
|
|
||||||
*
|
|
||||||
* @return an array with 2 elements, the horizontal indent and the vertical
|
|
||||||
* indent
|
|
||||||
*/
|
|
||||||
function getDesktopVideoPosition(videoWidth,
|
|
||||||
videoHeight,
|
|
||||||
videoSpaceWidth,
|
|
||||||
videoSpaceHeight) {
|
|
||||||
|
|
||||||
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
|
||||||
|
|
||||||
var verticalIndent = 0;// Top aligned
|
|
||||||
|
|
||||||
return [horizontalIndent, verticalIndent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,15 +69,10 @@ function getCameraVideoSize(videoWidth,
|
||||||
videoSpaceWidth,
|
videoSpaceWidth,
|
||||||
videoSpaceHeight) {
|
videoSpaceHeight) {
|
||||||
|
|
||||||
if (!videoWidth)
|
let aspectRatio = videoWidth / videoHeight;
|
||||||
videoWidth = currentVideoWidth;
|
|
||||||
if (!videoHeight)
|
|
||||||
videoHeight = currentVideoHeight;
|
|
||||||
|
|
||||||
var aspectRatio = videoWidth / videoHeight;
|
let availableWidth = videoWidth;
|
||||||
|
let availableHeight = videoHeight;
|
||||||
var availableWidth = videoWidth;
|
|
||||||
var availableHeight = videoHeight;
|
|
||||||
|
|
||||||
if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
|
if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
|
||||||
availableHeight = videoSpaceHeight;
|
availableHeight = videoSpaceHeight;
|
||||||
|
@ -233,487 +98,341 @@ function getCameraVideoSize(videoWidth,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return [availableWidth, availableHeight];
|
return { availableWidth, availableHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the src of the active speaker avatar
|
* Returns an array of the video horizontal and vertical indents,
|
||||||
|
* so that if fits its parent.
|
||||||
|
*
|
||||||
|
* @return an array with 2 elements, the horizontal indent and the vertical
|
||||||
|
* indent
|
||||||
*/
|
*/
|
||||||
function updateActiveSpeakerAvatarSrc() {
|
function getCameraVideoPosition(videoWidth,
|
||||||
let avatar = $("#activeSpeakerAvatar");
|
videoHeight,
|
||||||
let id = currentSmallVideo.id;
|
videoSpaceWidth,
|
||||||
let url = Avatar.getActiveSpeakerUrl(id);
|
videoSpaceHeight) {
|
||||||
if (id && avatar.attr('src') !== url) {
|
// Parent height isn't completely calculated when we position the video in
|
||||||
avatar.attr('src', url);
|
// full screen mode and this is why we use the screen height in this case.
|
||||||
currentSmallVideo.showAvatar();
|
// Need to think it further at some point and implement it properly.
|
||||||
|
if (UIUtil.isFullScreen()) {
|
||||||
|
videoSpaceHeight = window.innerHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
||||||
|
let verticalIndent = (videoSpaceHeight - videoHeight) / 2;
|
||||||
|
|
||||||
|
return { horizontalIndent, verticalIndent };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the video source of the large video.
|
* Returns an array of the video horizontal and vertical indents.
|
||||||
* @param isVisible
|
* Centers horizontally and top aligns vertically.
|
||||||
|
*
|
||||||
|
* @return an array with 2 elements, the horizontal indent and the vertical
|
||||||
|
* indent
|
||||||
*/
|
*/
|
||||||
function changeVideo(isVisible) {
|
function getDesktopVideoPosition(videoWidth,
|
||||||
|
videoHeight,
|
||||||
|
videoSpaceWidth,
|
||||||
|
videoSpaceHeight) {
|
||||||
|
|
||||||
if (!currentSmallVideo) {
|
let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
||||||
console.error("Unable to change large video - no 'currentSmallVideo'");
|
|
||||||
return;
|
let verticalIndent = 0;// Top aligned
|
||||||
|
|
||||||
|
return { horizontalIndent, verticalIndent };
|
||||||
}
|
}
|
||||||
|
|
||||||
updateActiveSpeakerAvatarSrc();
|
export const VideoContainerType = "video";
|
||||||
let largeVideoElement = $('#largeVideo');
|
|
||||||
|
|
||||||
currentSmallVideo.stream.attach(largeVideoElement);
|
class VideoContainer extends LargeContainer {
|
||||||
|
// FIXME: With Temasys we have to re-select everytime
|
||||||
|
get $video () {
|
||||||
|
return $('#largeVideo');
|
||||||
|
}
|
||||||
|
|
||||||
let flipX = currentSmallVideo.flipX;
|
get id () {
|
||||||
|
if (this.stream) {
|
||||||
largeVideoElement.css({
|
return getStreamId(this.stream);
|
||||||
transform: flipX ? "scaleX(-1)" : "none"
|
|
||||||
});
|
|
||||||
|
|
||||||
LargeVideo.updateVideoSizeAndPosition(currentSmallVideo.getVideoType());
|
|
||||||
|
|
||||||
// Only if the large video is currently visible.
|
|
||||||
if (isVisible) {
|
|
||||||
LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo);
|
|
||||||
|
|
||||||
$('#largeVideoWrapper').fadeTo(300, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
constructor (onPlay) {
|
||||||
* Creates the html elements for the large video.
|
super();
|
||||||
*/
|
this.stream = null;
|
||||||
function createLargeVideoHTML()
|
|
||||||
{
|
|
||||||
var html = '<div id="largeVideoContainer" class="videocontainer">';
|
|
||||||
html += '<div id="presentation"></div>' +
|
|
||||||
'<div id="etherpad"></div>' +
|
|
||||||
'<a target="_new"><div class="watermark leftwatermark"></div></a>' +
|
|
||||||
'<a target="_new"><div class="watermark rightwatermark"></div></a>' +
|
|
||||||
'<a class="poweredby" href="http://jitsi.org" target="_new" >' +
|
|
||||||
'<span data-i18n="poweredby"></span> jitsi.org' +
|
|
||||||
'</a>'+
|
|
||||||
'<div id="activeSpeaker">' +
|
|
||||||
'<img id="activeSpeakerAvatar" src=""/>' +
|
|
||||||
'<canvas id="activeSpeakerAudioLevel"></canvas>' +
|
|
||||||
'</div>' +
|
|
||||||
'<div id="largeVideoWrapper">' +
|
|
||||||
'<video id="largeVideo" muted="true"' +
|
|
||||||
'autoplay oncontextmenu="return false;"></video>' +
|
|
||||||
'</div id="largeVideoWrapper">' +
|
|
||||||
'<span id="videoConnectionMessage"></span>';
|
|
||||||
html += '</div>';
|
|
||||||
$(html).prependTo("#videospace");
|
|
||||||
|
|
||||||
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
|
this.$avatar = $('#activeSpeaker');
|
||||||
var leftWatermarkDiv
|
this.$wrapper = $('#largeVideoWrapper');
|
||||||
= $("#largeVideoContainer div[class='watermark leftwatermark']");
|
|
||||||
|
|
||||||
leftWatermarkDiv.css({display: 'block'});
|
|
||||||
leftWatermarkDiv.parent().get(0).href
|
|
||||||
= interfaceConfig.JITSI_WATERMARK_LINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interfaceConfig.SHOW_BRAND_WATERMARK) {
|
|
||||||
var rightWatermarkDiv
|
|
||||||
= $("#largeVideoContainer div[class='watermark rightwatermark']");
|
|
||||||
|
|
||||||
rightWatermarkDiv.css({display: 'block'});
|
|
||||||
rightWatermarkDiv.parent().get(0).href
|
|
||||||
= interfaceConfig.BRAND_WATERMARK_LINK;
|
|
||||||
rightWatermarkDiv.get(0).style.backgroundImage
|
|
||||||
= "url(images/rightwatermark.png)";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interfaceConfig.SHOW_POWERED_BY) {
|
|
||||||
$("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!RTCBrowserType.isIExplorer()) {
|
if (!RTCBrowserType.isIExplorer()) {
|
||||||
$('#largeVideo').volume = 0;
|
this.$video.volume = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var LargeVideo = {
|
this.$video.on('play', onPlay);
|
||||||
|
}
|
||||||
|
|
||||||
init: function (VideoLayout, emitter) {
|
getStreamSize () {
|
||||||
if(!isEnabled)
|
let video = this.$video[0];
|
||||||
return;
|
return {
|
||||||
createLargeVideoHTML();
|
width: video.videoWidth,
|
||||||
|
height: video.videoHeight
|
||||||
this.VideoLayout = VideoLayout;
|
|
||||||
this.eventEmitter = emitter;
|
|
||||||
this.eventEmitter.emit(UIEvents.LARGEVIDEO_INIT);
|
|
||||||
var self = this;
|
|
||||||
// Listen for large video size updates
|
|
||||||
var largeVideo = $('#largeVideo')[0];
|
|
||||||
var onplaying = function (arg1, arg2, arg3) {
|
|
||||||
// re-select
|
|
||||||
if (RTCBrowserType.isTemasysPluginUsed())
|
|
||||||
largeVideo = $('#largeVideo')[0];
|
|
||||||
currentVideoWidth = largeVideo.videoWidth;
|
|
||||||
currentVideoHeight = largeVideo.videoHeight;
|
|
||||||
self.position(currentVideoWidth, currentVideoHeight);
|
|
||||||
};
|
};
|
||||||
largeVideo.onplaying = onplaying;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Indicates if the large video is currently visible.
|
|
||||||
*
|
|
||||||
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
|
|
||||||
*/
|
|
||||||
isLargeVideoVisible: function() {
|
|
||||||
return $('#largeVideoWrapper').is(':visible');
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns <tt>true</tt> if the user is currently displayed on large video.
|
|
||||||
*/
|
|
||||||
isCurrentlyOnLarge: function (id) {
|
|
||||||
return id && id === this.getId();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the large video with the given new video source.
|
|
||||||
*/
|
|
||||||
updateLargeVideo: function (id, forceUpdate) {
|
|
||||||
if(!isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let newSmallVideo = this.VideoLayout.getSmallVideo(id);
|
|
||||||
console.info(`hover in ${id} , video: `, newSmallVideo);
|
|
||||||
|
|
||||||
if (!newSmallVideo) {
|
|
||||||
console.error("Small video not found for: " + id);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LargeVideo.isCurrentlyOnLarge(id) || forceUpdate) {
|
getVideoSize (containerWidth, containerHeight) {
|
||||||
$('#activeSpeaker').css('visibility', 'hidden');
|
let { width, height } = this.getStreamSize();
|
||||||
|
if (this.stream && this.stream.isScreenSharing()) {
|
||||||
let oldId = this.getId();
|
return getDesktopVideoSize(width, height, containerWidth, containerHeight);
|
||||||
|
} else {
|
||||||
currentSmallVideo = newSmallVideo;
|
return getCameraVideoSize(width, height, containerWidth, containerHeight);
|
||||||
|
|
||||||
if (oldId !== id) {
|
|
||||||
// we want the notification to trigger even if id is undefined,
|
|
||||||
// or null.
|
|
||||||
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getVideoPosition (width, height, containerWidth, containerHeight) {
|
||||||
|
if (this.stream && this.stream.isScreenSharing()) {
|
||||||
|
return getDesktopVideoPosition(width, height, containerWidth, containerHeight);
|
||||||
|
} else {
|
||||||
|
return getCameraVideoPosition(width, height, containerWidth, containerHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resize (containerWidth, containerHeight, animate = false) {
|
||||||
|
let { width, height } = this.getVideoSize(containerWidth, containerHeight);
|
||||||
|
let { horizontalIndent, verticalIndent } = this.getVideoPosition(width, height, containerWidth, containerHeight);
|
||||||
|
|
||||||
|
// update avatar position
|
||||||
|
let top = this.containerHeight / 2 - avatarSize / 4 * 3;
|
||||||
|
this.$avatar.css('top', top);
|
||||||
|
|
||||||
|
this.$wrapper.animate({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
|
||||||
|
top: verticalIndent,
|
||||||
|
bottom: verticalIndent,
|
||||||
|
|
||||||
|
left: horizontalIndent,
|
||||||
|
right: horizontalIndent
|
||||||
|
}, {
|
||||||
|
queue: false,
|
||||||
|
duration: animate ? 500 : 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setStream (stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
|
||||||
|
stream.attach(this.$video);
|
||||||
|
|
||||||
|
let flipX = stream.isLocal() && !stream.isScreenSharing();
|
||||||
|
this.$video.css({
|
||||||
|
transform: flipX ? 'scaleX(-1)' : 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showAvatar (show) {
|
||||||
|
this.$avatar.css("visibility", show ? "visible" : "hidden");
|
||||||
|
}
|
||||||
|
|
||||||
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
||||||
// largeVideo, because when Temasys plugin is in use it replaces
|
// largeVideo, because when Temasys plugin is in use it replaces
|
||||||
// <video> elements with plugin <object> tag. In Safari jQuery is
|
// <video> elements with plugin <object> tag. In Safari jQuery is
|
||||||
// unable to store values on this plugin object which breaks all
|
// unable to store values on this plugin object which breaks all
|
||||||
// animation effects performed on it directly.
|
// animation effects performed on it directly.
|
||||||
//
|
|
||||||
// If for any reason large video was hidden before calling fadeOut
|
|
||||||
// changeVideo will never be called, so we call show() in chain just
|
|
||||||
// to be sure
|
|
||||||
$('#largeVideoWrapper').show().fadeTo(300, 0,
|
|
||||||
changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
|
|
||||||
} else {
|
|
||||||
if (currentSmallVideo) {
|
|
||||||
currentSmallVideo.showAvatar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
show () {
|
||||||
|
let $wrapper = this.$wrapper;
|
||||||
/**
|
return new Promise(resolve => {
|
||||||
* Shows/hides the large video.
|
$wrapper.fadeIn(300, function () {
|
||||||
*/
|
$wrapper.css({visibility: 'visible'});
|
||||||
setLargeVideoVisible: function(isVisible) {
|
|
||||||
if(!isEnabled)
|
|
||||||
return;
|
|
||||||
if (isVisible) {
|
|
||||||
$('#largeVideoWrapper').css({visibility: 'visible'});
|
|
||||||
$('.watermark').css({visibility: 'visible'});
|
$('.watermark').css({visibility: 'visible'});
|
||||||
if(currentSmallVideo)
|
});
|
||||||
currentSmallVideo.enableDominantSpeaker(true);
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$('#largeVideoWrapper').css({visibility: 'hidden'});
|
hide () {
|
||||||
$('#activeSpeaker').css('visibility', 'hidden');
|
this.showAvatar(false);
|
||||||
|
|
||||||
|
let $wrapper = this.$wrapper;
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
$wrapper.fadeOut(300, function () {
|
||||||
|
$wrapper.css({visibility: 'hidden'});
|
||||||
$('.watermark').css({visibility: 'hidden'});
|
$('.watermark').css({visibility: 'hidden'});
|
||||||
if(currentSmallVideo)
|
resolve();
|
||||||
currentSmallVideo.enableDominantSpeaker(false);
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onVideoTypeChanged: function (id, newVideoType) {
|
|
||||||
if (!isEnabled)
|
|
||||||
return;
|
|
||||||
if (LargeVideo.isCurrentlyOnLarge(id)) {
|
|
||||||
LargeVideo.updateVideoSizeAndPosition(newVideoType);
|
|
||||||
|
|
||||||
this.position(null, null, null, null, true);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Positions the large video.
|
export default class LargeVideoManager {
|
||||||
*
|
constructor () {
|
||||||
* @param videoWidth the stream video width
|
this.containers = {};
|
||||||
* @param videoHeight the stream video height
|
|
||||||
*/
|
this.state = VideoContainerType;
|
||||||
position: function (videoWidth, videoHeight,
|
this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
|
||||||
videoSpaceWidth, videoSpaceHeight, animate) {
|
this.addContainer(VideoContainerType, this.videoContainer);
|
||||||
if(!isEnabled)
|
|
||||||
|
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;
|
return;
|
||||||
if(!videoSpaceWidth)
|
}
|
||||||
videoSpaceWidth = $('#videospace').width();
|
let container = this.getContainer(this.state);
|
||||||
if(!videoSpaceHeight)
|
container.onHoverIn(e);
|
||||||
videoSpaceHeight = window.innerHeight;
|
}
|
||||||
|
|
||||||
var videoSize = getVideoSize(videoWidth,
|
onHoverOut (e) {
|
||||||
videoHeight,
|
if (!this.state) {
|
||||||
videoSpaceWidth,
|
|
||||||
videoSpaceHeight);
|
|
||||||
|
|
||||||
var largeVideoWidth = videoSize[0];
|
|
||||||
var largeVideoHeight = videoSize[1];
|
|
||||||
|
|
||||||
var videoPosition = getVideoPosition(largeVideoWidth,
|
|
||||||
largeVideoHeight,
|
|
||||||
videoSpaceWidth,
|
|
||||||
videoSpaceHeight);
|
|
||||||
|
|
||||||
var horizontalIndent = videoPosition[0];
|
|
||||||
var verticalIndent = videoPosition[1];
|
|
||||||
|
|
||||||
positionVideo($('#largeVideoWrapper'),
|
|
||||||
largeVideoWidth,
|
|
||||||
largeVideoHeight,
|
|
||||||
horizontalIndent, verticalIndent, animate);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Resizes the large html elements.
|
|
||||||
*
|
|
||||||
* @param animate boolean property that indicates whether the resize should
|
|
||||||
* be animated or not.
|
|
||||||
* @param isSideBarVisible boolean property that indicates whether the chat
|
|
||||||
* area is displayed or not.
|
|
||||||
* If that parameter is null the method will check the chat panel
|
|
||||||
* visibility.
|
|
||||||
* @param completeFunction a function to be called when the video space is
|
|
||||||
* resized
|
|
||||||
* @returns {*[]} array with the current width and height values of the
|
|
||||||
* largeVideo html element.
|
|
||||||
*/
|
|
||||||
resize: function (animate, isSideBarVisible, completeFunction) {
|
|
||||||
if(!isEnabled)
|
|
||||||
return;
|
return;
|
||||||
var availableHeight = window.innerHeight;
|
}
|
||||||
var availableWidth = UIUtil.getAvailableVideoWidth(isSideBarVisible);
|
let container = this.getContainer(this.state);
|
||||||
|
container.onHoverOut(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (availableWidth < 0 || availableHeight < 0) return;
|
get id () {
|
||||||
|
return this.videoContainer.id;
|
||||||
|
}
|
||||||
|
|
||||||
var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
|
updateLargeVideo (stream) {
|
||||||
var top = availableHeight / 2 - avatarSize / 4 * 3;
|
let id = getStreamId(stream);
|
||||||
$('#activeSpeaker').css('top', top);
|
|
||||||
|
|
||||||
this.VideoLayout
|
let container = this.getContainer(this.state);
|
||||||
.resizeVideoSpace(animate, isSideBarVisible, completeFunction);
|
|
||||||
if(animate) {
|
container.hide().then(() => {
|
||||||
$('#largeVideoContainer').animate({
|
console.info("hover in %s", id);
|
||||||
width: availableWidth,
|
this.state = VideoContainerType;
|
||||||
height: availableHeight
|
this.videoContainer.setStream(stream);
|
||||||
},
|
this.videoContainer.show();
|
||||||
{
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContainerSize (isSideBarVisible) {
|
||||||
|
this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
|
||||||
|
this.height = window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeContainer (type, animate = false) {
|
||||||
|
let container = this.getContainer(type);
|
||||||
|
container.resize(this.width, this.height, animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
queue: false,
|
||||||
duration: 500
|
duration: animate ? 500 : 0
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
$('#largeVideoContainer').width(availableWidth);
|
|
||||||
$('#largeVideoContainer').height(availableHeight);
|
|
||||||
}
|
}
|
||||||
return [availableWidth, availableHeight];
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Resizes the large video.
|
|
||||||
*
|
|
||||||
* @param isSideBarVisible indicating if the side bar is visible
|
|
||||||
* @param completeFunction the callback function to be executed after the
|
|
||||||
* resize
|
|
||||||
*/
|
|
||||||
resizeVideoAreaAnimated: function (isSideBarVisible, completeFunction) {
|
|
||||||
if(!isEnabled)
|
|
||||||
return;
|
|
||||||
var size = this.resize(true, isSideBarVisible, completeFunction);
|
|
||||||
this.position(null, null, size[0], size[1], true);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the video size and position.
|
|
||||||
*
|
|
||||||
* @param videoType the video type indicating if the stream is of type
|
|
||||||
* desktop or web cam
|
|
||||||
*/
|
|
||||||
updateVideoSizeAndPosition: function (videoType) {
|
|
||||||
if (!videoType)
|
|
||||||
videoType = currentSmallVideo.getVideoType();
|
|
||||||
|
|
||||||
var isDesktop = videoType === 'desktop';
|
|
||||||
|
|
||||||
// Change the way we'll be measuring and positioning large video
|
|
||||||
getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize;
|
|
||||||
getVideoPosition = isDesktop ? getDesktopVideoPosition :
|
|
||||||
getCameraVideoPosition;
|
|
||||||
},
|
|
||||||
getId: function () {
|
|
||||||
return currentSmallVideo ? currentSmallVideo.id : null;
|
|
||||||
},
|
|
||||||
updateAvatar: function (id) {
|
|
||||||
if (!isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (id === this.getId()) {
|
|
||||||
updateActiveSpeakerAvatarSrc();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showAvatar: function (id, show) {
|
|
||||||
if (!isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.getId() === id && state === "video") {
|
|
||||||
$("#largeVideoWrapper").css("visibility", show ? "hidden" : "visible");
|
|
||||||
$('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Disables the large video
|
|
||||||
*/
|
|
||||||
disable: function () {
|
|
||||||
isEnabled = false;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Enables the large video
|
|
||||||
*/
|
|
||||||
enable: function () {
|
|
||||||
isEnabled = true;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns true if the video is enabled.
|
|
||||||
*/
|
|
||||||
isEnabled: function () {
|
|
||||||
return isEnabled;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Creates the iframe used by the etherpad
|
|
||||||
* @param src the value for src attribute
|
|
||||||
* @param onloadHandler handler executed when the iframe loads it content
|
|
||||||
* @returns {HTMLElement} the iframe
|
|
||||||
*/
|
|
||||||
createEtherpadIframe: function (src, onloadHandler) {
|
|
||||||
if(!isEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var etherpadIFrame = document.createElement('iframe');
|
|
||||||
etherpadIFrame.src = src;
|
|
||||||
etherpadIFrame.frameBorder = 0;
|
|
||||||
etherpadIFrame.scrolling = "no";
|
|
||||||
etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
|
|
||||||
etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
|
|
||||||
etherpadIFrame.setAttribute('style', 'visibility: hidden;');
|
|
||||||
|
|
||||||
document.getElementById('etherpad').appendChild(etherpadIFrame);
|
|
||||||
|
|
||||||
etherpadIFrame.onload = onloadHandler;
|
|
||||||
|
|
||||||
return etherpadIFrame;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Changes the state of the large video.
|
|
||||||
* Possible values - video, prezi, etherpad.
|
|
||||||
* @param newState - the new state
|
|
||||||
*/
|
|
||||||
setState: function (newState) {
|
|
||||||
if(state === newState)
|
|
||||||
return;
|
|
||||||
var currentContainer = getContainerByState(state);
|
|
||||||
if(!currentContainer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var oldState = state;
|
|
||||||
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case "etherpad":
|
|
||||||
$('#activeSpeaker').css('visibility', 'hidden');
|
|
||||||
currentContainer.fadeOut(300, function () {
|
|
||||||
if (oldState === "prezi") {
|
|
||||||
currentContainer.css({opacity: '0'});
|
|
||||||
$('#reloadPresentation').css({display: 'none'});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.setLargeVideoVisible(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#etherpad>iframe').fadeIn(300, function () {
|
|
||||||
document.body.style.background = '#eeeeee';
|
|
||||||
$('#etherpad>iframe').css({visibility: 'visible'});
|
|
||||||
$('#etherpad').css({zIndex: 2});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "prezi":
|
|
||||||
var prezi = $('#presentation>iframe');
|
|
||||||
currentContainer.fadeOut(300, function() {
|
|
||||||
document.body.style.background = 'black';
|
|
||||||
});
|
|
||||||
prezi.fadeIn(300, function() {
|
|
||||||
prezi.css({opacity:'1'});
|
|
||||||
ToolbarToggler.dockToolbar(true);//fix that
|
|
||||||
self.setLargeVideoVisible(false);
|
|
||||||
$('#etherpad>iframe').css({visibility: 'hidden'});
|
|
||||||
$('#etherpad').css({zIndex: 0});
|
|
||||||
});
|
|
||||||
$('#activeSpeaker').css('visibility', 'hidden');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "video":
|
|
||||||
currentContainer.fadeOut(300, function () {
|
|
||||||
$('#presentation>iframe').css({opacity:'0'});
|
|
||||||
$('#reloadPresentation').css({display:'none'});
|
|
||||||
$('#etherpad>iframe').css({visibility: 'hidden'});
|
|
||||||
$('#etherpad').css({zIndex: 0});
|
|
||||||
document.body.style.background = 'black';
|
|
||||||
ToolbarToggler.dockToolbar(false);//fix that
|
|
||||||
});
|
|
||||||
$('#largeVideoWrapper').fadeIn(300, function () {
|
|
||||||
self.setLargeVideoVisible(true);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = newState;
|
|
||||||
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns the current state of the large video.
|
|
||||||
* @returns {string} the current state - video, prezi or etherpad.
|
|
||||||
*/
|
|
||||||
getState: function () {
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Sets hover handlers for the large video container div.
|
|
||||||
*
|
|
||||||
* @param inHandler
|
|
||||||
* @param outHandler
|
|
||||||
*/
|
|
||||||
setHover: function(inHandler, outHandler)
|
|
||||||
{
|
|
||||||
$('#largeVideoContainer').hover(inHandler, outHandler);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables/disables the filter indicating a video problem to the user.
|
* Enables/disables the filter indicating a video problem to the user.
|
||||||
*
|
*
|
||||||
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
|
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
|
||||||
*/
|
*/
|
||||||
enableVideoProblemFilter: function (enable) {
|
enableVideoProblemFilter (enable) {
|
||||||
$("#largeVideo").toggleClass("videoProblemFilter", enable);
|
this.videoContainer.$video.toggleClass("videoProblemFilter", enable);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export default LargeVideo;
|
/**
|
||||||
|
* Updates the src of the active speaker avatar
|
||||||
|
*/
|
||||||
|
updateAvatar (thumbUrl) {
|
||||||
|
$("#activeSpeakerAvatar").attr('src', thumbUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAvatar (show) {
|
||||||
|
this.videoContainer.showAvatar(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
addContainer (type, container) {
|
||||||
|
if (this.containers[type]) {
|
||||||
|
throw new Error(`container of type ${type} already exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.containers[type] = container;
|
||||||
|
this.resizeContainer(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContainer (type) {
|
||||||
|
let container = this.containers[type];
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
throw new Error(`container of type ${type} doesn't exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContainer (type) {
|
||||||
|
if (!this.containers[type]) {
|
||||||
|
throw new Error(`container of type ${type} doesn't exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.containers[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
showContainer (type) {
|
||||||
|
if (this.state === type) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = this.getContainer(type);
|
||||||
|
|
||||||
|
if (this.state) {
|
||||||
|
let oldContainer = this.containers[this.state];
|
||||||
|
if (oldContainer) {
|
||||||
|
oldContainer.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = type;
|
||||||
|
|
||||||
|
return container.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -145,8 +145,8 @@ SmallVideo.prototype.bindHoverHandler = function () {
|
||||||
function () {
|
function () {
|
||||||
// If the video has been "pinned" by the user we want to
|
// If the video has been "pinned" by the user we want to
|
||||||
// keep the display name on place.
|
// keep the display name on place.
|
||||||
if (!LargeVideo.isLargeVideoVisible() ||
|
if (!self.VideoLayout.isLargeVideoVisible() ||
|
||||||
!LargeVideo.isCurrentlyOnLarge(self.id))
|
!self.VideoLayout.isCurrentlyOnLarge(self.id))
|
||||||
self.showDisplayName(false);
|
self.showDisplayName(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -254,7 +254,7 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEnable) {
|
if (isEnable) {
|
||||||
this.showDisplayName(LargeVideo.getState() === "video");
|
this.showDisplayName(this.VideoLayout.isLargeVideoVisible());
|
||||||
|
|
||||||
if (!this.container.classList.contains("dominantspeaker"))
|
if (!this.container.classList.contains("dominantspeaker"))
|
||||||
this.container.classList.add("dominantspeaker");
|
this.container.classList.add("dominantspeaker");
|
||||||
|
@ -388,7 +388,10 @@ SmallVideo.prototype.showAvatar = function (show) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LargeVideo.showAvatar(this.id, show)) {
|
if (this.VideoLayout.isCurrentlyOnLarge(this.id)
|
||||||
|
&& this.VideoLayout.isLargeVideoVisible()) {
|
||||||
|
|
||||||
|
this.VideoLayout.showLargeVideoAvatar(show);
|
||||||
setVisibility(avatar, false);
|
setVisibility(avatar, false);
|
||||||
setVisibility(video, false);
|
setVisibility(video, false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
|
|
||||||
import AudioLevels from "../audio_levels/AudioLevels";
|
import AudioLevels from "../audio_levels/AudioLevels";
|
||||||
|
import BottomToolbar from "../toolbars/BottomToolbar";
|
||||||
|
|
||||||
import UIEvents from "../../../service/UI/UIEvents";
|
import UIEvents from "../../../service/UI/UIEvents";
|
||||||
import UIUtil from "../util/UIUtil";
|
import UIUtil from "../util/UIUtil";
|
||||||
|
|
||||||
import RemoteVideo from "./RemoteVideo";
|
import RemoteVideo from "./RemoteVideo";
|
||||||
import LargeVideo from "./LargeVideo";
|
import LargeVideoManager, {VideoContainerType} from "./LargeVideo";
|
||||||
|
import {PreziContainerType} from '../prezi/Prezi';
|
||||||
import LocalVideo from "./LocalVideo";
|
import LocalVideo from "./LocalVideo";
|
||||||
|
|
||||||
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
||||||
|
@ -88,22 +90,31 @@ function getPeerContainerResourceId (containerElement) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let largeVideo;
|
||||||
|
|
||||||
var VideoLayout = {
|
var VideoLayout = {
|
||||||
init (emitter) {
|
init (emitter) {
|
||||||
eventEmitter = emitter;
|
eventEmitter = emitter;
|
||||||
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
||||||
if (interfaceConfig.filmStripOnly) {
|
|
||||||
LargeVideo.disable();
|
|
||||||
} else {
|
|
||||||
LargeVideo.init(VideoLayout, emitter);
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoLayout.resizeLargeVideoContainer();
|
|
||||||
|
|
||||||
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
|
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
initLargeVideo (isSideBarVisible) {
|
||||||
|
largeVideo = new LargeVideoManager();
|
||||||
|
largeVideo.updateContainerSize(isSideBarVisible);
|
||||||
|
AudioLevels.init();
|
||||||
|
},
|
||||||
|
|
||||||
|
setAudioLevel(id, lvl) {
|
||||||
|
if (!largeVideo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AudioLevels.updateAudioLevel(
|
||||||
|
id, lvl, largeVideo.id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
isInLastN (resource) {
|
isInLastN (resource) {
|
||||||
return lastNCount < 0 || // lastN is disabled
|
return lastNCount < 0 || // lastN is disabled
|
||||||
// lastNEndpoints cache not built yet
|
// lastNEndpoints cache not built yet
|
||||||
|
@ -147,8 +158,8 @@ var VideoLayout = {
|
||||||
localVideoThumbnail.changeVideo(stream);
|
localVideoThumbnail.changeVideo(stream);
|
||||||
|
|
||||||
/* force update if we're currently being displayed */
|
/* force update if we're currently being displayed */
|
||||||
if (LargeVideo.isCurrentlyOnLarge(localId)) {
|
if (this.isCurrentlyOnLarge(localId)) {
|
||||||
LargeVideo.updateLargeVideo(localId, true);
|
this.updateLargeVideo(localId, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -156,8 +167,8 @@ var VideoLayout = {
|
||||||
let id = APP.conference.localId;
|
let id = APP.conference.localId;
|
||||||
localVideoThumbnail.joined(id);
|
localVideoThumbnail.joined(id);
|
||||||
|
|
||||||
if (!LargeVideo.id) {
|
if (largeVideo && !largeVideo.id) {
|
||||||
LargeVideo.updateLargeVideo(id, true);
|
this.updateLargeVideo(id, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -183,7 +194,7 @@ var VideoLayout = {
|
||||||
* another one instead.
|
* another one instead.
|
||||||
*/
|
*/
|
||||||
updateRemovedVideo (id) {
|
updateRemovedVideo (id) {
|
||||||
if (id !== LargeVideo.getId()) {
|
if (!this.isCurrentlyOnLarge(id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +209,7 @@ var VideoLayout = {
|
||||||
newId = this.electLastVisibleVideo();
|
newId = this.electLastVisibleVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
LargeVideo.updateLargeVideo(newId);
|
this.updateLargeVideo(newId);
|
||||||
},
|
},
|
||||||
|
|
||||||
electLastVisibleVideo () {
|
electLastVisibleVideo () {
|
||||||
|
@ -242,10 +253,6 @@ var VideoLayout = {
|
||||||
remoteVideos[id].addRemoteStreamElement(stream);
|
remoteVideos[id].addRemoteStreamElement(stream);
|
||||||
},
|
},
|
||||||
|
|
||||||
getLargeVideoId () {
|
|
||||||
return LargeVideo.getId();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the type of the remote video.
|
* Return the type of the remote video.
|
||||||
* @param id the id for the remote video
|
* @param id the id for the remote video
|
||||||
|
@ -255,25 +262,6 @@ var VideoLayout = {
|
||||||
return remoteVideoTypes[id];
|
return remoteVideoTypes[id];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when large video update is finished
|
|
||||||
* @param currentSmallVideo small video currently displayed on large video
|
|
||||||
*/
|
|
||||||
largeVideoUpdated (currentSmallVideo) {
|
|
||||||
// Makes sure that dominant speaker UI
|
|
||||||
// is enabled only on current small video
|
|
||||||
localVideoThumbnail.enableDominantSpeaker(localVideoThumbnail === currentSmallVideo);
|
|
||||||
Object.keys(remoteVideos).forEach(
|
|
||||||
function (resourceJid) {
|
|
||||||
var remoteVideo = remoteVideos[resourceJid];
|
|
||||||
if (remoteVideo) {
|
|
||||||
remoteVideo.enableDominantSpeaker(
|
|
||||||
remoteVideo === currentSmallVideo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleVideoThumbClicked (noPinnedEndpointChangedEvent,
|
handleVideoThumbClicked (noPinnedEndpointChangedEvent,
|
||||||
resourceJid) {
|
resourceJid) {
|
||||||
if(focusedVideoResourceJid) {
|
if(focusedVideoResourceJid) {
|
||||||
|
@ -291,7 +279,7 @@ var VideoLayout = {
|
||||||
// Enable the currently set dominant speaker.
|
// Enable the currently set dominant speaker.
|
||||||
if (currentDominantSpeaker) {
|
if (currentDominantSpeaker) {
|
||||||
if(smallVideo && smallVideo.hasVideo()) {
|
if(smallVideo && smallVideo.hasVideo()) {
|
||||||
LargeVideo.updateLargeVideo(currentDominantSpeaker);
|
this.updateLargeVideo(currentDominantSpeaker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,9 +302,7 @@ var VideoLayout = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LargeVideo.setState("video");
|
this.updateLargeVideo(resourceJid);
|
||||||
|
|
||||||
LargeVideo.updateLargeVideo(resourceJid);
|
|
||||||
|
|
||||||
// Writing volume not allowed in IE
|
// Writing volume not allowed in IE
|
||||||
if (!RTCBrowserType.isIExplorer()) {
|
if (!RTCBrowserType.isIExplorer()) {
|
||||||
|
@ -370,11 +356,11 @@ var VideoLayout = {
|
||||||
// the current dominant speaker.
|
// the current dominant speaker.
|
||||||
if ((!focusedVideoResourceJid &&
|
if ((!focusedVideoResourceJid &&
|
||||||
!currentDominantSpeaker &&
|
!currentDominantSpeaker &&
|
||||||
!require("../prezi/Prezi").isPresentationVisible()) ||
|
!this.isLargeContainerTypeVisible(PreziContainerType)) ||
|
||||||
focusedVideoResourceJid === resourceJid ||
|
focusedVideoResourceJid === resourceJid ||
|
||||||
(resourceJid &&
|
(resourceJid &&
|
||||||
currentDominantSpeaker === resourceJid)) {
|
currentDominantSpeaker === resourceJid)) {
|
||||||
LargeVideo.updateLargeVideo(resourceJid, true);
|
this.updateLargeVideo(resourceJid, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -419,63 +405,44 @@ var VideoLayout = {
|
||||||
/**
|
/**
|
||||||
* Resizes the large video container.
|
* Resizes the large video container.
|
||||||
*/
|
*/
|
||||||
resizeLargeVideoContainer () {
|
resizeLargeVideoContainer (isSideBarVisible) {
|
||||||
if(LargeVideo.isEnabled()) {
|
if (largeVideo) {
|
||||||
LargeVideo.resize();
|
largeVideo.updateContainerSize(isSideBarVisible);
|
||||||
|
largeVideo.resize(false);
|
||||||
} else {
|
} else {
|
||||||
VideoLayout.resizeVideoSpace();
|
this.resizeVideoSpace(false, isSideBarVisible);
|
||||||
}
|
}
|
||||||
VideoLayout.resizeThumbnails();
|
this.resizeThumbnails(false);
|
||||||
LargeVideo.position();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes thumbnails.
|
* Resizes thumbnails.
|
||||||
*/
|
*/
|
||||||
resizeThumbnails (animate) {
|
resizeThumbnails (animate = false) {
|
||||||
var videoSpaceWidth = $('#remoteVideos').width();
|
let videoSpaceWidth = $('#remoteVideos').width();
|
||||||
|
|
||||||
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
|
let [width, height] = this.calculateThumbnailSize(videoSpaceWidth);
|
||||||
var width = thumbnailSize[0];
|
|
||||||
var height = thumbnailSize[1];
|
|
||||||
|
|
||||||
$('.userAvatar').css('left', (width - height) / 2);
|
$('.userAvatar').css('left', (width - height) / 2);
|
||||||
|
|
||||||
if(animate) {
|
|
||||||
$('#remoteVideos').animate({
|
$('#remoteVideos').animate({
|
||||||
// adds 2 px because of small video 1px border
|
// adds 2 px because of small video 1px border
|
||||||
height: height + 2
|
height: height + 2
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
queue: false,
|
queue: false,
|
||||||
duration: 500
|
duration: animate ? 500 : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#remoteVideos>span').animate({
|
$('#remoteVideos>span').animate({
|
||||||
height: height,
|
height, width
|
||||||
width: width
|
}, {
|
||||||
},
|
|
||||||
{
|
|
||||||
queue: false,
|
queue: false,
|
||||||
duration: 500,
|
duration: animate ? 500 : 0,
|
||||||
complete: function () {
|
complete: function () {
|
||||||
$(document).trigger(
|
BottomToolbar.onRemoteVideoResized(width, height);
|
||||||
"remotevideo.resized",
|
AudioLevels.onRemoteVideoResized(width, height);
|
||||||
[width,
|
|
||||||
height]);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
|
||||||
// size videos so that while keeping AR and max height, we have a
|
|
||||||
// nice fit
|
|
||||||
// adds 2 px because of small video 1px border
|
|
||||||
$('#remoteVideos').height(height + 2);
|
|
||||||
$('#remoteVideos>span').width(width);
|
|
||||||
$('#remoteVideos>span').height(height);
|
|
||||||
|
|
||||||
$(document).trigger("remotevideo.resized", [width, height]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -604,7 +571,7 @@ var VideoLayout = {
|
||||||
// Update the large video if the video source is already available,
|
// Update the large video if the video source is already available,
|
||||||
// otherwise wait for the "videoactive.jingle" event.
|
// otherwise wait for the "videoactive.jingle" event.
|
||||||
if (videoSel[0].currentTime > 0) {
|
if (videoSel[0].currentTime > 0) {
|
||||||
LargeVideo.updateLargeVideo(id);
|
this.updateLargeVideo(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -696,7 +663,7 @@ var VideoLayout = {
|
||||||
// displayed in the large video we have to switch to another
|
// displayed in the large video we have to switch to another
|
||||||
// user.
|
// user.
|
||||||
if (!updateLargeVideo &&
|
if (!updateLargeVideo &&
|
||||||
resourceJid === LargeVideo.getId()) {
|
this.isCurrentlyOnLarge(resourceJid)) {
|
||||||
updateLargeVideo = true;
|
updateLargeVideo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -754,7 +721,7 @@ var VideoLayout = {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// videoSrcToSsrc needs to be update for this call to succeed.
|
// videoSrcToSsrc needs to be update for this call to succeed.
|
||||||
LargeVideo.updateLargeVideo(resource);
|
this.updateLargeVideo(resource);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,15 +829,9 @@ var VideoLayout = {
|
||||||
}
|
}
|
||||||
|
|
||||||
smallVideo.setVideoType(newVideoType);
|
smallVideo.setVideoType(newVideoType);
|
||||||
LargeVideo.onVideoTypeChanged(id, newVideoType);
|
if (this.isCurrentlyOnLarge(id)) {
|
||||||
},
|
this.updateLargeVideo(id, true);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Updates the video size and position.
|
|
||||||
*/
|
|
||||||
updateLargeVideoSize () {
|
|
||||||
LargeVideo.updateVideoSizeAndPosition();
|
|
||||||
LargeVideo.position(null, null, null, null, true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showMore (jid) {
|
showMore (jid) {
|
||||||
|
@ -886,22 +847,8 @@ var VideoLayout = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addPreziContainer (id) {
|
addRemoteVideoContainer (id) {
|
||||||
var container = RemoteVideo.createContainer(id);
|
return RemoteVideo.createContainer(id);
|
||||||
VideoLayout.resizeThumbnails();
|
|
||||||
return container;
|
|
||||||
},
|
|
||||||
|
|
||||||
setLargeVideoVisible (isVisible) {
|
|
||||||
LargeVideo.setLargeVideoVisible(isVisible);
|
|
||||||
if(!isVisible && focusedVideoResourceJid) {
|
|
||||||
var smallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
|
|
||||||
if(smallVideo) {
|
|
||||||
smallVideo.focus(false);
|
|
||||||
smallVideo.showAvatar();
|
|
||||||
}
|
|
||||||
focusedVideoResourceJid = null;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -912,8 +859,15 @@ var VideoLayout = {
|
||||||
* resized.
|
* resized.
|
||||||
*/
|
*/
|
||||||
resizeVideoArea (isSideBarVisible, callback) {
|
resizeVideoArea (isSideBarVisible, callback) {
|
||||||
LargeVideo.resizeVideoAreaAnimated(isSideBarVisible, callback);
|
let animate = true;
|
||||||
VideoLayout.resizeThumbnails(true);
|
|
||||||
|
if (largeVideo) {
|
||||||
|
largeVideo.updateContainerSize(isSideBarVisible);
|
||||||
|
largeVideo.resize(animate);
|
||||||
|
this.resizeVideoSpace(animate, isSideBarVisible, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoLayout.resizeThumbnails(animate);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -945,8 +899,7 @@ var VideoLayout = {
|
||||||
complete: completeFunction
|
complete: completeFunction
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$('#videospace').width(availableWidth);
|
$('#videospace').width(availableWidth).height(availableHeight);
|
||||||
$('#videospace').height(availableHeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -968,43 +921,105 @@ var VideoLayout = {
|
||||||
"Missed avatar update - no small video yet for " + id
|
"Missed avatar update - no small video yet for " + id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
LargeVideo.updateAvatar(id, thumbUrl);
|
if (this.isCurrentlyOnLarge(id)) {
|
||||||
},
|
largeVideo.updateAvatar(thumbUrl);
|
||||||
|
}
|
||||||
createEtherpadIframe (src, onloadHandler) {
|
|
||||||
return LargeVideo.createEtherpadIframe(src, onloadHandler);
|
|
||||||
},
|
|
||||||
|
|
||||||
setLargeVideoState (state) {
|
|
||||||
LargeVideo.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
getLargeVideoState () {
|
|
||||||
return LargeVideo.getState();
|
|
||||||
},
|
|
||||||
|
|
||||||
setLargeVideoHover (inHandler, outHandler) {
|
|
||||||
LargeVideo.setHover(inHandler, outHandler);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the video has been interrupted.
|
* Indicates that the video has been interrupted.
|
||||||
*/
|
*/
|
||||||
onVideoInterrupted () {
|
onVideoInterrupted () {
|
||||||
LargeVideo.enableVideoProblemFilter(true);
|
this.enableVideoProblemFilter(true);
|
||||||
var reconnectingKey = "connection.RECONNECTING";
|
let reconnectingKey = "connection.RECONNECTING";
|
||||||
$('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
|
|
||||||
$('#videoConnectionMessage')
|
$('#videoConnectionMessage')
|
||||||
.text(APP.translation.translateString(reconnectingKey));
|
.attr("data-i18n", reconnectingKey)
|
||||||
$('#videoConnectionMessage').css({display: "block"});
|
.text(APP.translation.translateString(reconnectingKey))
|
||||||
|
.css({display: "block"});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the video has been restored.
|
* Indicates that the video has been restored.
|
||||||
*/
|
*/
|
||||||
onVideoRestored () {
|
onVideoRestored () {
|
||||||
LargeVideo.enableVideoProblemFilter(false);
|
this.enableVideoProblemFilter(false);
|
||||||
$('#videoConnectionMessage').css({display: "none"});
|
$('#videoConnectionMessage').css({display: "none"});
|
||||||
|
},
|
||||||
|
|
||||||
|
enableVideoProblemFilter (enable) {
|
||||||
|
if (!largeVideo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
largeVideo.enableVideoProblemFilter(enable);
|
||||||
|
},
|
||||||
|
|
||||||
|
isLargeVideoVisible () {
|
||||||
|
return this.isLargeContainerTypeVisible(VideoContainerType);
|
||||||
|
},
|
||||||
|
|
||||||
|
isCurrentlyOnLarge (id) {
|
||||||
|
return largeVideo && largeVideo.id === id;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLargeVideo (id, forceUpdate) {
|
||||||
|
if (!largeVideo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let isOnLarge = this.isCurrentlyOnLarge(id);
|
||||||
|
let currentId = largeVideo.id;
|
||||||
|
|
||||||
|
if (!isOnLarge || forceUpdate) {
|
||||||
|
if (id !== currentId) {
|
||||||
|
eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
|
||||||
|
}
|
||||||
|
if (currentId) {
|
||||||
|
let currentSmallVideo = this.getSmallVideo(currentId);
|
||||||
|
currentSmallVideo && currentSmallVideo.enableDominantSpeaker(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let smallVideo = this.getSmallVideo(id);
|
||||||
|
|
||||||
|
largeVideo.updateLargeVideo(smallVideo.stream);
|
||||||
|
|
||||||
|
smallVideo.enableDominantSpeaker(true);
|
||||||
|
} else if (currentId) {
|
||||||
|
let currentSmallVideo = this.getSmallVideo(currentId);
|
||||||
|
currentSmallVideo.showAvatar();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showLargeVideoAvatar (show) {
|
||||||
|
largeVideo && largeVideo.showAvatar(show);
|
||||||
|
},
|
||||||
|
|
||||||
|
addLargeVideoContainer (type, container) {
|
||||||
|
largeVideo && largeVideo.addContainer(type, container);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeLargeVideoContainer (type) {
|
||||||
|
largeVideo && largeVideo.removeContainer(type);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
showLargeVideoContainer (type, show) {
|
||||||
|
if (!largeVideo) {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
let isVisible = this.isLargeContainerTypeVisible(type);
|
||||||
|
if (isVisible === show) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !show then use default type - large video
|
||||||
|
return largeVideo.showContainer(show ? type : VideoContainerType);
|
||||||
|
},
|
||||||
|
|
||||||
|
isLargeContainerTypeVisible (type) {
|
||||||
|
return largeVideo && largeVideo.state === type;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ export default {
|
||||||
NICKNAME_CHANGED: "UI.nickname_changed",
|
NICKNAME_CHANGED: "UI.nickname_changed",
|
||||||
SELECTED_ENDPOINT: "UI.selected_endpoint",
|
SELECTED_ENDPOINT: "UI.selected_endpoint",
|
||||||
PINNED_ENDPOINT: "UI.pinned_endpoint",
|
PINNED_ENDPOINT: "UI.pinned_endpoint",
|
||||||
LARGEVIDEO_INIT: "UI.largevideo_init",
|
|
||||||
/**
|
/**
|
||||||
* Notifies that local user created text message.
|
* Notifies that local user created text message.
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +21,9 @@ export default {
|
||||||
AUDIO_MUTED: "UI.audio_muted",
|
AUDIO_MUTED: "UI.audio_muted",
|
||||||
VIDEO_MUTED: "UI.video_muted",
|
VIDEO_MUTED: "UI.video_muted",
|
||||||
PREZI_CLICKED: "UI.prezi_clicked",
|
PREZI_CLICKED: "UI.prezi_clicked",
|
||||||
|
SHARE_PREZI: "UI.share_prezi",
|
||||||
|
PREZI_SLIDE_CHANGED: "UI.prezi_slide_changed",
|
||||||
|
STOP_SHARING_PREZI: "UI.stop_sharing_prezi",
|
||||||
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
||||||
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
||||||
USER_INVITED: "UI.user_invited",
|
USER_INVITED: "UI.user_invited",
|
||||||
|
|
Loading…
Reference in New Issue