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",
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
|
||||
#remoteVideos .videocontainer {
|
||||
display: inline-block;
|
||||
display: none;
|
||||
background-color: black;
|
||||
background-size: contain;
|
||||
border-radius:8px;
|
||||
|
|
19
index.html
19
index.html
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* Opens/hides the Etherpad.
|
||||
*/
|
||||
toggleEtherpad: function (isPresentation) {
|
||||
if (!etherpadIFrame)
|
||||
createIFrame();
|
||||
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);
|
||||
|
||||
if(VideoLayout.getLargeVideoState() === "etherpad")
|
||||
{
|
||||
VideoLayout.setLargeVideoState("video");
|
||||
}
|
||||
else
|
||||
{
|
||||
VideoLayout.setLargeVideoState("etherpad");
|
||||
}
|
||||
resize();
|
||||
}
|
||||
const inner = doc.getElementsByName("ace_inner")[0];
|
||||
bubbleIframeMouseMove(inner);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
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 */
|
||||
/* 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);
|
||||
}
|
||||
return availableWidth;
|
||||
function isPreziLink(url) {
|
||||
if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presentation height.
|
||||
*/
|
||||
function getPresentationHeihgt() {
|
||||
var remoteVideos = $('#remoteVideos');
|
||||
return window.innerHeight - remoteVideos.outerHeight();
|
||||
let presId = url.substring(url.indexOf("prezi.com/") + 10);
|
||||
if (!isAlphanumeric(presId) || presId.indexOf('/') < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the presentation iframe.
|
||||
*/
|
||||
function resize() {
|
||||
if ($('#presentation>iframe')) {
|
||||
$('#presentation>iframe').width(getPresentationWidth());
|
||||
$('#presentation>iframe').height(getPresentationHeihgt());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Presentation has been removed.
|
||||
*/
|
||||
$(document).bind('presentationremoved.muc', presentationRemoved);
|
||||
function notifyOtherIsSharingPrezi() {
|
||||
messageHandler.openMessageDialog(
|
||||
"dialog.sharePreziTitle",
|
||||
"dialog.sharePreziMsg"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Presentation has been added.
|
||||
*/
|
||||
$(document).bind('presentationadded.muc', presentationAdded);
|
||||
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();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* 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();
|
||||
for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
|
||||
preziPlayer.flyToStep(current, i);
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(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 */
|
||||
(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';
|
||||
|
@ -289,10 +285,6 @@
|
|||
window.attachEvent('onmessage', PreziPlayer.messageReceived);
|
||||
}
|
||||
|
||||
return PreziPlayer;
|
||||
window.PreziPlayer = PreziPlayer;
|
||||
|
||||
})();
|
||||
|
||||
})();
|
||||
|
||||
module.exports = PreziPlayer;
|
||||
export default PreziPlayer;
|
||||
|
|
|
@ -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() ||
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
/* 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;
|
||||
|
||||
let verticalIndent = 0;// Top aligned
|
||||
|
||||
return { horizontalIndent, verticalIndent };
|
||||
}
|
||||
|
||||
updateActiveSpeakerAvatarSrc();
|
||||
let largeVideoElement = $('#largeVideo');
|
||||
export const VideoContainerType = "video";
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
get id () {
|
||||
if (this.stream) {
|
||||
return getStreamId(this.stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
constructor (onPlay) {
|
||||
super();
|
||||
this.stream = null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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'});
|
||||
}
|
||||
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);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 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)
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue