refactoring of Etherpad and Prezi

This commit is contained in:
isymchych 2015-12-25 18:55:45 +02:00
parent e494c3028d
commit 0fd0f5b633
18 changed files with 2256 additions and 2078 deletions

51
app.js
View File

@ -41,6 +41,9 @@ const Commands = {
CONNECTION_QUALITY: "connectionQuality",
EMAIL: "email",
VIDEO_TYPE: "videoType"
ETHERPAD: "etherpad",
PREZI: "prezi",
STOP_PREZI: "stop-prezi"
};
function buildRoomName () {
@ -218,16 +221,14 @@ function initConference(localTracks, connection) {
room.on(ConferenceEvents.USER_JOINED, function (id, user) {
if (APP.conference.isLocalId(id)) {
return;
}
console.error('USER %s connnected', id);
console.error('USER %s connnected', id, user);
// FIXME email???
APP.UI.addUser(id, user.getDisplayName());
});
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.stopPrezi(id);
});
@ -348,9 +349,12 @@ function initConference(localTracks, connection) {
room.removeCommand(Commands.CONNECTION_QUALITY);
});
// listen to remote stats
room.addCommandListener(Commands.CONNECTION_QUALITY, function (data) {
APP.connectionquality.updateRemoteStats(data.attributes.id, data.value);
});
room.addCommandListener(
Commands.CONNECTION_QUALITY,
function ({value, attributes}) {
APP.connectionquality.updateRemoteStats(attributes.id, value);
}
);
APP.connectionquality.addListener(
CQEvents.REMOTESTATS_UPDATED,
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) => {
APP.UI.onPeerVideoTypeChanged(from, data.value);
});

View File

@ -34,7 +34,7 @@
}
#remoteVideos .videocontainer {
display: inline-block;
display: none;
background-color: black;
background-size: contain;
border-radius:8px;

View File

@ -140,6 +140,25 @@
</div>
<div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
<div id="videospace">
<div id="largeVideoContainer" class="videocontainer">
<div id="presentation"></div>
<div id="etherpad"></div>
<a target="_new"><div class="watermark leftwatermark"></div></a>
<a target="_new"><div class="watermark rightwatermark"></div></a>
<a class="poweredby" href="http://jitsi.org" target="_new">
<span data-i18n="poweredby"></span> jitsi.org
</a>
<div id="activeSpeaker">
<img id="activeSpeakerAvatar" src=""/>
<canvas id="activeSpeakerAudioLevel"></canvas>
</div>
<div id="largeVideoWrapper">
<video id="largeVideo" muted="true" autoplay oncontextmenu="return false;"></video>
</div>
<span id="videoConnectionMessage"></span>
</div>
<div id="remoteVideos">
<span id="localVideoContainer" class="videocontainer">
<span id="localNick" class="nick"></span>

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
/* jshint -W101 */
var UI = {};
import AudioLevels from './audio_levels/AudioLevels';
import Chat from "./side_pannels/chat/Chat";
import Toolbar from "./toolbars/Toolbar";
import ToolbarToggler from "./toolbars/ToolbarToggler";
@ -12,12 +11,12 @@ import Avatar from "./avatar/Avatar";
import PanelToggler from "./side_pannels/SidePanelToggler";
import UIUtil from "./util/UIUtil";
import UIEvents from "../../service/UI/UIEvents";
import PreziManager from './prezi/Prezi';
import EtherpadManager from './etherpad/Etherpad';
import VideoLayout from "./videolayout/VideoLayout";
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
var Prezi = require("./prezi/Prezi");
var Etherpad = require("./etherpad/Etherpad");
var EventEmitter = require("events");
var Settings = require("./../settings/Settings");
UI.messageHandler = require("./util/MessageHandler");
@ -32,6 +31,9 @@ var Feedback = require("./Feedback");
var eventEmitter = new EventEmitter();
UI.eventEmitter = eventEmitter;
let preziManager;
let etherpadManager;
function promptDisplayName() {
let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
let defaultNickMsg = APP.translation.translateString(
@ -77,12 +79,6 @@ function promptDisplayName() {
);
}
function setupPrezi() {
$("#reloadPresentationLink").click(function() {
Prezi.reloadPresentation();
});
}
function setupChat() {
Chat.init(eventEmitter);
$("#toggle_smileys").click(function() {
@ -189,20 +185,18 @@ UI.initConference = function () {
};
function registerListeners() {
UI.addListener(UIEvents.LARGEVIDEO_INIT, function () {
AudioLevels.init();
});
UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
UI.setUserAvatar(APP.conference.localId, email);
});
UI.addListener(UIEvents.PREZI_CLICKED, function () {
Prezi.openPreziDialog();
preziManager.handlePreziButtonClicked();
});
UI.addListener(UIEvents.ETHERPAD_CLICKED, function () {
Etherpad.toggleEtherpad(0);
if (etherpadManager) {
etherpadManager.toggleEtherpad();
}
});
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
@ -221,7 +215,7 @@ function registerListeners() {
function bindEvents() {
function onResize() {
PanelToggler.resizeChat();
VideoLayout.resizeLargeVideoContainer();
VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
}
// Resize and reposition videos in full screen mode.
@ -254,11 +248,17 @@ UI.start = function () {
registerListeners();
VideoLayout.init(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
VideoLayout.initLargeVideo(PanelToggler.isVisible());
}
VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
ContactList.init(eventEmitter);
bindEvents();
setupPrezi();
preziManager = new PreziManager(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
@ -350,9 +350,14 @@ UI.setSubject = function (subject) {
Chat.setSubject(subject);
};
function initEtherpad(name) {
Etherpad.init(name);
}
UI.initEtherpad = function (name) {
if (etherpadManager) {
return;
}
console.log('Etherpad is enabled');
etherpadManager = new EtherpadManager(config.etherpad_base, name);
Toolbar.showEtherpadButton();
};
UI.addUser = function (id, displayName) {
ContactList.addContact(id);
@ -443,7 +448,6 @@ UI.getSettings = function () {
UI.toggleFilmStrip = function () {
BottomToolbar.toggleFilmStrip();
VideoLayout.updateLargeVideoSize();
};
UI.toggleChat = function () {
@ -592,9 +596,7 @@ UI.handleLastNEndpoints = function (ids) {
};
UI.setAudioLevel = function (id, lvl) {
AudioLevels.updateAudioLevel(
id, lvl, VideoLayout.getLargeVideoId()
);
VideoLayout.setAudioLevel(id, lvl);
};
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;

View File

@ -112,33 +112,6 @@ function getVideoSpanId(id) {
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.
*/
@ -248,6 +221,33 @@ const AudioLevels = {
// Fill the shape.
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;
});
}
}
};

View File

@ -1,61 +1,16 @@
/* global $, config,
setLargeVideoVisible, Util */
/* global $ */
var VideoLayout = require("../videolayout/VideoLayout");
var Prezi = require("../prezi/Prezi");
var UIUtil = require("../util/UIUtil");
import VideoLayout from "../videolayout/VideoLayout";
import LargeContainer from '../videolayout/LargeContainer';
import UIUtil from "../util/UIUtil";
import SidePanelToggler from "../side_pannels/SidePanelToggler";
var etherpadName = null;
var etherpadIFrame = null;
var domain = null;
var options = "?showControls=true&showChat=false&showLineNumbers=true" +
"&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);
});
}
const options = $.param({
showControns: true,
showChat: false,
showLineNumbers: true,
useMonospaceFont: false
});
function bubbleIframeMouseMove(iframe){
var existingOnMouseMove = iframe.contentWindow.onmousemove;
@ -84,48 +39,123 @@ function bubbleIframeMouseMove(iframe){
};
}
const DEFAULT_WIDTH = 640;
const DEFAULT_HEIGHT = 480;
var Etherpad = {
/**
* Initializes the etherpad.
*/
init: function (name) {
const EtherpadContainerType = "etherpad";
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);
/**
* Resizes the etherpad, when the window is resized.
*/
$(window).resize(function () {
resize();
iframe.onload = function() {
document.domain = document.domain;
bubbleIframeMouseMove(iframe);
setTimeout(function() {
const doc = iframe.contentDocument;
// 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);
const inner = doc.getElementsByName("ace_inner")[0];
bubbleIframeMouseMove(inner);
}, 2000);
};
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();
});
});
}
},
/**
* Opens/hides the Etherpad.
*/
toggleEtherpad: function (isPresentation) {
if (!etherpadIFrame)
createIFrame();
hide () {
const $iframe = $(this.iframe);
const $container = $(this.container);
if(VideoLayout.getLargeVideoState() === "etherpad")
{
VideoLayout.setLargeVideoState("video");
return new Promise(resolve => {
$iframe.fadeOut(300, function () {
$iframe.css({visibility: 'hidden'});
$container.css({zIndex: 0});
resolve();
});
});
}
else
{
VideoLayout.setLargeVideoState("etherpad");
}
resize();
}
};
}
module.exports = Etherpad;
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);
}
}

View File

@ -1,268 +1,21 @@
/* global $, APP */
/* jshint -W101 */
import UIUtil from "../util/UIUtil";
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");
var PreziPlayer = require("./PreziPlayer");
const defaultPreziLink = "http://prezi.com/wz7vhjycl7e6/my-prezi";
const alphanumRegex = /^[a-z0-9-_\/&\?=;]+$/i;
const aspectRatio = 16.0 / 9.0;
var preziPlayer = null;
/**
* 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;
}
};
const DEFAULT_WIDTH = 640;
const DEFAULT_HEIGHT = 480;
/**
* Indicates if the given string is an alphanumeric string.
@ -270,76 +23,355 @@ function presentationRemoved(event, jid, presUrl) {
* purpose of checking URIs.
*/
function isAlphanumeric(unsafeText) {
var regex = /^[a-z0-9-_\/&\?=;]+$/i;
return regex.test(unsafeText);
return alphanumRegex.test(unsafeText);
}
/**
* Returns the presentation id from the given url.
*/
function getPresentationId (presUrl) {
var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
return presIdTmp.substring(0, presIdTmp.indexOf('/'));
function getPresentationId (url) {
let presId = url.substring(url.indexOf("prezi.com/") + 10);
return presId.substring(0, presId.indexOf('/'));
}
/**
* Returns the presentation width.
*/
function getPresentationWidth() {
var availableWidth = UIUtil.getAvailableVideoWidth();
var availableHeight = getPresentationHeihgt();
var aspectRatio = 16.0 / 9.0;
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
function isPreziLink(url) {
if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) {
return false;
}
return availableWidth;
let presId = url.substring(url.indexOf("prezi.com/") + 10);
if (!isAlphanumeric(presId) || presId.indexOf('/') < 2) {
return false;
}
return true;
}
/**
* Returns the presentation height.
*/
function getPresentationHeihgt() {
var remoteVideos = $('#remoteVideos');
return window.innerHeight - remoteVideos.outerHeight();
function notifyOtherIsSharingPrezi() {
messageHandler.openMessageDialog(
"dialog.sharePreziTitle",
"dialog.sharePreziMsg"
);
}
/**
* Resizes the presentation iframe.
*/
function resize() {
if ($('#presentation>iframe')) {
$('#presentation>iframe').width(getPresentationWidth());
$('#presentation>iframe').height(getPresentationHeihgt());
function proposeToClosePrezi() {
return new Promise(function (resolve, reject) {
messageHandler.openTwoButtonDialog(
"dialog.removePreziTitle",
null,
"dialog.removePreziMsg",
null,
false,
"dialog.Remove",
function(e,v,m,f) {
if (v) {
resolve();
} else {
reject();
}
}
);
});
}
function requestPreziLink() {
const title = APP.translation.generateTranslationHTML("dialog.sharePreziTitle");
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');
}
}
}
});
});
}
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);
}
});
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();
}
}
/**
* Presentation has been removed.
*/
$(document).bind('presentationremoved.muc', presentationRemoved);
export default class PreziManager {
constructor (emitter) {
this.emitter = emitter;
/**
* Presentation has been added.
*/
$(document).bind('presentationadded.muc', presentationAdded);
this.userId = null;
this.url = null;
this.prezi = null;
/*
* Indicates presentation slide change.
*/
$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
if (preziPlayer && preziPlayer.getCurrentStep() != current) {
preziPlayer.flyToStep(current);
$("#reloadPresentationLink").click(this.reloadPresentation.bind(this));
}
var animationStepsArray = preziPlayer.getAnimationCountOnSteps();
for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
preziPlayer.flyToStep(current, i);
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();
}
}
});
$(window).resize(function () {
resize();
});
reloadPresentation () {
if (!this.prezi) {
return;
}
let iframe = this.prezi.$iframe[0];
iframe.src = iframe.src;
}
module.exports = Prezi;
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;
});
}
}

View File

@ -1,37 +1,33 @@
/* global PreziPlayer */
/* 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.CURRENT_STEP = 'currentStep';
PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
PreziPlayer.CURRENT_OBJECT = 'currentObject';
PreziPlayer.STATUS_LOADING = 'loading';
PreziPlayer.STATUS_READY = 'ready';
PreziPlayer.STATUS_CONTENT_READY = 'contentready';
PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
PreziPlayer.EVENT_STATUS = "statusChange";
PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
PreziPlayer.domain = "https://prezi.com";
PreziPlayer.path = "/player/";
PreziPlayer.players = {};
PreziPlayer.binded_methods = ['changesHandler'];
PreziPlayer.API_VERSION = 1;
PreziPlayer.CURRENT_STEP = 'currentStep';
PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
PreziPlayer.CURRENT_OBJECT = 'currentObject';
PreziPlayer.STATUS_LOADING = 'loading';
PreziPlayer.STATUS_READY = 'ready';
PreziPlayer.STATUS_CONTENT_READY = 'contentready';
PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
PreziPlayer.EVENT_STATUS = "statusChange";
PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
PreziPlayer.domain = "https://prezi.com";
PreziPlayer.path = "/player/";
PreziPlayer.players = {};
PreziPlayer.binded_methods = ['changesHandler'];
PreziPlayer.createMultiplePlayers = function(optionArray){
PreziPlayer.createMultiplePlayers = function(optionArray){
for(var i=0; i<optionArray.length; i++) {
var optionSet = optionArray[i];
new PreziPlayer(optionSet.id, optionSet);
}
};
};
PreziPlayer.messageReceived = function(event){
PreziPlayer.messageReceived = function(event){
var message, item, player;
try {
message = JSON.parse(event.data);
@ -51,11 +47,11 @@
}
}
} catch (e) { }
};
};
/*jshint -W004 */
function PreziPlayer(id, options) {
/*jshint +W004 */
function PreziPlayer(id, options) {
/*jshint +W004 */
var params, paramString = "", _this = this;
if (PreziPlayer.players[id]){
PreziPlayer.players[id].destroy();
@ -106,9 +102,9 @@
_this.sendMessage({'action': 'init'});
}, 500);
PreziPlayer.players[id] = this;
}
}
PreziPlayer.prototype.changesHandler = function(message) {
PreziPlayer.prototype.changesHandler = function(message) {
var key, value, j, item;
if (this.initPollInterval) {
clearInterval(this.initPollInterval);
@ -126,43 +122,43 @@
}
}
}
};
};
PreziPlayer.prototype.destroy = function() {
PreziPlayer.prototype.destroy = function() {
if (this.initPollInterval) {
clearInterval(this.initPollInterval);
this.initPollInterval = false;
}
this.embedTo.innerHTML = '';
};
};
PreziPlayer.prototype.sendMessage = function(message) {
PreziPlayer.prototype.sendMessage = function(message) {
if (this.options.debug === true) {
if (console && console.log) console.log('sent', message);
}
message.version = PreziPlayer.API_VERSION;
message.id = this.id;
return this.iframe.contentWindow.postMessage(JSON.stringify(message), '*');
};
};
PreziPlayer.prototype.nextStep = /* nextStep is DEPRECATED */
PreziPlayer.prototype.flyToNextStep = function() {
PreziPlayer.prototype.nextStep = /* nextStep is DEPRECATED */
PreziPlayer.prototype.flyToNextStep = function() {
return this.sendMessage({
'action': 'present',
'data': ['moveToNextStep']
});
};
};
PreziPlayer.prototype.previousStep = /* previousStep is DEPRECATED */
PreziPlayer.prototype.flyToPreviousStep = function() {
PreziPlayer.prototype.previousStep = /* previousStep is DEPRECATED */
PreziPlayer.prototype.flyToPreviousStep = function() {
return this.sendMessage({
'action': 'present',
'data': ['moveToPrevStep']
});
};
};
PreziPlayer.prototype.toStep = /* toStep is DEPRECATED */
PreziPlayer.prototype.flyToStep = function(step, animation_step) {
PreziPlayer.prototype.toStep = /* toStep is DEPRECATED */
PreziPlayer.prototype.flyToStep = function(step, animation_step) {
var obj = this;
// check animation_step
if (animation_step > 0 &&
@ -186,90 +182,90 @@
'action': 'present',
'data': ['moveToStep', step]
});
};
};
PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
PreziPlayer.prototype.flyToObject = function(objectId) {
PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
PreziPlayer.prototype.flyToObject = function(objectId) {
return this.sendMessage({
'action': 'present',
'data': ['moveToObject', objectId]
});
};
};
PreziPlayer.prototype.play = function(defaultDelay) {
PreziPlayer.prototype.play = function(defaultDelay) {
return this.sendMessage({
'action': 'present',
'data': ['startAutoPlay', defaultDelay]
});
};
};
PreziPlayer.prototype.stop = function() {
PreziPlayer.prototype.stop = function() {
return this.sendMessage({
'action': 'present',
'data': ['stopAutoPlay']
});
};
};
PreziPlayer.prototype.pause = function(defaultDelay) {
PreziPlayer.prototype.pause = function(defaultDelay) {
return this.sendMessage({
'action': 'present',
'data': ['pauseAutoPlay', defaultDelay]
});
};
};
PreziPlayer.prototype.getCurrentStep = function() {
PreziPlayer.prototype.getCurrentStep = function() {
return this.values.currentStep;
};
};
PreziPlayer.prototype.getCurrentAnimationStep = function() {
PreziPlayer.prototype.getCurrentAnimationStep = function() {
return this.values.currentAnimationStep;
};
};
PreziPlayer.prototype.getCurrentObject = function() {
PreziPlayer.prototype.getCurrentObject = function() {
return this.values.currentObject;
};
};
PreziPlayer.prototype.getStatus = function() {
PreziPlayer.prototype.getStatus = function() {
return this.values.status;
};
};
PreziPlayer.prototype.isPlaying = function() {
PreziPlayer.prototype.isPlaying = function() {
return this.values.isAutoPlaying;
};
};
PreziPlayer.prototype.getStepCount = function() {
PreziPlayer.prototype.getStepCount = function() {
return this.values.stepCount;
};
};
PreziPlayer.prototype.getAnimationCountOnSteps = function() {
PreziPlayer.prototype.getAnimationCountOnSteps = function() {
return this.values.animationCountOnSteps;
};
};
PreziPlayer.prototype.getTitle = function() {
PreziPlayer.prototype.getTitle = function() {
return this.values.title;
};
};
PreziPlayer.prototype.setDimensions = function(dims) {
PreziPlayer.prototype.setDimensions = function(dims) {
for (var parameter in dims) {
this.iframe[parameter] = dims[parameter];
}
};
};
PreziPlayer.prototype.getDimensions = function() {
PreziPlayer.prototype.getDimensions = function() {
return {
width: parseInt(this.iframe.width, 10),
height: parseInt(this.iframe.height, 10)
};
};
};
PreziPlayer.prototype.on = function(event, callback) {
PreziPlayer.prototype.on = function(event, callback) {
this.callbacks.push({
event: event,
callback: callback
});
};
};
PreziPlayer.prototype.off = function(event, callback) {
PreziPlayer.prototype.off = function(event, callback) {
var j, item;
if (event === undefined) {
this.callbacks = [];
@ -281,18 +277,14 @@
this.callbacks.splice(j, 1);
}
}
};
};
if (window.addEventListener) {
if (window.addEventListener) {
window.addEventListener('message', PreziPlayer.messageReceived, false);
} else {
} else {
window.attachEvent('onmessage', PreziPlayer.messageReceived);
}
}
return PreziPlayer;
window.PreziPlayer = PreziPlayer;
})();
})();
module.exports = PreziPlayer;
export default PreziPlayer;

View File

@ -6,7 +6,6 @@ import SettingsMenu from "./settings/SettingsMenu";
import VideoLayout from "../videolayout/VideoLayout";
import ToolbarToggler from "../toolbars/ToolbarToggler";
import UIUtil from "../util/UIUtil";
import LargeVideo from "../videolayout/LargeVideo";
const buttons = {
'#chatspace': '#chatBottomButton',
@ -47,7 +46,7 @@ function toggle (object, selector, onOpenComplete, onOpen, onClose) {
} else {
// Undock the toolbar when the chat is shown and if we're in a
// video mode.
if (LargeVideo.isLargeVideoVisible()) {
if (VideoLayout.isLargeVideoVisible()) {
ToolbarToggler.dockToolbar(false);
}
@ -62,7 +61,7 @@ function toggle (object, selector, onOpenComplete, onOpen, onClose) {
}
$("#toast-container").animate({
right: (PanelToggler.getPanelSize()[0] + 5)
right: (UIUtil.getSidePanelSize()[0] + 5)
}, {
queue: false,
duration: 500
@ -116,7 +115,7 @@ var PanelToggler = {
},
resizeChat () {
let [width, height] = this.getPanelSize();
let [width, height] = UIUtil.getSidePanelSize();
Chat.resizeChat(width, height);
},
@ -156,21 +155,6 @@ var PanelToggler = {
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 () {
return (Chat.isVisible() ||
ContactList.isVisible() ||

View File

@ -9,13 +9,6 @@ const defaultBottomToolbarButtons = {
'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 = {
init (emitter) {
UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
@ -42,6 +35,13 @@ const BottomToolbar = {
toggleFilmStrip () {
$("#remoteVideos").toggleClass("hidden");
},
onRemoteVideoResized (width, height) {
let toolbar = $('#bottomToolbar');
let bottom = (height - toolbar.outerHeight())/2 + 18;
toolbar.css({bottom});
}
};

View File

@ -250,7 +250,7 @@ const Toolbar = {
* Disables and enables some of the buttons.
*/
setupButtonsFromConfig () {
if (UIUtil.isButtonEnabled('prezi')) {
if (!UIUtil.isButtonEnabled('prezi')) {
$("#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.
showRecordingButton (show) {
if (UIUtil.isButtonEnabled('recording') && show) {

View File

@ -1,19 +1,34 @@
/* global $, config, interfaceConfig */
import PanelToggler from "../side_pannels/SidePanelToggler";
/**
* Created by hristo on 12/22/14.
*/
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.
*/
getAvailableVideoWidth: function (isVisible) {
if(typeof isVisible === "undefined" || isVisible === null)
isVisible = PanelToggler.isVisible();
var rightPanelWidth
= isVisible ? PanelToggler.getPanelSize()[0] : 0;
getAvailableVideoWidth: function (isSidePanelVisible) {
let rightPanelWidth = 0;
if (isSidePanelVisible) {
rightPanelWidth = UIUtil.getSidePanelSize()[0];
}
return window.innerWidth - rightPanelWidth;
},
@ -118,6 +133,12 @@ import PanelToggler from "../side_pannels/SidePanelToggler";
redirect (url) {
window.location.href = url;
},
isFullScreen () {
return document.fullScreen
|| document.mozFullScreen
|| document.webkitIsFullScreen;
}
};

View File

@ -0,0 +1,24 @@
export default class LargeContainer {
/**
* @returns Promise
*/
show () {
}
/**
* @returns Promise
*/
hide () {
}
resize (containerWidth, containerHeight, animate) {
}
onHoverIn (e) {
}
onHoverOut (e) {
}
}

View File

@ -1,98 +1,20 @@
/* global $, APP, interfaceConfig */
/* jshint -W101 */
import Avatar from "../avatar/Avatar";
import ToolbarToggler from "../toolbars/ToolbarToggler";
import UIUtil from "../util/UIUtil";
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
//var video = $('#largeVideo');
const avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
var currentVideoWidth = null;
var currentVideoHeight = null;
// By default we use camera
var getVideoSize = getCameraVideoSize;
var getVideoPosition = getCameraVideoPosition;
/**
* The small video instance that is displayed in the large video
* @type {SmallVideo}
*/
var currentSmallVideo = null;
/**
* 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
});
function getStreamId(stream) {
if (stream.isLocal()) {
return APP.conference.localId;
} else {
video.width(width);
video.height(height);
video.css({
top: verticalIndent,
bottom: verticalIndent,
left: horizontalIndent,
right: horizontalIndent
});
return stream.getParticipantId();
}
}
/**
@ -106,80 +28,28 @@ function getDesktopVideoSize(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
if (!videoWidth)
videoWidth = currentVideoWidth;
if (!videoHeight)
videoHeight = currentVideoHeight;
var aspectRatio = videoWidth / videoHeight;
let aspectRatio = videoWidth / videoHeight;
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
let availableWidth = Math.max(videoWidth, videoSpaceWidth);
let availableHeight = Math.max(videoHeight, videoSpaceHeight);
var filmstrip = $("#remoteVideos");
let filmstrip = $("#remoteVideos");
if (!filmstrip.hasClass("hidden"))
videoSpaceHeight -= filmstrip.outerHeight();
if (availableWidth / aspectRatio >= videoSpaceHeight)
{
if (availableWidth / aspectRatio >= videoSpaceHeight) {
availableHeight = videoSpaceHeight;
availableWidth = availableHeight * aspectRatio;
}
if (availableHeight * aspectRatio >= videoSpaceWidth)
{
if (availableHeight * aspectRatio >= videoSpaceWidth) {
availableWidth = videoSpaceWidth;
availableHeight = availableWidth / aspectRatio;
}
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];
return { availableWidth, availableHeight };
}
@ -199,15 +69,10 @@ function getCameraVideoSize(videoWidth,
videoSpaceWidth,
videoSpaceHeight) {
if (!videoWidth)
videoWidth = currentVideoWidth;
if (!videoHeight)
videoHeight = currentVideoHeight;
let aspectRatio = videoWidth / videoHeight;
var aspectRatio = videoWidth / videoHeight;
var availableWidth = videoWidth;
var availableHeight = videoHeight;
let availableWidth = videoWidth;
let availableHeight = videoHeight;
if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
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() {
let avatar = $("#activeSpeakerAvatar");
let id = currentSmallVideo.id;
let url = Avatar.getActiveSpeakerUrl(id);
if (id && avatar.attr('src') !== url) {
avatar.attr('src', url);
currentSmallVideo.showAvatar();
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.
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.
* @param isVisible
* 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 changeVideo(isVisible) {
function getDesktopVideoPosition(videoWidth,
videoHeight,
videoSpaceWidth,
videoSpaceHeight) {
if (!currentSmallVideo) {
console.error("Unable to change large video - no 'currentSmallVideo'");
return;
}
let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
updateActiveSpeakerAvatarSrc();
let largeVideoElement = $('#largeVideo');
let verticalIndent = 0;// Top aligned
currentSmallVideo.stream.attach(largeVideoElement);
let flipX = currentSmallVideo.flipX;
largeVideoElement.css({
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);
}
return { horizontalIndent, verticalIndent };
}
/**
* Creates the html elements for the large video.
*/
function createLargeVideoHTML()
{
var html = '<div id="largeVideoContainer" class="videocontainer">';
html += '<div id="presentation"></div>' +
'<div id="etherpad"></div>' +
'<a target="_new"><div class="watermark leftwatermark"></div></a>' +
'<a target="_new"><div class="watermark rightwatermark"></div></a>' +
'<a class="poweredby" href="http://jitsi.org" target="_new" >' +
'<span data-i18n="poweredby"></span> jitsi.org' +
'</a>'+
'<div id="activeSpeaker">' +
'<img id="activeSpeakerAvatar" src=""/>' +
'<canvas id="activeSpeakerAudioLevel"></canvas>' +
'</div>' +
'<div id="largeVideoWrapper">' +
'<video id="largeVideo" muted="true"' +
'autoplay oncontextmenu="return false;"></video>' +
'</div id="largeVideoWrapper">' +
'<span id="videoConnectionMessage"></span>';
html += '</div>';
$(html).prependTo("#videospace");
export const VideoContainerType = "video";
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
var leftWatermarkDiv
= $("#largeVideoContainer div[class='watermark leftwatermark']");
leftWatermarkDiv.css({display: 'block'});
leftWatermarkDiv.parent().get(0).href
= interfaceConfig.JITSI_WATERMARK_LINK;
class VideoContainer extends LargeContainer {
// FIXME: With Temasys we have to re-select everytime
get $video () {
return $('#largeVideo');
}
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)";
get id () {
if (this.stream) {
return getStreamId(this.stream);
}
}
if (interfaceConfig.SHOW_POWERED_BY) {
$("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
}
constructor (onPlay) {
super();
this.stream = null;
this.$avatar = $('#activeSpeaker');
this.$wrapper = $('#largeVideoWrapper');
if (!RTCBrowserType.isIExplorer()) {
$('#largeVideo').volume = 0;
this.$video.volume = 0;
}
}
var LargeVideo = {
this.$video.on('play', onPlay);
}
init: function (VideoLayout, emitter) {
if(!isEnabled)
return;
createLargeVideoHTML();
this.VideoLayout = VideoLayout;
this.eventEmitter = emitter;
this.eventEmitter.emit(UIEvents.LARGEVIDEO_INIT);
var self = this;
// Listen for large video size updates
var largeVideo = $('#largeVideo')[0];
var onplaying = function (arg1, arg2, arg3) {
// re-select
if (RTCBrowserType.isTemasysPluginUsed())
largeVideo = $('#largeVideo')[0];
currentVideoWidth = largeVideo.videoWidth;
currentVideoHeight = largeVideo.videoHeight;
self.position(currentVideoWidth, currentVideoHeight);
getStreamSize () {
let video = this.$video[0];
return {
width: video.videoWidth,
height: video.videoHeight
};
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) {
$('#activeSpeaker').css('visibility', 'hidden');
let oldId = this.getId();
currentSmallVideo = newSmallVideo;
if (oldId !== id) {
// we want the notification to trigger even if id is undefined,
// or null.
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
getVideoSize (containerWidth, containerHeight) {
let { width, height } = this.getStreamSize();
if (this.stream && this.stream.isScreenSharing()) {
return getDesktopVideoSize(width, height, containerWidth, containerHeight);
} else {
return getCameraVideoSize(width, height, containerWidth, containerHeight);
}
}
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
// largeVideo, because when Temasys plugin is in use it replaces
// <video> elements with plugin <object> tag. In Safari jQuery is
// unable to store values on this plugin object which breaks all
// animation effects performed on it directly.
//
// If for any reason large video was hidden before calling fadeOut
// changeVideo will never be called, so we call show() in chain just
// to be sure
$('#largeVideoWrapper').show().fadeTo(300, 0,
changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
} else {
if (currentSmallVideo) {
currentSmallVideo.showAvatar();
}
}
},
/**
* Shows/hides the large video.
*/
setLargeVideoVisible: function(isVisible) {
if(!isEnabled)
return;
if (isVisible) {
$('#largeVideoWrapper').css({visibility: 'visible'});
show () {
let $wrapper = this.$wrapper;
return new Promise(resolve => {
$wrapper.fadeIn(300, function () {
$wrapper.css({visibility: 'visible'});
$('.watermark').css({visibility: 'visible'});
if(currentSmallVideo)
currentSmallVideo.enableDominantSpeaker(true);
});
resolve();
});
}
else {
$('#largeVideoWrapper').css({visibility: 'hidden'});
$('#activeSpeaker').css('visibility', 'hidden');
hide () {
this.showAvatar(false);
let $wrapper = this.$wrapper;
return new Promise(resolve => {
$wrapper.fadeOut(300, function () {
$wrapper.css({visibility: 'hidden'});
$('.watermark').css({visibility: 'hidden'});
if(currentSmallVideo)
currentSmallVideo.enableDominantSpeaker(false);
resolve();
});
});
}
},
onVideoTypeChanged: function (id, newVideoType) {
if (!isEnabled)
return;
if (LargeVideo.isCurrentlyOnLarge(id)) {
LargeVideo.updateVideoSizeAndPosition(newVideoType);
}
this.position(null, null, null, null, true);
export default class LargeVideoManager {
constructor () {
this.containers = {};
this.state = VideoContainerType;
this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
this.addContainer(VideoContainerType, this.videoContainer);
this.width = 0;
this.height = 0;
this.$container = $('#largeVideoContainer');
this.$container.css({
display: 'inline-block'
});
if (interfaceConfig.SHOW_JITSI_WATERMARK) {
let leftWatermarkDiv = this.$container.find("div.watermark.leftwatermark");
leftWatermarkDiv.css({display: 'block'});
leftWatermarkDiv.parent().attr('href', interfaceConfig.JITSI_WATERMARK_LINK);
}
},
/**
* Positions the large video.
*
* @param videoWidth the stream video width
* @param videoHeight the stream video height
*/
position: function (videoWidth, videoHeight,
videoSpaceWidth, videoSpaceHeight, animate) {
if(!isEnabled)
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;
if(!videoSpaceWidth)
videoSpaceWidth = $('#videospace').width();
if(!videoSpaceHeight)
videoSpaceHeight = window.innerHeight;
}
let container = this.getContainer(this.state);
container.onHoverIn(e);
}
var videoSize = getVideoSize(videoWidth,
videoHeight,
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)
onHoverOut (e) {
if (!this.state) {
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;
var top = availableHeight / 2 - avatarSize / 4 * 3;
$('#activeSpeaker').css('top', top);
updateLargeVideo (stream) {
let id = getStreamId(stream);
this.VideoLayout
.resizeVideoSpace(animate, isSideBarVisible, completeFunction);
if(animate) {
$('#largeVideoContainer').animate({
width: availableWidth,
height: availableHeight
},
{
let container = this.getContainer(this.state);
container.hide().then(() => {
console.info("hover in %s", id);
this.state = VideoContainerType;
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,
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.
*
* @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
*/
enableVideoProblemFilter: function (enable) {
$("#largeVideo").toggleClass("videoProblemFilter", enable);
enableVideoProblemFilter (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();
}
}

View File

@ -145,8 +145,8 @@ SmallVideo.prototype.bindHoverHandler = function () {
function () {
// If the video has been "pinned" by the user we want to
// keep the display name on place.
if (!LargeVideo.isLargeVideoVisible() ||
!LargeVideo.isCurrentlyOnLarge(self.id))
if (!self.VideoLayout.isLargeVideoVisible() ||
!self.VideoLayout.isCurrentlyOnLarge(self.id))
self.showDisplayName(false);
}
);
@ -254,7 +254,7 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable) {
}
if (isEnable) {
this.showDisplayName(LargeVideo.getState() === "video");
this.showDisplayName(this.VideoLayout.isLargeVideoVisible());
if (!this.container.classList.contains("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(video, false);
} else {

View File

@ -2,12 +2,14 @@
/* jshint -W101 */
import AudioLevels from "../audio_levels/AudioLevels";
import BottomToolbar from "../toolbars/BottomToolbar";
import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from "../util/UIUtil";
import RemoteVideo from "./RemoteVideo";
import LargeVideo from "./LargeVideo";
import LargeVideoManager, {VideoContainerType} from "./LargeVideo";
import {PreziContainerType} from '../prezi/Prezi';
import LocalVideo from "./LocalVideo";
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
@ -88,22 +90,31 @@ function getPeerContainerResourceId (containerElement) {
}
}
let largeVideo;
var VideoLayout = {
init (emitter) {
eventEmitter = emitter;
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
if (interfaceConfig.filmStripOnly) {
LargeVideo.disable();
} else {
LargeVideo.init(VideoLayout, emitter);
}
VideoLayout.resizeLargeVideoContainer();
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) {
return lastNCount < 0 || // lastN is disabled
// lastNEndpoints cache not built yet
@ -147,8 +158,8 @@ var VideoLayout = {
localVideoThumbnail.changeVideo(stream);
/* force update if we're currently being displayed */
if (LargeVideo.isCurrentlyOnLarge(localId)) {
LargeVideo.updateLargeVideo(localId, true);
if (this.isCurrentlyOnLarge(localId)) {
this.updateLargeVideo(localId, true);
}
},
@ -156,8 +167,8 @@ var VideoLayout = {
let id = APP.conference.localId;
localVideoThumbnail.joined(id);
if (!LargeVideo.id) {
LargeVideo.updateLargeVideo(id, true);
if (largeVideo && !largeVideo.id) {
this.updateLargeVideo(id, true);
}
},
@ -183,7 +194,7 @@ var VideoLayout = {
* another one instead.
*/
updateRemovedVideo (id) {
if (id !== LargeVideo.getId()) {
if (!this.isCurrentlyOnLarge(id)) {
return;
}
@ -198,7 +209,7 @@ var VideoLayout = {
newId = this.electLastVisibleVideo();
}
LargeVideo.updateLargeVideo(newId);
this.updateLargeVideo(newId);
},
electLastVisibleVideo () {
@ -242,10 +253,6 @@ var VideoLayout = {
remoteVideos[id].addRemoteStreamElement(stream);
},
getLargeVideoId () {
return LargeVideo.getId();
},
/**
* Return the type of the remote video.
* @param id the id for the remote video
@ -255,25 +262,6 @@ var VideoLayout = {
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,
resourceJid) {
if(focusedVideoResourceJid) {
@ -291,7 +279,7 @@ var VideoLayout = {
// Enable the currently set dominant speaker.
if (currentDominantSpeaker) {
if(smallVideo && smallVideo.hasVideo()) {
LargeVideo.updateLargeVideo(currentDominantSpeaker);
this.updateLargeVideo(currentDominantSpeaker);
}
}
@ -314,9 +302,7 @@ var VideoLayout = {
}
}
LargeVideo.setState("video");
LargeVideo.updateLargeVideo(resourceJid);
this.updateLargeVideo(resourceJid);
// Writing volume not allowed in IE
if (!RTCBrowserType.isIExplorer()) {
@ -370,11 +356,11 @@ var VideoLayout = {
// the current dominant speaker.
if ((!focusedVideoResourceJid &&
!currentDominantSpeaker &&
!require("../prezi/Prezi").isPresentationVisible()) ||
!this.isLargeContainerTypeVisible(PreziContainerType)) ||
focusedVideoResourceJid === resourceJid ||
(resourceJid &&
currentDominantSpeaker === resourceJid)) {
LargeVideo.updateLargeVideo(resourceJid, true);
this.updateLargeVideo(resourceJid, true);
}
},
@ -419,63 +405,44 @@ var VideoLayout = {
/**
* Resizes the large video container.
*/
resizeLargeVideoContainer () {
if(LargeVideo.isEnabled()) {
LargeVideo.resize();
resizeLargeVideoContainer (isSideBarVisible) {
if (largeVideo) {
largeVideo.updateContainerSize(isSideBarVisible);
largeVideo.resize(false);
} else {
VideoLayout.resizeVideoSpace();
this.resizeVideoSpace(false, isSideBarVisible);
}
VideoLayout.resizeThumbnails();
LargeVideo.position();
this.resizeThumbnails(false);
},
/**
* Resizes thumbnails.
*/
resizeThumbnails (animate) {
var videoSpaceWidth = $('#remoteVideos').width();
resizeThumbnails (animate = false) {
let videoSpaceWidth = $('#remoteVideos').width();
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var width = thumbnailSize[0];
var height = thumbnailSize[1];
let [width, height] = this.calculateThumbnailSize(videoSpaceWidth);
$('.userAvatar').css('left', (width - height) / 2);
if(animate) {
$('#remoteVideos').animate({
// adds 2 px because of small video 1px border
height: height + 2
},
{
}, {
queue: false,
duration: 500
duration: animate ? 500 : 0
});
$('#remoteVideos>span').animate({
height: height,
width: width
},
{
height, width
}, {
queue: false,
duration: 500,
duration: animate ? 500 : 0,
complete: function () {
$(document).trigger(
"remotevideo.resized",
[width,
height]);
BottomToolbar.onRemoteVideoResized(width, height);
AudioLevels.onRemoteVideoResized(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,
// otherwise wait for the "videoactive.jingle" event.
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
// user.
if (!updateLargeVideo &&
resourceJid === LargeVideo.getId()) {
this.isCurrentlyOnLarge(resourceJid)) {
updateLargeVideo = true;
}
}
@ -754,7 +721,7 @@ var VideoLayout = {
continue;
// videoSrcToSsrc needs to be update for this call to succeed.
LargeVideo.updateLargeVideo(resource);
this.updateLargeVideo(resource);
break;
}
}
@ -862,15 +829,9 @@ var VideoLayout = {
}
smallVideo.setVideoType(newVideoType);
LargeVideo.onVideoTypeChanged(id, newVideoType);
},
/**
* Updates the video size and position.
*/
updateLargeVideoSize () {
LargeVideo.updateVideoSizeAndPosition();
LargeVideo.position(null, null, null, null, true);
if (this.isCurrentlyOnLarge(id)) {
this.updateLargeVideo(id, true);
}
},
showMore (jid) {
@ -886,22 +847,8 @@ var VideoLayout = {
}
},
addPreziContainer (id) {
var container = 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;
}
addRemoteVideoContainer (id) {
return RemoteVideo.createContainer(id);
},
/**
@ -912,8 +859,15 @@ var VideoLayout = {
* resized.
*/
resizeVideoArea (isSideBarVisible, callback) {
LargeVideo.resizeVideoAreaAnimated(isSideBarVisible, callback);
VideoLayout.resizeThumbnails(true);
let animate = 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
});
} else {
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
$('#videospace').width(availableWidth).height(availableHeight);
}
},
@ -968,43 +921,105 @@ var VideoLayout = {
"Missed avatar update - no small video yet for " + id
);
}
LargeVideo.updateAvatar(id, 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);
if (this.isCurrentlyOnLarge(id)) {
largeVideo.updateAvatar(thumbUrl);
}
},
/**
* Indicates that the video has been interrupted.
*/
onVideoInterrupted () {
LargeVideo.enableVideoProblemFilter(true);
var reconnectingKey = "connection.RECONNECTING";
$('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
this.enableVideoProblemFilter(true);
let reconnectingKey = "connection.RECONNECTING";
$('#videoConnectionMessage')
.text(APP.translation.translateString(reconnectingKey));
$('#videoConnectionMessage').css({display: "block"});
.attr("data-i18n", reconnectingKey)
.text(APP.translation.translateString(reconnectingKey))
.css({display: "block"});
},
/**
* Indicates that the video has been restored.
*/
onVideoRestored () {
LargeVideo.enableVideoProblemFilter(false);
this.enableVideoProblemFilter(false);
$('#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;
}
};

View File

@ -2,7 +2,6 @@ export default {
NICKNAME_CHANGED: "UI.nickname_changed",
SELECTED_ENDPOINT: "UI.selected_endpoint",
PINNED_ENDPOINT: "UI.pinned_endpoint",
LARGEVIDEO_INIT: "UI.largevideo_init",
/**
* Notifies that local user created text message.
*/
@ -22,6 +21,9 @@ export default {
AUDIO_MUTED: "UI.audio_muted",
VIDEO_MUTED: "UI.video_muted",
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",
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
USER_INVITED: "UI.user_invited",