Ver código fonte

refactoring of Etherpad and Prezi

master
isymchych 9 anos atrás
pai
commit
0fd0f5b633

+ 43
- 8
app.js Ver arquivo

41
     CONNECTION_QUALITY: "connectionQuality",
41
     CONNECTION_QUALITY: "connectionQuality",
42
     EMAIL: "email",
42
     EMAIL: "email",
43
     VIDEO_TYPE: "videoType"
43
     VIDEO_TYPE: "videoType"
44
+    ETHERPAD: "etherpad",
45
+    PREZI: "prezi",
46
+    STOP_PREZI: "stop-prezi"
44
 };
47
 };
45
 
48
 
46
 function buildRoomName () {
49
 function buildRoomName () {
218
 
221
 
219
 
222
 
220
     room.on(ConferenceEvents.USER_JOINED, function (id, user) {
223
     room.on(ConferenceEvents.USER_JOINED, function (id, user) {
221
-        if (APP.conference.isLocalId(id)) {
222
-            return;
223
-        }
224
-        console.error('USER %s connnected', id);
224
+        console.error('USER %s connnected', id, user);
225
         // FIXME email???
225
         // FIXME email???
226
         APP.UI.addUser(id, user.getDisplayName());
226
         APP.UI.addUser(id, user.getDisplayName());
227
     });
227
     });
228
     room.on(ConferenceEvents.USER_LEFT, function (id, user) {
228
     room.on(ConferenceEvents.USER_LEFT, function (id, user) {
229
-        console.error('USER LEFT', id);
229
+        console.error('USER %s LEFT', id, user);
230
         APP.UI.removeUser(id, user.getDisplayName());
230
         APP.UI.removeUser(id, user.getDisplayName());
231
+        APP.UI.stopPrezi(id);
231
     });
232
     });
232
 
233
 
233
 
234
 
348
         room.removeCommand(Commands.CONNECTION_QUALITY);
349
         room.removeCommand(Commands.CONNECTION_QUALITY);
349
     });
350
     });
350
     // listen to remote stats
351
     // listen to remote stats
351
-    room.addCommandListener(Commands.CONNECTION_QUALITY, function (data) {
352
-        APP.connectionquality.updateRemoteStats(data.attributes.id, data.value);
353
-    });
352
+    room.addCommandListener(
353
+        Commands.CONNECTION_QUALITY,
354
+        function ({value, attributes}) {
355
+            APP.connectionquality.updateRemoteStats(attributes.id, value);
356
+        }
357
+    );
354
     APP.connectionquality.addListener(
358
     APP.connectionquality.addListener(
355
         CQEvents.REMOTESTATS_UPDATED,
359
         CQEvents.REMOTESTATS_UPDATED,
356
         function (id, percent, stats) {
360
         function (id, percent, stats) {
358
         }
362
         }
359
     );
363
     );
360
 
364
 
365
+    room.addCommandListener(Commands.ETHERPAD, function ({value}) {
366
+        APP.UI.initEtherpad(value);
367
+    });
368
+
369
+
370
+    room.addCommandListener(Commands.PREZI, function ({value, attributes}) {
371
+        APP.UI.showPrezi(attributes.id, value, attributes.slide);
372
+    });
373
+    room.addCommandListener(Commands.STOP_PREZI, function ({attributes}) {
374
+        APP.UI.stopPrezi(attributes.id);
375
+    });
376
+    APP.UI.addListener(UIEvents.SHARE_PREZI, function (url, slide) {
377
+        console.log('Sharing Prezi %s slide %s', url, slide);
378
+        room.removeCommand(Commands.PREZI);
379
+        room.sendCommand(Commands.PREZI, {
380
+            value: url,
381
+            attributes: {
382
+                id: room.myUserId(),
383
+                slide
384
+            }
385
+        });
386
+    });
387
+    APP.UI.addListener(UIEvents.STOP_SHARING_PREZI, function () {
388
+        room.removeCommand(Commands.PREZI);
389
+        room.sendCommandOnce(Commands.STOP_PREZI, {
390
+            attributes: {
391
+                id: room.myUserId()
392
+            }
393
+        });
394
+    });
395
+
361
     room.addCommandListener(Commands.VIDEO_TYPE, (data, from) => {
396
     room.addCommandListener(Commands.VIDEO_TYPE, (data, from) => {
362
         APP.UI.onPeerVideoTypeChanged(from, data.value);
397
         APP.UI.onPeerVideoTypeChanged(from, data.value);
363
     });
398
     });

+ 2
- 2
css/videolayout_default.css Ver arquivo

34
 }
34
 }
35
 
35
 
36
 #remoteVideos .videocontainer {
36
 #remoteVideos .videocontainer {
37
-    display: inline-block;
37
+    display: none;
38
     background-color: black;
38
     background-color: black;
39
     background-size: contain;
39
     background-size: contain;
40
     border-radius:8px;
40
     border-radius:8px;
378
     padding-right:2px;
378
     padding-right:2px;
379
     height:38px;
379
     height:38px;
380
     width:auto;
380
     width:auto;
381
-    background-color: rgba(0,0,0,0.8); 
381
+    background-color: rgba(0,0,0,0.8);
382
     border: 1px solid rgba(256, 256, 256, 0.2);
382
     border: 1px solid rgba(256, 256, 256, 0.2);
383
     border-radius: 6px;
383
     border-radius: 6px;
384
     pointer-events: auto;
384
     pointer-events: auto;

+ 19
- 0
index.html Ver arquivo

140
         </div>
140
         </div>
141
         <div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
141
         <div id="reloadPresentation"><a id="reloadPresentationLink"><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
142
         <div id="videospace">
142
         <div id="videospace">
143
+
144
+            <div id="largeVideoContainer" class="videocontainer">
145
+                <div id="presentation"></div>
146
+                <div id="etherpad"></div>
147
+                <a target="_new"><div class="watermark leftwatermark"></div></a>
148
+                <a target="_new"><div class="watermark rightwatermark"></div></a>
149
+                <a class="poweredby" href="http://jitsi.org" target="_new">
150
+                    <span data-i18n="poweredby"></span> jitsi.org
151
+                </a>
152
+                <div id="activeSpeaker">
153
+                    <img id="activeSpeakerAvatar" src=""/>
154
+                    <canvas id="activeSpeakerAudioLevel"></canvas>
155
+                </div>
156
+                <div id="largeVideoWrapper">
157
+                    <video id="largeVideo" muted="true" autoplay oncontextmenu="return false;"></video>
158
+                </div>
159
+                <span id="videoConnectionMessage"></span>
160
+            </div>
161
+
143
             <div id="remoteVideos">
162
             <div id="remoteVideos">
144
                 <span id="localVideoContainer" class="videocontainer">
163
                 <span id="localVideoContainer" class="videocontainer">
145
                     <span id="localNick" class="nick"></span>
164
                     <span id="localNick" class="nick"></span>

+ 1096
- 812
libs/lib-jitsi-meet.js
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 36
- 24
modules/UI/UI.js Ver arquivo

2
 /* jshint -W101 */
2
 /* jshint -W101 */
3
 var UI = {};
3
 var UI = {};
4
 
4
 
5
-import AudioLevels from './audio_levels/AudioLevels';
6
 import Chat from "./side_pannels/chat/Chat";
5
 import Chat from "./side_pannels/chat/Chat";
7
 import Toolbar from "./toolbars/Toolbar";
6
 import Toolbar from "./toolbars/Toolbar";
8
 import ToolbarToggler from "./toolbars/ToolbarToggler";
7
 import ToolbarToggler from "./toolbars/ToolbarToggler";
12
 import PanelToggler from "./side_pannels/SidePanelToggler";
11
 import PanelToggler from "./side_pannels/SidePanelToggler";
13
 import UIUtil from "./util/UIUtil";
12
 import UIUtil from "./util/UIUtil";
14
 import UIEvents from "../../service/UI/UIEvents";
13
 import UIEvents from "../../service/UI/UIEvents";
14
+import PreziManager from './prezi/Prezi';
15
+import EtherpadManager from './etherpad/Etherpad';
15
 
16
 
16
 import VideoLayout from "./videolayout/VideoLayout";
17
 import VideoLayout from "./videolayout/VideoLayout";
17
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
18
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
18
 
19
 
19
-var Prezi = require("./prezi/Prezi");
20
-var Etherpad = require("./etherpad/Etherpad");
21
 var EventEmitter = require("events");
20
 var EventEmitter = require("events");
22
 var Settings = require("./../settings/Settings");
21
 var Settings = require("./../settings/Settings");
23
 UI.messageHandler = require("./util/MessageHandler");
22
 UI.messageHandler = require("./util/MessageHandler");
32
 var eventEmitter = new EventEmitter();
31
 var eventEmitter = new EventEmitter();
33
 UI.eventEmitter = eventEmitter;
32
 UI.eventEmitter = eventEmitter;
34
 
33
 
34
+let preziManager;
35
+let etherpadManager;
36
+
35
 function promptDisplayName() {
37
 function promptDisplayName() {
36
     let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
38
     let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
37
     let defaultNickMsg = APP.translation.translateString(
39
     let defaultNickMsg = APP.translation.translateString(
77
     );
79
     );
78
 }
80
 }
79
 
81
 
80
-function setupPrezi() {
81
-    $("#reloadPresentationLink").click(function() {
82
-        Prezi.reloadPresentation();
83
-    });
84
-}
85
-
86
 function setupChat() {
82
 function setupChat() {
87
     Chat.init(eventEmitter);
83
     Chat.init(eventEmitter);
88
     $("#toggle_smileys").click(function() {
84
     $("#toggle_smileys").click(function() {
189
 };
185
 };
190
 
186
 
191
 function registerListeners() {
187
 function registerListeners() {
192
-    UI.addListener(UIEvents.LARGEVIDEO_INIT, function () {
193
-        AudioLevels.init();
194
-    });
195
-
196
     UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
188
     UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
197
         UI.setUserAvatar(APP.conference.localId, email);
189
         UI.setUserAvatar(APP.conference.localId, email);
198
     });
190
     });
199
 
191
 
200
     UI.addListener(UIEvents.PREZI_CLICKED, function () {
192
     UI.addListener(UIEvents.PREZI_CLICKED, function () {
201
-        Prezi.openPreziDialog();
193
+        preziManager.handlePreziButtonClicked();
202
     });
194
     });
203
 
195
 
204
     UI.addListener(UIEvents.ETHERPAD_CLICKED, function () {
196
     UI.addListener(UIEvents.ETHERPAD_CLICKED, function () {
205
-        Etherpad.toggleEtherpad(0);
197
+        if (etherpadManager) {
198
+            etherpadManager.toggleEtherpad();
199
+        }
206
     });
200
     });
207
 
201
 
208
     UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
202
     UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
221
 function bindEvents() {
215
 function bindEvents() {
222
     function onResize() {
216
     function onResize() {
223
         PanelToggler.resizeChat();
217
         PanelToggler.resizeChat();
224
-        VideoLayout.resizeLargeVideoContainer();
218
+        VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
225
     }
219
     }
226
 
220
 
227
     // Resize and reposition videos in full screen mode.
221
     // Resize and reposition videos in full screen mode.
254
     registerListeners();
248
     registerListeners();
255
 
249
 
256
     VideoLayout.init(eventEmitter);
250
     VideoLayout.init(eventEmitter);
251
+    if (!interfaceConfig.filmStripOnly) {
252
+        VideoLayout.initLargeVideo(PanelToggler.isVisible());
253
+    }
254
+    VideoLayout.resizeLargeVideoContainer(PanelToggler.isVisible());
255
+
257
     ContactList.init(eventEmitter);
256
     ContactList.init(eventEmitter);
258
 
257
 
259
     bindEvents();
258
     bindEvents();
260
-    setupPrezi();
259
+    preziManager = new PreziManager(eventEmitter);
261
     if (!interfaceConfig.filmStripOnly) {
260
     if (!interfaceConfig.filmStripOnly) {
261
+
262
         $("#videospace").mousemove(function () {
262
         $("#videospace").mousemove(function () {
263
             return ToolbarToggler.showToolbar();
263
             return ToolbarToggler.showToolbar();
264
         });
264
         });
350
     Chat.setSubject(subject);
350
     Chat.setSubject(subject);
351
 };
351
 };
352
 
352
 
353
-function initEtherpad(name) {
354
-    Etherpad.init(name);
355
-}
353
+UI.initEtherpad = function (name) {
354
+    if (etherpadManager) {
355
+        return;
356
+    }
357
+    console.log('Etherpad is enabled');
358
+    etherpadManager = new EtherpadManager(config.etherpad_base, name);
359
+    Toolbar.showEtherpadButton();
360
+};
356
 
361
 
357
 UI.addUser = function (id, displayName) {
362
 UI.addUser = function (id, displayName) {
358
     ContactList.addContact(id);
363
     ContactList.addContact(id);
443
 
448
 
444
 UI.toggleFilmStrip = function () {
449
 UI.toggleFilmStrip = function () {
445
     BottomToolbar.toggleFilmStrip();
450
     BottomToolbar.toggleFilmStrip();
446
-    VideoLayout.updateLargeVideoSize();
447
 };
451
 };
448
 
452
 
449
 UI.toggleChat = function () {
453
 UI.toggleChat = function () {
592
 };
596
 };
593
 
597
 
594
 UI.setAudioLevel = function (id, lvl) {
598
 UI.setAudioLevel = function (id, lvl) {
595
-    AudioLevels.updateAudioLevel(
596
-        id, lvl, VideoLayout.getLargeVideoId()
597
-    );
599
+    VideoLayout.setAudioLevel(id, lvl);
598
 };
600
 };
599
 
601
 
600
 UI.updateDesktopSharingButtons = function () {
602
 UI.updateDesktopSharingButtons = function () {
750
     }
752
     }
751
 };
753
 };
752
 
754
 
755
+UI.showPrezi = function (userId, url, slide) {
756
+    preziManager.showPrezi(userId, url, slide);
757
+};
758
+
759
+UI.stopPrezi = function (userId) {
760
+  if (preziManager.isSharing(userId)) {
761
+      preziManager.removePrezi(userId);
762
+  }
763
+};
764
+
753
 module.exports = UI;
765
 module.exports = UI;

+ 27
- 27
modules/UI/audio_levels/AudioLevels.js Ver arquivo

112
     return videoSpanId;
112
     return videoSpanId;
113
 }
113
 }
114
 
114
 
115
-/**
116
- * Indicates that the remote video has been resized.
117
- */
118
-$(document).bind('remotevideo.resized', function (event, width, height) {
119
-    let resized = false;
120
-
121
-    $('#remoteVideos>span>canvas').each(function() {
122
-        let canvas = $(this).get(0);
123
-        if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
124
-            canvas.width = width + interfaceConfig.CANVAS_EXTRA;
125
-            resized = true;
126
-        }
127
-
128
-        if (canvas.height !== height + interfaceConfig.CANVAS_EXTRA) {
129
-            canvas.height = height + interfaceConfig.CANVAS_EXTRA;
130
-            resized = true;
131
-        }
132
-    });
133
-
134
-    if (resized) {
135
-        Object.keys(audioLevelCanvasCache).forEach(function (id) {
136
-            audioLevelCanvasCache[id].width = width + interfaceConfig.CANVAS_EXTRA;
137
-            audioLevelCanvasCache[id].height = height + interfaceConfig.CANVAS_EXTRA;
138
-        });
139
-    }
140
-});
141
-
142
 /**
115
 /**
143
  * The audio Levels plugin.
116
  * The audio Levels plugin.
144
  */
117
  */
248
 
221
 
249
         // Fill the shape.
222
         // Fill the shape.
250
         ASDrawContext.fill();
223
         ASDrawContext.fill();
224
+    },
225
+
226
+    /**
227
+     * Indicates that the remote video has been resized.
228
+     */
229
+    onRemoteVideoResized (width, height) {
230
+        let resized = false;
231
+
232
+        $('#remoteVideos>span>canvas').each(function() {
233
+            let canvas = $(this).get(0);
234
+            if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
235
+                canvas.width = width + interfaceConfig.CANVAS_EXTRA;
236
+                resized = true;
237
+            }
238
+
239
+            if (canvas.height !== height + interfaceConfig.CANVAS_EXTRA) {
240
+                canvas.height = height + interfaceConfig.CANVAS_EXTRA;
241
+                resized = true;
242
+            }
243
+        });
244
+
245
+        if (resized) {
246
+            Object.keys(audioLevelCanvasCache).forEach(function (id) {
247
+                audioLevelCanvasCache[id].width = width + interfaceConfig.CANVAS_EXTRA;
248
+                audioLevelCanvasCache[id].height = height + interfaceConfig.CANVAS_EXTRA;
249
+            });
250
+        }
251
     }
251
     }
252
 };
252
 };
253
 
253
 

+ 120
- 90
modules/UI/etherpad/Etherpad.js Ver arquivo

1
-/* global $, config,
2
-   setLargeVideoVisible, Util */
3
-
4
-var VideoLayout = require("../videolayout/VideoLayout");
5
-var Prezi = require("../prezi/Prezi");
6
-var UIUtil = require("../util/UIUtil");
7
-
8
-var etherpadName = null;
9
-var etherpadIFrame = null;
10
-var domain = null;
11
-var options = "?showControls=true&showChat=false&showLineNumbers=true" +
12
-    "&useMonospaceFont=false";
13
-
14
-
15
-/**
16
- * Resizes the etherpad.
17
- */
18
-function resize() {
19
-    if ($('#etherpad>iframe').length) {
20
-        var remoteVideos = $('#remoteVideos');
21
-        var availableHeight
22
-            = window.innerHeight - remoteVideos.outerHeight();
23
-        var availableWidth = UIUtil.getAvailableVideoWidth();
24
-
25
-        $('#etherpad>iframe').width(availableWidth);
26
-        $('#etherpad>iframe').height(availableHeight);
27
-    }
28
-}
1
+/* global $ */
29
 
2
 
30
-/**
31
- * Creates the Etherpad button and adds it to the toolbar.
32
- */
33
-function enableEtherpadButton() {
34
-    if (!$('#toolbar_button_etherpad').is(":visible"))
35
-        $('#toolbar_button_etherpad').css({display: 'inline-block'});
36
-}
3
+import VideoLayout from "../videolayout/VideoLayout";
4
+import LargeContainer from '../videolayout/LargeContainer';
5
+import UIUtil from "../util/UIUtil";
6
+import SidePanelToggler from "../side_pannels/SidePanelToggler";
37
 
7
 
38
-/**
39
- * Creates the IFrame for the etherpad.
40
- */
41
-function createIFrame() {
42
-    etherpadIFrame = VideoLayout.createEtherpadIframe(
43
-            domain + etherpadName + options, function() {
44
-
45
-                document.domain = document.domain;
46
-                bubbleIframeMouseMove(etherpadIFrame);
47
-                setTimeout(function() {
48
-                    // the iframes inside of the etherpad are
49
-                    // not yet loaded when the etherpad iframe is loaded
50
-                    var outer = etherpadIFrame.
51
-                        contentDocument.getElementsByName("ace_outer")[0];
52
-                    bubbleIframeMouseMove(outer);
53
-                    var inner = outer.
54
-                        contentDocument.getElementsByName("ace_inner")[0];
55
-                    bubbleIframeMouseMove(inner);
56
-                }, 2000);
57
-            });
58
-}
8
+const options = $.param({
9
+    showControns: true,
10
+    showChat: false,
11
+    showLineNumbers: true,
12
+    useMonospaceFont: false
13
+});
59
 
14
 
60
 function bubbleIframeMouseMove(iframe){
15
 function bubbleIframeMouseMove(iframe){
61
     var existingOnMouseMove = iframe.contentWindow.onmousemove;
16
     var existingOnMouseMove = iframe.contentWindow.onmousemove;
71
             e.detail,
26
             e.detail,
72
             e.screenX,
27
             e.screenX,
73
             e.screenY,
28
             e.screenY,
74
-                e.clientX + boundingClientRect.left,
75
-                e.clientY + boundingClientRect.top,
29
+            e.clientX + boundingClientRect.left,
30
+            e.clientY + boundingClientRect.top,
76
             e.ctrlKey,
31
             e.ctrlKey,
77
             e.altKey,
32
             e.altKey,
78
             e.shiftKey,
33
             e.shiftKey,
84
     };
39
     };
85
 }
40
 }
86
 
41
 
42
+const DEFAULT_WIDTH = 640;
43
+const DEFAULT_HEIGHT = 480;
44
+
45
+const EtherpadContainerType = "etherpad";
46
+
47
+class Etherpad extends LargeContainer {
48
+    constructor (domain, name) {
49
+        super();
50
+
51
+        const iframe = document.createElement('iframe');
52
+
53
+        iframe.src = domain + name + '?' + options;
54
+        iframe.frameBorder = 0;
55
+        iframe.scrolling = "no";
56
+        iframe.width = DEFAULT_WIDTH;
57
+        iframe.height = DEFAULT_HEIGHT;
58
+        iframe.setAttribute('style', 'visibility: hidden;');
59
+
60
+        this.container.appendChild(iframe);
61
+
62
+        iframe.onload = function() {
63
+            document.domain = document.domain;
64
+            bubbleIframeMouseMove(iframe);
65
+
66
+            setTimeout(function() {
67
+                const doc = iframe.contentDocument;
68
+
69
+                // the iframes inside of the etherpad are
70
+                // not yet loaded when the etherpad iframe is loaded
71
+                const outer = doc.getElementsByName("ace_outer")[0];
72
+                bubbleIframeMouseMove(outer);
73
+
74
+                const inner = doc.getElementsByName("ace_inner")[0];
75
+                bubbleIframeMouseMove(inner);
76
+            }, 2000);
77
+        };
78
+
79
+        this.iframe = iframe;
80
+    }
81
+
82
+    get isOpen () {
83
+        return !!this.iframe;
84
+    }
85
+
86
+    get container () {
87
+        return document.getElementById('etherpad');
88
+    }
89
+
90
+    resize (containerWidth, containerHeight, animate) {
91
+        let remoteVideos = $('#remoteVideos');
87
 
92
 
88
-var Etherpad = {
89
-    /**
90
-     * Initializes the etherpad.
91
-     */
92
-    init: function (name) {
93
+        let height = containerHeight - remoteVideos.outerHeight();
94
+        let width = containerWidth;
93
 
95
 
94
-        if (config.etherpad_base && !etherpadName && name) {
96
+        $(this.iframe).width(width).height(height);
97
+    }
95
 
98
 
96
-            domain = config.etherpad_base;
99
+    show () {
100
+        const $iframe = $(this.iframe);
101
+        const $container = $(this.container);
97
 
102
 
98
-            etherpadName = name;
103
+        return new Promise(resolve => {
104
+            $iframe.fadeIn(300, function () {
105
+                document.body.style.background = '#eeeeee';
106
+                $iframe.css({visibility: 'visible'});
107
+                $container.css({zIndex: 2});
108
+                resolve();
109
+            });
110
+        });
111
+    }
99
 
112
 
100
-            enableEtherpadButton();
113
+    hide () {
114
+        const $iframe = $(this.iframe);
115
+        const $container = $(this.container);
101
 
116
 
102
-            /**
103
-             * Resizes the etherpad, when the window is resized.
104
-             */
105
-            $(window).resize(function () {
106
-                resize();
117
+        return new Promise(resolve => {
118
+            $iframe.fadeOut(300, function () {
119
+                $iframe.css({visibility: 'hidden'});
120
+                $container.css({zIndex: 0});
121
+                resolve();
107
             });
122
             });
123
+        });
124
+    }
125
+}
126
+
127
+export default class EtherpadManager {
128
+    constructor (domain, name) {
129
+        if (!domain || !name) {
130
+            throw new Error("missing domain or name");
108
         }
131
         }
109
-    },
110
 
132
 
111
-    /**
112
-     * Opens/hides the Etherpad.
113
-     */
114
-    toggleEtherpad: function (isPresentation) {
115
-        if (!etherpadIFrame)
116
-            createIFrame();
133
+        this.domain = domain;
134
+        this.name = name;
135
+        this.etherpad = null;
136
+    }
117
 
137
 
138
+    get isOpen () {
139
+        return !!this.etherpad;
140
+    }
118
 
141
 
119
-        if(VideoLayout.getLargeVideoState() === "etherpad")
120
-        {
121
-            VideoLayout.setLargeVideoState("video");
122
-        }
123
-        else
124
-        {
125
-            VideoLayout.setLargeVideoState("etherpad");
126
-        }
127
-        resize();
142
+    openEtherpad () {
143
+        this.etherpad = new Etherpad(this.domain, this.name);
144
+        VideoLayout.addLargeVideoContainer(
145
+            EtherpadContainerType,
146
+            this.etherpad
147
+        );
128
     }
148
     }
129
-};
130
 
149
 
131
-module.exports = Etherpad;
150
+    toggleEtherpad () {
151
+        if (!this.isOpen) {
152
+            this.openEtherpad();
153
+        }
154
+
155
+        let isVisible = VideoLayout.isLargeContainerTypeVisible(
156
+            EtherpadContainerType
157
+        );
158
+
159
+        VideoLayout.showLargeVideoContainer(EtherpadContainerType, !isVisible);
160
+    }
161
+}

+ 321
- 289
modules/UI/prezi/Prezi.js Ver arquivo

1
 /* global $, APP */
1
 /* global $, APP */
2
 /* jshint -W101 */
2
 /* jshint -W101 */
3
-import UIUtil from "../util/UIUtil";
3
+
4
 import VideoLayout from "../videolayout/VideoLayout";
4
 import VideoLayout from "../videolayout/VideoLayout";
5
+import LargeContainer from '../videolayout/LargeContainer';
6
+import PreziPlayer from './PreziPlayer';
7
+import UIUtil from '../util/UIUtil';
8
+import UIEvents from '../../../service/UI/UIEvents';
9
+import messageHandler from '../util/MessageHandler';
10
+import ToolbarToggler from "../toolbars/ToolbarToggler";
11
+import SidePanelToggler from "../side_pannels/SidePanelToggler";
5
 
12
 
6
-var messageHandler = require("../util/MessageHandler");
7
-var PreziPlayer = require("./PreziPlayer");
13
+const defaultPreziLink = "http://prezi.com/wz7vhjycl7e6/my-prezi";
14
+const alphanumRegex = /^[a-z0-9-_\/&\?=;]+$/i;
15
+const aspectRatio = 16.0 / 9.0;
8
 
16
 
9
-var preziPlayer = null;
17
+const DEFAULT_WIDTH = 640;
18
+const DEFAULT_HEIGHT = 480;
10
 
19
 
20
+/**
21
+ * Indicates if the given string is an alphanumeric string.
22
+ * Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
23
+ * purpose of checking URIs.
24
+ */
25
+function isAlphanumeric(unsafeText) {
26
+    return alphanumRegex.test(unsafeText);
27
+}
11
 
28
 
12
 /**
29
 /**
13
- * Shows/hides a presentation.
30
+ * Returns the presentation id from the given url.
14
  */
31
  */
15
-function setPresentationVisible(visible) {
32
+function getPresentationId (url) {
33
+    let presId = url.substring(url.indexOf("prezi.com/") + 10);
34
+    return presId.substring(0, presId.indexOf('/'));
35
+}
16
 
36
 
17
-    if (visible) {
18
-        VideoLayout.setLargeVideoState("prezi");
37
+function isPreziLink(url) {
38
+    if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) {
39
+        return false;
19
     }
40
     }
20
-    else {
21
-        VideoLayout.setLargeVideoState("video");
41
+
42
+    let presId = url.substring(url.indexOf("prezi.com/") + 10);
43
+    if (!isAlphanumeric(presId) || presId.indexOf('/') < 2) {
44
+        return false;
22
     }
45
     }
23
-}
24
 
46
 
25
-var Prezi = {
47
+    return true;
48
+}
26
 
49
 
50
+function notifyOtherIsSharingPrezi() {
51
+    messageHandler.openMessageDialog(
52
+        "dialog.sharePreziTitle",
53
+        "dialog.sharePreziMsg"
54
+    );
55
+}
27
 
56
 
28
-    /**
29
-     * Reloads the current presentation.
30
-     */
31
-    reloadPresentation: function() {
32
-        var iframe = document.getElementById(preziPlayer.options.preziId);
33
-        iframe.src = iframe.src;
34
-    },
35
-
36
-    /**
37
-     * Returns <tt>true</tt> if the presentation is visible, <tt>false</tt> -
38
-     * otherwise.
39
-     */
40
-    isPresentationVisible: function () {
41
-        return ($('#presentation>iframe') != null
42
-                && $('#presentation>iframe').css('opacity') == 1);
43
-    },
44
-
45
-    /**
46
-     * Opens the Prezi dialog, from which the user could choose a presentation
47
-     * to load.
48
-     */
49
-    openPreziDialog: function() {
50
-        var myprezi = APP.xmpp.getPrezi();
51
-        if (myprezi) {
52
-            messageHandler.openTwoButtonDialog("dialog.removePreziTitle",
53
-                null,
54
-                "dialog.removePreziMsg",
55
-                null,
56
-                false,
57
-                "dialog.Remove",
58
-                function(e,v,m,f) {
59
-                    if(v) {
60
-                        APP.xmpp.removePreziFromPresence();
61
-                    }
62
-                }
63
-            );
64
-        }
65
-        else if (preziPlayer != null) {
66
-            messageHandler.openTwoButtonDialog("dialog.sharePreziTitle",
67
-                null, "dialog.sharePreziMsg",
68
-                null,
69
-                false,
70
-                "dialog.Ok",
71
-                function(e,v,m,f) {
72
-                    $.prompt.close();
57
+function proposeToClosePrezi() {
58
+    return new Promise(function (resolve, reject) {
59
+        messageHandler.openTwoButtonDialog(
60
+            "dialog.removePreziTitle",
61
+            null,
62
+            "dialog.removePreziMsg",
63
+            null,
64
+            false,
65
+            "dialog.Remove",
66
+            function(e,v,m,f) {
67
+                if (v) {
68
+                    resolve();
69
+                } else {
70
+                    reject();
73
                 }
71
                 }
74
-            );
75
-        }
76
-        else {
77
-            var html = APP.translation.generateTranslationHTML(
78
-                "dialog.sharePreziTitle");
79
-            var cancelButton = APP.translation.generateTranslationHTML(
80
-                "dialog.Cancel");
81
-            var shareButton = APP.translation.generateTranslationHTML(
82
-                "dialog.Share");
83
-            var backButton = APP.translation.generateTranslationHTML(
84
-                "dialog.Back");
85
-            var buttons = [];
86
-            var buttons1 = [];
87
-            // Cancel button to both states
88
-            buttons.push({title: cancelButton, value: false});
89
-            buttons1.push({title: cancelButton, value: false});
90
-            // Share button
91
-            buttons.push({title: shareButton, value: true});
92
-            // Back button
93
-            buttons1.push({title: backButton, value: true});
94
-            var linkError = APP.translation.generateTranslationHTML(
95
-                "dialog.preziLinkError");
96
-            var defaultUrl = APP.translation.translateString("defaultPreziLink",
97
-                {url: "http://prezi.com/wz7vhjycl7e6/my-prezi"});
98
-            var openPreziState = {
99
-                state0: {
100
-                    html:   '<h2>' + html + '</h2>' +
101
-                            '<input name="preziUrl" type="text" ' +
102
-                            'data-i18n="[placeholder]defaultPreziLink" data-i18n-options=\'' +
103
-                            JSON.stringify({"url": "http://prezi.com/wz7vhjycl7e6/my-prezi"}) +
104
-                            '\' placeholder="' + defaultUrl + '" autofocus>',
105
-                    persistent: false,
106
-                    buttons: buttons,
107
-                    focus: ':input:first',
108
-                    defaultButton: 0,
109
-                    submit: function (e, v, m, f) {
110
-                        e.preventDefault();
111
-                        if(v)
112
-                        {
113
-                            var preziUrl = f.preziUrl;
114
-
115
-                            if (preziUrl)
116
-                            {
117
-                                var urlValue
118
-                                    = encodeURI(UIUtil.escapeHtml(preziUrl));
119
-
120
-                                if (urlValue.indexOf('http://prezi.com/') != 0
121
-                                    && urlValue.indexOf('https://prezi.com/') != 0)
122
-                                {
123
-                                    $.prompt.goToState('state1');
124
-                                    return false;
125
-                                }
126
-                                else {
127
-                                    var presIdTmp = urlValue.substring(
128
-                                            urlValue.indexOf("prezi.com/") + 10);
129
-                                    if (!isAlphanumeric(presIdTmp)
130
-                                            || presIdTmp.indexOf('/') < 2) {
131
-                                        $.prompt.goToState('state1');
132
-                                        return false;
133
-                                    }
134
-                                    else {
135
-                                        APP.xmpp.addToPresence("prezi", urlValue);
136
-                                        $.prompt.close();
137
-                                    }
138
-                                }
139
-                            }
140
-                        }
141
-                        else
142
-                            $.prompt.close();
72
+            }
73
+        );
74
+
75
+    });
76
+}
77
+
78
+function requestPreziLink() {
79
+    const title = APP.translation.generateTranslationHTML("dialog.sharePreziTitle");
80
+    const cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
81
+    const shareButton = APP.translation.generateTranslationHTML("dialog.Share");
82
+    const backButton = APP.translation.generateTranslationHTML("dialog.Back");
83
+    const linkError = APP.translation.generateTranslationHTML("dialog.preziLinkError");
84
+    const i18nOptions = {url: defaultPreziLink};
85
+    const defaultUrl = APP.translation.translateString(
86
+        "defaultPreziLink", i18nOptions
87
+    );
88
+
89
+    return new Promise(function (resolve, reject) {
90
+        let dialog = messageHandler.openDialogWithStates({
91
+            state0: {
92
+                html:  `
93
+                    <h2>${title}</h2>
94
+                    <input name="preziUrl" type="text"
95
+                           data-i18n="[placeholder]defaultPreziLink"
96
+                           data-i18n-options="${JSON.stringify(i18nOptions)}"
97
+                           placeholder="${defaultUrl}" autofocus>`,
98
+                persistent: false,
99
+                buttons: [
100
+                    {title: cancelButton, value: false},
101
+                    {title: shareButton, value: true}
102
+                ],
103
+                focus: ':input:first',
104
+                defaultButton: 1,
105
+                submit: function (e, v, m, f) {
106
+                    e.preventDefault();
107
+                    if (!v) {
108
+                        reject('cancelled');
109
+                        dialog.close();
110
+                        return;
143
                     }
111
                     }
144
-                },
145
-                state1: {
146
-                    html:   '<h2>' + html + '</h2>' +
147
-                            linkError,
148
-                    persistent: false,
149
-                    buttons: buttons1,
150
-                    focus: ':input:first',
151
-                    defaultButton: 1,
152
-                    submit: function (e, v, m, f) {
153
-                        e.preventDefault();
154
-                        if (v === 0)
155
-                            $.prompt.close();
156
-                        else
157
-                            $.prompt.goToState('state0');
112
+
113
+                    let preziUrl = f.preziUrl;
114
+                    if (!preziUrl) {
115
+                        return;
158
                     }
116
                     }
159
-                }
160
-            };
161
-            messageHandler.openDialogWithStates(openPreziState);
162
-        }
163
-    }
164
 
117
 
165
-};
118
+                    let urlValue = encodeURI(UIUtil.escapeHtml(preziUrl));
166
 
119
 
167
-/**
168
- * A new presentation has been added.
169
- *
170
- * @param event the event indicating the add of a presentation
171
- * @param jid the jid from which the presentation was added
172
- * @param presUrl url of the presentation
173
- * @param currentSlide the current slide to which we should move
174
- */
175
-function presentationAdded(event, jid, presUrl, currentSlide) {
176
-    console.log("presentation added", presUrl);
120
+                    if (!isPreziLink(urlValue)) {
121
+                        dialog.goToState('state1');
122
+                        return false;
123
+                    }
177
 
124
 
178
-    var presId = getPresentationId(presUrl);
125
+                    resolve(urlValue);
126
+                    dialog.close();
127
+                }
128
+            },
129
+
130
+            state1: {
131
+                html: `<h2>${title}</h2> ${linkError}`,
132
+                persistent: false,
133
+                buttons: [
134
+                    {title: cancelButton, value: false},
135
+                    {title: backButton, value: true}
136
+                ],
137
+                focus: ':input:first',
138
+                defaultButton: 1,
139
+                submit: function (e, v, m, f) {
140
+                    e.preventDefault();
141
+                    if (v === 0) {
142
+                        reject();
143
+                        dialog.close();
144
+                    } else {
145
+                        dialog.goToState('state0');
146
+                    }
147
+                }
148
+            }
149
+        });
179
 
150
 
180
-    var elementId = 'participant_'
181
-        + Strophe.getResourceFromJid(jid)
182
-        + '_' + presId;
151
+    });
152
+}
183
 
153
 
184
-    VideoLayout.addPreziContainer(elementId);
154
+export const PreziContainerType = "prezi";
185
 
155
 
186
-    var controlsEnabled = false;
187
-    if (jid === APP.xmpp.myJid())
188
-        controlsEnabled = true;
156
+class PreziContainer extends LargeContainer {
189
 
157
 
190
-    setPresentationVisible(true);
191
-    VideoLayout.setLargeVideoHover(
192
-        function (event) {
193
-            if (Prezi.isPresentationVisible()) {
194
-                var reloadButtonRight = window.innerWidth
195
-                    - $('#presentation>iframe').offset().left
196
-                    - $('#presentation>iframe').width();
158
+    constructor ({preziId, isMy, slide, onSlideChanged}) {
159
+        super();
160
+        this.reloadBtn = $('#reloadPresentation');
197
 
161
 
198
-                $('#reloadPresentation').css({  right: reloadButtonRight,
199
-                    display:'inline-block'});
162
+        let preziPlayer = new PreziPlayer(
163
+            'presentation', {
164
+                preziId,
165
+                width: DEFAULT_WIDTH,
166
+                height: DEFAULT_HEIGHT,
167
+                controls: isMy,
168
+                debug: true
200
             }
169
             }
201
-        },
202
-        function (event) {
203
-            if (!Prezi.isPresentationVisible())
204
-                $('#reloadPresentation').css({display:'none'});
205
-            else {
206
-                var e = event.toElement || event.relatedTarget;
207
-
208
-                if (e && e.id != 'reloadPresentation' && e.id != 'header')
209
-                    $('#reloadPresentation').css({display:'none'});
170
+        );
171
+        this.preziPlayer = preziPlayer;
172
+        this.$iframe = $(preziPlayer.iframe);
173
+
174
+        this.$iframe.attr('id', preziId);
175
+
176
+        preziPlayer.on(PreziPlayer.EVENT_STATUS, function({value}) {
177
+            console.log("prezi status", value);
178
+            if (value == PreziPlayer.STATUS_CONTENT_READY && !isMy) {
179
+                preziPlayer.flyToStep(slide);
210
             }
180
             }
211
         });
181
         });
212
 
182
 
213
-    preziPlayer = new PreziPlayer(
214
-        'presentation',
215
-        {preziId: presId,
216
-            width: getPresentationWidth(),
217
-            height: getPresentationHeihgt(),
218
-            controls: controlsEnabled,
219
-            debug: true
183
+        preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function({value}) {
184
+            console.log("event value", value);
185
+            onSlideChanged(value);
220
         });
186
         });
187
+    }
188
+
189
+    goToSlide (slide) {
190
+        if (this.preziPlayer.getCurrentStep() === slide) {
191
+            return;
192
+        }
221
 
193
 
222
-    $('#presentation>iframe').attr('id', preziPlayer.options.preziId);
194
+        this.preziPlayer.flyToStep(slide);
223
 
195
 
224
-    preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
225
-        console.log("prezi status", event.value);
226
-        if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
227
-            if (jid != APP.xmpp.myJid())
228
-                preziPlayer.flyToStep(currentSlide);
196
+        let animationStepsArray = this.preziPlayer.getAnimationCountOnSteps();
197
+        if (!animationStepsArray) {
198
+            return;
229
         }
199
         }
230
-    });
231
 
200
 
232
-    preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
233
-        console.log("event value", event.value);
234
-        APP.xmpp.addToPresence("preziSlide", event.value);
235
-    });
201
+        for (var i = 0; i < parseInt(animationStepsArray[slide]); i += 1) {
202
+            this.preziPlayer.flyToStep(slide, i);
203
+        }
204
+    }
205
+
206
+    showReloadBtn (show) {
207
+        this.reloadBtn.css('display', show ? 'inline-block' : 'none');
208
+    }
209
+
210
+    show () {
211
+        return new Promise(resolve => {
212
+            this.$iframe.fadeIn(300, () => {
213
+                this.$iframe.css({opacity: 1});
214
+                ToolbarToggler.dockToolbar(true);
215
+                resolve();
216
+            });
217
+        });
218
+    }
219
+
220
+    hide () {
221
+        return new Promise(resolve => {
222
+            this.$iframe.fadeOut(300, () => {
223
+                this.$iframe.css({opacity: 0});
224
+                this.showReloadBtn(false);
225
+                ToolbarToggler.dockToolbar(false);
226
+                resolve();
227
+            });
228
+        });
229
+    }
230
+
231
+    onHoverIn () {
232
+        let rightOffset = window.innerWidth - this.$iframe.offset().left - this.$iframe.width();
233
+
234
+        this.showReloadBtn(true);
235
+        this.reloadBtn.css('right', rightOffset);
236
+    }
236
 
237
 
237
-    $("#" + elementId).css( 'background-image',
238
-        'url(../images/avatarprezi.png)');
239
-    $("#" + elementId).click(
240
-        function () {
241
-            setPresentationVisible(true);
238
+    onHoverOut (event) {
239
+        let e = event.toElement || event.relatedTarget;
240
+
241
+        if (e && e.id != 'reloadPresentation' && e.id != 'header') {
242
+            this.showReloadBtn(false);
242
         }
243
         }
243
-    );
244
-};
244
+    }
245
 
245
 
246
-/**
247
- * A presentation has been removed.
248
- *
249
- * @param event the event indicating the remove of a presentation
250
- * @param jid the jid for which the presentation was removed
251
- * @param the url of the presentation
252
- */
253
-function presentationRemoved(event, jid, presUrl) {
254
-    console.log('presentation removed', presUrl);
255
-    var presId = getPresentationId(presUrl);
256
-    setPresentationVisible(false);
257
-    $('#participant_'
258
-        + Strophe.getResourceFromJid(jid)
259
-        + '_' + presId).remove();
260
-    $('#presentation>iframe').remove();
261
-    if (preziPlayer != null) {
262
-        preziPlayer.destroy();
263
-        preziPlayer = null;
246
+    resize (containerWidth, containerHeight) {
247
+        let remoteVideos = $('#remoteVideos');
248
+        let height = containerHeight - remoteVideos.outerHeight();
249
+
250
+        let width = containerWidth;
251
+
252
+        if (height < width / aspectRatio) {
253
+            width = Math.floor(height * aspectRatio);
254
+        }
255
+
256
+        this.$iframe.width(width).height(height);
264
     }
257
     }
265
-};
266
 
258
 
267
-/**
268
- * Indicates if the given string is an alphanumeric string.
269
- * Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
270
- * purpose of checking URIs.
271
- */
272
-function isAlphanumeric(unsafeText) {
273
-    var regex = /^[a-z0-9-_\/&\?=;]+$/i;
274
-    return regex.test(unsafeText);
259
+    close () {
260
+        this.showReloadBtn(false);
261
+        this.preziPlayer.destroy();
262
+        this.$iframe.remove();
263
+    }
275
 }
264
 }
276
 
265
 
277
-/**
278
- * Returns the presentation id from the given url.
279
- */
280
-function getPresentationId (presUrl) {
281
-    var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
282
-    return presIdTmp.substring(0, presIdTmp.indexOf('/'));
283
-}
266
+export default class PreziManager {
267
+    constructor (emitter) {
268
+        this.emitter = emitter;
284
 
269
 
285
-/**
286
- * Returns the presentation width.
287
- */
288
-function getPresentationWidth() {
289
-    var availableWidth = UIUtil.getAvailableVideoWidth();
290
-    var availableHeight = getPresentationHeihgt();
270
+        this.userId = null;
271
+        this.url = null;
272
+        this.prezi = null;
291
 
273
 
292
-    var aspectRatio = 16.0 / 9.0;
293
-    if (availableHeight < availableWidth / aspectRatio) {
294
-        availableWidth = Math.floor(availableHeight * aspectRatio);
274
+        $("#reloadPresentationLink").click(this.reloadPresentation.bind(this));
295
     }
275
     }
296
-    return availableWidth;
297
-}
298
 
276
 
299
-/**
300
- * Returns the presentation height.
301
- */
302
-function getPresentationHeihgt() {
303
-    var remoteVideos = $('#remoteVideos');
304
-    return window.innerHeight - remoteVideos.outerHeight();
305
-}
277
+    get isPresenting () {
278
+        return !!this.userId;
279
+    }
306
 
280
 
307
-/**
308
- * Resizes the presentation iframe.
309
- */
310
-function resize() {
311
-    if ($('#presentation>iframe')) {
312
-        $('#presentation>iframe').width(getPresentationWidth());
313
-        $('#presentation>iframe').height(getPresentationHeihgt());
281
+    get isMyPrezi () {
282
+        return this.userId === APP.conference.localId;
314
     }
283
     }
315
-}
316
 
284
 
317
-/**
318
- * Presentation has been removed.
319
- */
320
-$(document).bind('presentationremoved.muc', presentationRemoved);
285
+    isSharing (id) {
286
+        return this.userId === id;
287
+    }
321
 
288
 
322
-/**
323
- * Presentation has been added.
324
- */
325
-$(document).bind('presentationadded.muc', presentationAdded);
289
+    handlePreziButtonClicked () {
290
+        if (!this.isPresenting) {
291
+            requestPreziLink().then(
292
+                url => this.emitter.emit(UIEvents.SHARE_PREZI, url, 0),
293
+                err => console.error('PREZI CANCELED', err)
294
+            );
295
+            return;
296
+        }
326
 
297
 
327
-/*
328
- * Indicates presentation slide change.
329
- */
330
-$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
331
-    if (preziPlayer && preziPlayer.getCurrentStep() != current) {
332
-        preziPlayer.flyToStep(current);
298
+        if (this.isMyPrezi) {
299
+            proposeToClosePrezi().then(() => this.emitter.emit(UIEvents.STOP_SHARING_PREZI));
300
+        } else {
301
+            notifyOtherIsSharingPrezi();
302
+        }
303
+    }
333
 
304
 
334
-        var animationStepsArray = preziPlayer.getAnimationCountOnSteps();
335
-        for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
336
-            preziPlayer.flyToStep(current, i);
305
+    reloadPresentation () {
306
+        if (!this.prezi) {
307
+            return;
308
+        }
309
+        let iframe = this.prezi.$iframe[0];
310
+        iframe.src = iframe.src;
311
+    }
312
+
313
+    showPrezi (id, url, slide) {
314
+        if (!this.isPresenting) {
315
+            this.createPrezi(id, url, slide);
316
+        }
317
+
318
+        if (this.userId === id && this.url === url) {
319
+            this.prezi.goToSlide(slide);
320
+        } else {
321
+            console.error(this.userId, id);
322
+            console.error(this.url, url);
323
+            throw new Error("unexpected presentation change");
337
         }
324
         }
338
     }
325
     }
339
-});
340
 
326
 
341
-$(window).resize(function () {
342
-    resize();
343
-});
327
+    createPrezi (id, url, slide) {
328
+        console.log("presentation added", url);
329
+
330
+        this.userId = id;
331
+        this.url = url;
332
+
333
+        let preziId = getPresentationId(url);
334
+        let elementId = `participant_${id}_${preziId}`;
344
 
335
 
345
-module.exports = Prezi;
336
+        this.$thumb = $(VideoLayout.addRemoteVideoContainer(elementId));
337
+        VideoLayout.resizeThumbnails();
338
+        this.$thumb.css({
339
+            'background-image': 'url(../images/avatarprezi.png)'
340
+        }).click(() => VideoLayout.showLargeVideoContainer(PreziContainerType, true));
341
+
342
+        this.prezi = new PreziContainer({
343
+            preziId,
344
+            isMy: this.isMyPrezi,
345
+            slide,
346
+            onSlideChanged: newSlide => {
347
+                if (this.isMyPrezi) {
348
+                    this.emitter.emit(UIEvents.SHARE_PREZI, url, newSlide);
349
+                }
350
+            }
351
+        });
352
+
353
+        VideoLayout.addLargeVideoContainer(PreziContainerType, this.prezi);
354
+        VideoLayout.showLargeVideoContainer(PreziContainerType, true);
355
+    }
356
+
357
+    removePrezi (id) {
358
+        if (this.userId !== id) {
359
+            throw new Error(`cannot close presentation from ${this.userId} instead of ${id}`);
360
+        }
361
+
362
+        this.$thumb.remove();
363
+        this.$thumb = null;
364
+
365
+        // wait until Prezi is hidden, then remove it
366
+        VideoLayout.showLargeVideoContainer(PreziContainerType, false).then(() => {
367
+            console.log("presentation removed", this.url);
368
+
369
+            VideoLayout.removeLargeVideoContainer(PreziContainerType);
370
+
371
+            this.userId = null;
372
+            this.url = null;
373
+            this.prezi.close();
374
+            this.prezi = null;
375
+        });
376
+    }
377
+}

+ 276
- 284
modules/UI/prezi/PreziPlayer.js Ver arquivo

1
-/* global PreziPlayer */
2
 /* jshint -W101 */
1
 /* jshint -W101 */
3
-(function() {
4
-    "use strict";
5
-    var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
6
 
2
 
7
-    window.PreziPlayer = (function() {
8
-
9
-        PreziPlayer.API_VERSION = 1;
10
-        PreziPlayer.CURRENT_STEP = 'currentStep';
11
-        PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
12
-        PreziPlayer.CURRENT_OBJECT = 'currentObject';
13
-        PreziPlayer.STATUS_LOADING = 'loading';
14
-        PreziPlayer.STATUS_READY = 'ready';
15
-        PreziPlayer.STATUS_CONTENT_READY = 'contentready';
16
-        PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
17
-        PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
18
-        PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
19
-        PreziPlayer.EVENT_STATUS = "statusChange";
20
-        PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
21
-        PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
22
-        PreziPlayer.domain = "https://prezi.com";
23
-        PreziPlayer.path = "/player/";
24
-        PreziPlayer.players = {};
25
-        PreziPlayer.binded_methods = ['changesHandler'];
26
-
27
-        PreziPlayer.createMultiplePlayers = function(optionArray){
28
-            for(var i=0; i<optionArray.length; i++) {
29
-                var optionSet = optionArray[i];
30
-                new PreziPlayer(optionSet.id, optionSet);
31
-            }
32
-        };
33
-
34
-        PreziPlayer.messageReceived = function(event){
35
-            var message, item, player;
36
-            try {
37
-                message = JSON.parse(event.data);
38
-                if (message.id && (player = PreziPlayer.players[message.id])) {
39
-                    if (player.options.debug === true) {
40
-                        if (console && console.log)
41
-                            console.log('received', message);
42
-                    }
43
-                    if (message.type === "changes") {
44
-                        player.changesHandler(message);
45
-                    }
46
-                    for (var i = 0; i < player.callbacks.length; i++) {
47
-                        item = player.callbacks[i];
48
-                        if (item && message.type === item.event) {
49
-                            item.callback(message);
50
-                        }
51
-                    }
52
-                }
53
-            } catch (e) { }
54
-        };
55
-
56
-/*jshint -W004 */
57
-        function PreziPlayer(id, options) {
58
-/*jshint +W004 */
59
-            var params, paramString = "", _this = this;
60
-            if (PreziPlayer.players[id]){
61
-                PreziPlayer.players[id].destroy();
3
+var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
+
5
+PreziPlayer.API_VERSION = 1;
6
+PreziPlayer.CURRENT_STEP = 'currentStep';
7
+PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
8
+PreziPlayer.CURRENT_OBJECT = 'currentObject';
9
+PreziPlayer.STATUS_LOADING = 'loading';
10
+PreziPlayer.STATUS_READY = 'ready';
11
+PreziPlayer.STATUS_CONTENT_READY = 'contentready';
12
+PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
13
+PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
14
+PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
15
+PreziPlayer.EVENT_STATUS = "statusChange";
16
+PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
17
+PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
18
+PreziPlayer.domain = "https://prezi.com";
19
+PreziPlayer.path = "/player/";
20
+PreziPlayer.players = {};
21
+PreziPlayer.binded_methods = ['changesHandler'];
22
+
23
+PreziPlayer.createMultiplePlayers = function(optionArray){
24
+    for(var i=0; i<optionArray.length; i++) {
25
+        var optionSet = optionArray[i];
26
+        new PreziPlayer(optionSet.id, optionSet);
27
+    }
28
+};
29
+
30
+PreziPlayer.messageReceived = function(event){
31
+    var message, item, player;
32
+    try {
33
+        message = JSON.parse(event.data);
34
+        if (message.id && (player = PreziPlayer.players[message.id])) {
35
+            if (player.options.debug === true) {
36
+                if (console && console.log)
37
+                    console.log('received', message);
62
             }
38
             }
63
-            for(var i=0; i<PreziPlayer.binded_methods.length; i++) {
64
-                var method_name = PreziPlayer.binded_methods[i];
65
-                _this[method_name] = __bind(_this[method_name], _this);
66
-            }
67
-            options = options || {};
68
-            this.options = options;
69
-            this.values = {'status': PreziPlayer.STATUS_LOADING};
70
-            this.values[PreziPlayer.CURRENT_STEP] = 0;
71
-            this.values[PreziPlayer.CURRENT_ANIMATION_STEP] = 0;
72
-            this.values[PreziPlayer.CURRENT_OBJECT] = null;
73
-            this.callbacks = [];
74
-            this.id = id;
75
-            this.embedTo = document.getElementById(id);
76
-            if (!this.embedTo) {
77
-                throw "The element id is not available.";
78
-            }
79
-            this.iframe = document.createElement('iframe');
80
-            params = [
81
-                { name: 'oid', value: options.preziId },
82
-                { name: 'explorable', value: options.explorable ? 1 : 0 },
83
-                { name: 'controls', value: options.controls ? 1 : 0 }
84
-            ];
85
-            for (i=0; i<params.length; i++) {
86
-                var param = params[i];
87
-                paramString += (i===0 ? "?" : "&") + param.name + "=" + param.value;
88
-            }
89
-            this.iframe.src = PreziPlayer.domain + PreziPlayer.path + paramString;
90
-            this.iframe.frameBorder = 0;
91
-            this.iframe.scrolling = "no";
92
-            this.iframe.width = options.width || 640;
93
-            this.iframe.height = options.height || 480;
94
-            this.embedTo.innerHTML = '';
95
-            // JITSI: IN CASE SOMETHING GOES WRONG.
96
-            try {
97
-                this.embedTo.appendChild(this.iframe);
98
-            }
99
-            catch (err) {
100
-                console.log("CATCH ERROR");
101
-            }
102
-
103
-            // JITSI: Increase interval from 200 to 500, which fixes prezi
104
-            // crashes for us.
105
-            this.initPollInterval = setInterval(function(){
106
-                _this.sendMessage({'action': 'init'});
107
-            }, 500);
108
-            PreziPlayer.players[id] = this;
109
-        }
110
-
111
-        PreziPlayer.prototype.changesHandler = function(message) {
112
-            var key, value, j, item;
113
-            if (this.initPollInterval) {
114
-                clearInterval(this.initPollInterval);
115
-                this.initPollInterval = false;
116
-            }
117
-            for (key in message.data) {
118
-                if (message.data.hasOwnProperty(key)){
119
-                    value = message.data[key];
120
-                    this.values[key] = value;
121
-                    for (j=0; j<this.callbacks.length; j++) {
122
-                        item = this.callbacks[j];
123
-                        if (item && item.event === key + "Change"){
124
-                            item.callback({type: item.event, value: value});
125
-                        }
126
-                    }
127
-                }
128
-            }
129
-        };
130
-
131
-        PreziPlayer.prototype.destroy = function() {
132
-            if (this.initPollInterval) {
133
-                clearInterval(this.initPollInterval);
134
-                this.initPollInterval = false;
135
-            }
136
-            this.embedTo.innerHTML = '';
137
-        };
138
-
139
-        PreziPlayer.prototype.sendMessage = function(message) {
140
-            if (this.options.debug === true) {
141
-                if (console && console.log) console.log('sent', message);
142
-            }
143
-            message.version = PreziPlayer.API_VERSION;
144
-            message.id = this.id;
145
-            return this.iframe.contentWindow.postMessage(JSON.stringify(message), '*');
146
-        };
147
-
148
-        PreziPlayer.prototype.nextStep = /* nextStep is DEPRECATED */
149
-        PreziPlayer.prototype.flyToNextStep = function() {
150
-            return this.sendMessage({
151
-                'action': 'present',
152
-                'data': ['moveToNextStep']
153
-            });
154
-        };
155
-
156
-        PreziPlayer.prototype.previousStep = /* previousStep is DEPRECATED */
157
-        PreziPlayer.prototype.flyToPreviousStep = function() {
158
-            return this.sendMessage({
159
-                'action': 'present',
160
-                'data': ['moveToPrevStep']
161
-            });
162
-        };
163
-
164
-        PreziPlayer.prototype.toStep = /* toStep is DEPRECATED */
165
-        PreziPlayer.prototype.flyToStep = function(step, animation_step) {
166
-            var obj = this;
167
-            // check animation_step
168
-            if (animation_step > 0 &&
169
-                obj.values.animationCountOnSteps &&
170
-                obj.values.animationCountOnSteps[step] <= animation_step) {
171
-                animation_step = obj.values.animationCountOnSteps[step];
39
+            if (message.type === "changes") {
40
+                player.changesHandler(message);
172
             }
41
             }
173
-            // jump to animation steps by calling flyToNextStep()
174
-            function doAnimationSteps() {
175
-                if (obj.values.isMoving) {
176
-                    setTimeout(doAnimationSteps, 100); // wait until the flight ends
177
-                    return;
178
-                }
179
-                while (animation_step-- > 0) {
180
-                    obj.flyToNextStep(); // do the animation steps
42
+            for (var i = 0; i < player.callbacks.length; i++) {
43
+                item = player.callbacks[i];
44
+                if (item && message.type === item.event) {
45
+                    item.callback(message);
181
                 }
46
                 }
182
             }
47
             }
183
-            setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time
184
-            // jump to the step
185
-            return this.sendMessage({
186
-                'action': 'present',
187
-                'data': ['moveToStep', step]
188
-            });
189
-        };
190
-
191
-        PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
192
-        PreziPlayer.prototype.flyToObject = function(objectId) {
193
-            return this.sendMessage({
194
-                'action': 'present',
195
-                'data': ['moveToObject', objectId]
196
-            });
197
-        };
198
-
199
-        PreziPlayer.prototype.play = function(defaultDelay) {
200
-            return this.sendMessage({
201
-                'action': 'present',
202
-                'data': ['startAutoPlay', defaultDelay]
203
-            });
204
-        };
205
-
206
-        PreziPlayer.prototype.stop = function() {
207
-            return this.sendMessage({
208
-                'action': 'present',
209
-                'data': ['stopAutoPlay']
210
-            });
211
-        };
212
-
213
-        PreziPlayer.prototype.pause = function(defaultDelay) {
214
-            return this.sendMessage({
215
-                'action': 'present',
216
-                'data': ['pauseAutoPlay', defaultDelay]
217
-            });
218
-        };
219
-
220
-        PreziPlayer.prototype.getCurrentStep = function() {
221
-            return this.values.currentStep;
222
-        };
223
-
224
-        PreziPlayer.prototype.getCurrentAnimationStep = function() {
225
-            return this.values.currentAnimationStep;
226
-        };
227
-
228
-        PreziPlayer.prototype.getCurrentObject = function() {
229
-            return this.values.currentObject;
230
-        };
231
-
232
-        PreziPlayer.prototype.getStatus = function() {
233
-            return this.values.status;
234
-        };
235
-
236
-        PreziPlayer.prototype.isPlaying = function() {
237
-            return this.values.isAutoPlaying;
238
-        };
239
-
240
-        PreziPlayer.prototype.getStepCount = function() {
241
-            return this.values.stepCount;
242
-        };
243
-
244
-        PreziPlayer.prototype.getAnimationCountOnSteps = function() {
245
-            return this.values.animationCountOnSteps;
246
-        };
247
-
248
-        PreziPlayer.prototype.getTitle = function() {
249
-            return this.values.title;
250
-        };
251
-
252
-        PreziPlayer.prototype.setDimensions = function(dims) {
253
-            for (var parameter in dims) {
254
-                this.iframe[parameter] = dims[parameter];
255
-            }
256
-        };
257
-
258
-        PreziPlayer.prototype.getDimensions = function() {
259
-            return {
260
-                width: parseInt(this.iframe.width, 10),
261
-                height: parseInt(this.iframe.height, 10)
262
-            };
263
-        };
264
-
265
-        PreziPlayer.prototype.on = function(event, callback) {
266
-            this.callbacks.push({
267
-                event: event,
268
-                callback: callback
269
-            });
270
-        };
48
+        }
49
+    } catch (e) { }
50
+};
271
 
51
 
272
-        PreziPlayer.prototype.off = function(event, callback) {
273
-            var j, item;
274
-            if (event === undefined) {
275
-                this.callbacks = [];
276
-            }
277
-            j = this.callbacks.length;
278
-            while (j--) {
52
+/*jshint -W004 */
53
+function PreziPlayer(id, options) {
54
+    /*jshint +W004 */
55
+    var params, paramString = "", _this = this;
56
+    if (PreziPlayer.players[id]){
57
+        PreziPlayer.players[id].destroy();
58
+    }
59
+    for(var i=0; i<PreziPlayer.binded_methods.length; i++) {
60
+        var method_name = PreziPlayer.binded_methods[i];
61
+        _this[method_name] = __bind(_this[method_name], _this);
62
+    }
63
+    options = options || {};
64
+    this.options = options;
65
+    this.values = {'status': PreziPlayer.STATUS_LOADING};
66
+    this.values[PreziPlayer.CURRENT_STEP] = 0;
67
+    this.values[PreziPlayer.CURRENT_ANIMATION_STEP] = 0;
68
+    this.values[PreziPlayer.CURRENT_OBJECT] = null;
69
+    this.callbacks = [];
70
+    this.id = id;
71
+    this.embedTo = document.getElementById(id);
72
+    if (!this.embedTo) {
73
+        throw "The element id is not available.";
74
+    }
75
+    this.iframe = document.createElement('iframe');
76
+    params = [
77
+        { name: 'oid', value: options.preziId },
78
+        { name: 'explorable', value: options.explorable ? 1 : 0 },
79
+        { name: 'controls', value: options.controls ? 1 : 0 }
80
+    ];
81
+    for (i=0; i<params.length; i++) {
82
+        var param = params[i];
83
+        paramString += (i===0 ? "?" : "&") + param.name + "=" + param.value;
84
+    }
85
+    this.iframe.src = PreziPlayer.domain + PreziPlayer.path + paramString;
86
+    this.iframe.frameBorder = 0;
87
+    this.iframe.scrolling = "no";
88
+    this.iframe.width = options.width || 640;
89
+    this.iframe.height = options.height || 480;
90
+    this.embedTo.innerHTML = '';
91
+    // JITSI: IN CASE SOMETHING GOES WRONG.
92
+    try {
93
+        this.embedTo.appendChild(this.iframe);
94
+    }
95
+    catch (err) {
96
+        console.log("CATCH ERROR");
97
+    }
98
+
99
+    // JITSI: Increase interval from 200 to 500, which fixes prezi
100
+    // crashes for us.
101
+    this.initPollInterval = setInterval(function(){
102
+        _this.sendMessage({'action': 'init'});
103
+    }, 500);
104
+    PreziPlayer.players[id] = this;
105
+}
106
+
107
+PreziPlayer.prototype.changesHandler = function(message) {
108
+    var key, value, j, item;
109
+    if (this.initPollInterval) {
110
+        clearInterval(this.initPollInterval);
111
+        this.initPollInterval = false;
112
+    }
113
+    for (key in message.data) {
114
+        if (message.data.hasOwnProperty(key)){
115
+            value = message.data[key];
116
+            this.values[key] = value;
117
+            for (j=0; j<this.callbacks.length; j++) {
279
                 item = this.callbacks[j];
118
                 item = this.callbacks[j];
280
-                if (item && item.event === event && (callback === undefined || item.callback === callback)){
281
-                    this.callbacks.splice(j, 1);
119
+                if (item && item.event === key + "Change"){
120
+                    item.callback({type: item.event, value: value});
282
                 }
121
                 }
283
             }
122
             }
284
-        };
285
-
286
-        if (window.addEventListener) {
287
-            window.addEventListener('message', PreziPlayer.messageReceived, false);
288
-        } else {
289
-            window.attachEvent('onmessage', PreziPlayer.messageReceived);
290
         }
123
         }
124
+    }
125
+};
126
+
127
+PreziPlayer.prototype.destroy = function() {
128
+    if (this.initPollInterval) {
129
+        clearInterval(this.initPollInterval);
130
+        this.initPollInterval = false;
131
+    }
132
+    this.embedTo.innerHTML = '';
133
+};
134
+
135
+PreziPlayer.prototype.sendMessage = function(message) {
136
+    if (this.options.debug === true) {
137
+        if (console && console.log) console.log('sent', message);
138
+    }
139
+    message.version = PreziPlayer.API_VERSION;
140
+    message.id = this.id;
141
+    return this.iframe.contentWindow.postMessage(JSON.stringify(message), '*');
142
+};
143
+
144
+PreziPlayer.prototype.nextStep = /* nextStep is DEPRECATED */
145
+PreziPlayer.prototype.flyToNextStep = function() {
146
+    return this.sendMessage({
147
+        'action': 'present',
148
+        'data': ['moveToNextStep']
149
+    });
150
+};
151
+
152
+PreziPlayer.prototype.previousStep = /* previousStep is DEPRECATED */
153
+PreziPlayer.prototype.flyToPreviousStep = function() {
154
+    return this.sendMessage({
155
+        'action': 'present',
156
+        'data': ['moveToPrevStep']
157
+    });
158
+};
159
+
160
+PreziPlayer.prototype.toStep = /* toStep is DEPRECATED */
161
+PreziPlayer.prototype.flyToStep = function(step, animation_step) {
162
+    var obj = this;
163
+    // check animation_step
164
+    if (animation_step > 0 &&
165
+        obj.values.animationCountOnSteps &&
166
+        obj.values.animationCountOnSteps[step] <= animation_step) {
167
+        animation_step = obj.values.animationCountOnSteps[step];
168
+    }
169
+    // jump to animation steps by calling flyToNextStep()
170
+    function doAnimationSteps() {
171
+        if (obj.values.isMoving) {
172
+            setTimeout(doAnimationSteps, 100); // wait until the flight ends
173
+            return;
174
+        }
175
+        while (animation_step-- > 0) {
176
+            obj.flyToNextStep(); // do the animation steps
177
+        }
178
+    }
179
+    setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time
180
+    // jump to the step
181
+    return this.sendMessage({
182
+        'action': 'present',
183
+        'data': ['moveToStep', step]
184
+    });
185
+};
186
+
187
+PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
188
+PreziPlayer.prototype.flyToObject = function(objectId) {
189
+    return this.sendMessage({
190
+        'action': 'present',
191
+        'data': ['moveToObject', objectId]
192
+    });
193
+};
194
+
195
+PreziPlayer.prototype.play = function(defaultDelay) {
196
+    return this.sendMessage({
197
+        'action': 'present',
198
+        'data': ['startAutoPlay', defaultDelay]
199
+    });
200
+};
201
+
202
+PreziPlayer.prototype.stop = function() {
203
+    return this.sendMessage({
204
+        'action': 'present',
205
+        'data': ['stopAutoPlay']
206
+    });
207
+};
208
+
209
+PreziPlayer.prototype.pause = function(defaultDelay) {
210
+    return this.sendMessage({
211
+        'action': 'present',
212
+        'data': ['pauseAutoPlay', defaultDelay]
213
+    });
214
+};
215
+
216
+PreziPlayer.prototype.getCurrentStep = function() {
217
+    return this.values.currentStep;
218
+};
219
+
220
+PreziPlayer.prototype.getCurrentAnimationStep = function() {
221
+    return this.values.currentAnimationStep;
222
+};
223
+
224
+PreziPlayer.prototype.getCurrentObject = function() {
225
+    return this.values.currentObject;
226
+};
227
+
228
+PreziPlayer.prototype.getStatus = function() {
229
+    return this.values.status;
230
+};
231
+
232
+PreziPlayer.prototype.isPlaying = function() {
233
+    return this.values.isAutoPlaying;
234
+};
235
+
236
+PreziPlayer.prototype.getStepCount = function() {
237
+    return this.values.stepCount;
238
+};
239
+
240
+PreziPlayer.prototype.getAnimationCountOnSteps = function() {
241
+    return this.values.animationCountOnSteps;
242
+};
243
+
244
+PreziPlayer.prototype.getTitle = function() {
245
+    return this.values.title;
246
+};
247
+
248
+PreziPlayer.prototype.setDimensions = function(dims) {
249
+    for (var parameter in dims) {
250
+        this.iframe[parameter] = dims[parameter];
251
+    }
252
+};
253
+
254
+PreziPlayer.prototype.getDimensions = function() {
255
+    return {
256
+        width: parseInt(this.iframe.width, 10),
257
+        height: parseInt(this.iframe.height, 10)
258
+    };
259
+};
260
+
261
+PreziPlayer.prototype.on = function(event, callback) {
262
+    this.callbacks.push({
263
+        event: event,
264
+        callback: callback
265
+    });
266
+};
267
+
268
+PreziPlayer.prototype.off = function(event, callback) {
269
+    var j, item;
270
+    if (event === undefined) {
271
+        this.callbacks = [];
272
+    }
273
+    j = this.callbacks.length;
274
+    while (j--) {
275
+        item = this.callbacks[j];
276
+        if (item && item.event === event && (callback === undefined || item.callback === callback)){
277
+            this.callbacks.splice(j, 1);
278
+        }
279
+    }
280
+};
291
 
281
 
292
-        return PreziPlayer;
293
-
294
-    })();
282
+if (window.addEventListener) {
283
+    window.addEventListener('message', PreziPlayer.messageReceived, false);
284
+} else {
285
+    window.attachEvent('onmessage', PreziPlayer.messageReceived);
286
+}
295
 
287
 
296
-})();
288
+window.PreziPlayer = PreziPlayer;
297
 
289
 
298
-module.exports = PreziPlayer;
290
+export default PreziPlayer;

+ 3
- 19
modules/UI/side_pannels/SidePanelToggler.js Ver arquivo

6
 import VideoLayout from "../videolayout/VideoLayout";
6
 import VideoLayout from "../videolayout/VideoLayout";
7
 import ToolbarToggler from "../toolbars/ToolbarToggler";
7
 import ToolbarToggler from "../toolbars/ToolbarToggler";
8
 import UIUtil from "../util/UIUtil";
8
 import UIUtil from "../util/UIUtil";
9
-import LargeVideo from "../videolayout/LargeVideo";
10
 
9
 
11
 const buttons = {
10
 const buttons = {
12
     '#chatspace': '#chatBottomButton',
11
     '#chatspace': '#chatBottomButton',
47
     } else {
46
     } else {
48
         // Undock the toolbar when the chat is shown and if we're in a
47
         // Undock the toolbar when the chat is shown and if we're in a
49
         // video mode.
48
         // video mode.
50
-        if (LargeVideo.isLargeVideoVisible()) {
49
+        if (VideoLayout.isLargeVideoVisible()) {
51
             ToolbarToggler.dockToolbar(false);
50
             ToolbarToggler.dockToolbar(false);
52
         }
51
         }
53
 
52
 
62
         }
61
         }
63
 
62
 
64
         $("#toast-container").animate({
63
         $("#toast-container").animate({
65
-            right: (PanelToggler.getPanelSize()[0] + 5)
64
+            right: (UIUtil.getSidePanelSize()[0] + 5)
66
         }, {
65
         }, {
67
             queue: false,
66
             queue: false,
68
             duration: 500
67
             duration: 500
116
     },
115
     },
117
 
116
 
118
     resizeChat () {
117
     resizeChat () {
119
-        let [width, height] = this.getPanelSize();
118
+        let [width, height] = UIUtil.getSidePanelSize();
120
         Chat.resizeChat(width, height);
119
         Chat.resizeChat(width, height);
121
     },
120
     },
122
 
121
 
156
             null);
155
             null);
157
     },
156
     },
158
 
157
 
159
-    /**
160
-     * Returns the size of the side panel.
161
-     */
162
-    getPanelSize () {
163
-        var availableHeight = window.innerHeight;
164
-        var availableWidth = window.innerWidth;
165
-
166
-        var panelWidth = 200;
167
-        if (availableWidth * 0.2 < 200) {
168
-            panelWidth = availableWidth * 0.2;
169
-        }
170
-
171
-        return [panelWidth, availableHeight];
172
-    },
173
-
174
     isVisible () {
158
     isVisible () {
175
         return (Chat.isVisible() ||
159
         return (Chat.isVisible() ||
176
                 ContactList.isVisible() ||
160
                 ContactList.isVisible() ||

+ 7
- 7
modules/UI/toolbars/BottomToolbar.js Ver arquivo

9
     'filmstrip': '#bottom_toolbar_film_strip'
9
     'filmstrip': '#bottom_toolbar_film_strip'
10
 };
10
 };
11
 
11
 
12
-$(document).bind("remotevideo.resized", function (event, width, height) {
13
-    let toolbar = $('#bottomToolbar');
14
-    let bottom = (height - toolbar.outerHeight())/2 + 18;
15
-
16
-    toolbar.css({bottom});
17
-});
18
-
19
 const BottomToolbar = {
12
 const BottomToolbar = {
20
     init (emitter) {
13
     init (emitter) {
21
         UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
14
         UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
42
 
35
 
43
     toggleFilmStrip () {
36
     toggleFilmStrip () {
44
         $("#remoteVideos").toggleClass("hidden");
37
         $("#remoteVideos").toggleClass("hidden");
38
+    },
39
+
40
+    onRemoteVideoResized (width, height) {
41
+        let toolbar = $('#bottomToolbar');
42
+        let bottom = (height - toolbar.outerHeight())/2 + 18;
43
+
44
+        toolbar.css({bottom});
45
     }
45
     }
46
 };
46
 };
47
 
47
 

+ 20
- 14
modules/UI/toolbars/Toolbar.js Ver arquivo

182
     }
182
     }
183
 };
183
 };
184
 const defaultToolbarButtons = {
184
 const defaultToolbarButtons = {
185
-  'microphone': '#toolbar_button_mute',
186
-  'camera':     '#toolbar_button_camera',
187
-  'desktop':    '#toolbar_button_desktopsharing',
188
-  'security':   '#toolbar_button_security',
189
-  'invite':     '#toolbar_button_link',
190
-  'chat':       '#toolbar_button_chat',
191
-  'prezi':      '#toolbar_button_prezi',
192
-  'etherpad':   '#toolbar_button_etherpad',
193
-  'fullscreen': '#toolbar_button_fullScreen',
194
-  'settings':   '#toolbar_button_settings',
195
-  'hangup':     '#toolbar_button_hangup'
185
+    'microphone': '#toolbar_button_mute',
186
+    'camera':     '#toolbar_button_camera',
187
+    'desktop':    '#toolbar_button_desktopsharing',
188
+    'security':   '#toolbar_button_security',
189
+    'invite':     '#toolbar_button_link',
190
+    'chat':       '#toolbar_button_chat',
191
+    'prezi':      '#toolbar_button_prezi',
192
+    'etherpad':   '#toolbar_button_etherpad',
193
+    'fullscreen': '#toolbar_button_fullScreen',
194
+    'settings':   '#toolbar_button_settings',
195
+    'hangup':     '#toolbar_button_hangup'
196
 };
196
 };
197
 
197
 
198
 function dialpadButtonClicked() {
198
 function dialpadButtonClicked() {
208
     messageHandler.openTwoButtonDialog(
208
     messageHandler.openTwoButtonDialog(
209
         null, null, null,
209
         null, null, null,
210
         `<h2>${sipMsg}</h2>
210
         `<h2>${sipMsg}</h2>
211
-         <input name="sipNumber" type="text" value="${defaultNumber}" autofocus>`,
211
+            <input name="sipNumber" type="text" value="${defaultNumber}" autofocus>`,
212
         false, "dialog.Dial",
212
         false, "dialog.Dial",
213
         function (e, v, m, f) {
213
         function (e, v, m, f) {
214
             if (v && f.sipNumber) {
214
             if (v && f.sipNumber) {
250
      * Disables and enables some of the buttons.
250
      * Disables and enables some of the buttons.
251
      */
251
      */
252
     setupButtonsFromConfig () {
252
     setupButtonsFromConfig () {
253
-        if (UIUtil.isButtonEnabled('prezi')) {
253
+        if (!UIUtil.isButtonEnabled('prezi')) {
254
             $("#toolbar_button_prezi").css({display: "none"});
254
             $("#toolbar_button_prezi").css({display: "none"});
255
         }
255
         }
256
     },
256
     },
283
         }
283
         }
284
     },
284
     },
285
 
285
 
286
+    showEtherpadButton () {
287
+        if (!$('#toolbar_button_etherpad').is(":visible")) {
288
+            $('#toolbar_button_etherpad').css({display: 'inline-block'});
289
+        }
290
+    },
291
+
286
     // Shows or hides the 'recording' button.
292
     // Shows or hides the 'recording' button.
287
     showRecordingButton (show) {
293
     showRecordingButton (show) {
288
         if (UIUtil.isButtonEnabled('recording') && show) {
294
         if (UIUtil.isButtonEnabled('recording') && show) {
304
     // we have params to start automatically sharing
310
     // we have params to start automatically sharing
305
     checkAutoEnableDesktopSharing () {
311
     checkAutoEnableDesktopSharing () {
306
         if (UIUtil.isButtonEnabled('desktop')
312
         if (UIUtil.isButtonEnabled('desktop')
307
-                && config.autoEnableDesktopSharing) {
313
+            && config.autoEnableDesktopSharing) {
308
             APP.desktopsharing.toggleScreenSharing();
314
             APP.desktopsharing.toggleScreenSharing();
309
         }
315
         }
310
     },
316
     },

+ 28
- 7
modules/UI/util/UIUtil.js Ver arquivo

1
 /* global $, config, interfaceConfig */
1
 /* global $, config, interfaceConfig */
2
 
2
 
3
-import PanelToggler from "../side_pannels/SidePanelToggler";
4
-
5
 /**
3
 /**
6
  * Created by hristo on 12/22/14.
4
  * Created by hristo on 12/22/14.
7
  */
5
  */
8
  var UIUtil = {
6
  var UIUtil = {
7
+
8
+    /**
9
+     * Returns the size of the side panel.
10
+     */
11
+     getSidePanelSize () {
12
+        var availableHeight = window.innerHeight;
13
+        var availableWidth = window.innerWidth;
14
+
15
+        var panelWidth = 200;
16
+        if (availableWidth * 0.2 < 200) {
17
+            panelWidth = availableWidth * 0.2;
18
+        }
19
+
20
+        return [panelWidth, availableHeight];
21
+     },
22
+
9
     /**
23
     /**
10
      * Returns the available video width.
24
      * Returns the available video width.
11
      */
25
      */
12
-    getAvailableVideoWidth: function (isVisible) {
13
-        if(typeof isVisible === "undefined" || isVisible === null)
14
-            isVisible = PanelToggler.isVisible();
15
-        var rightPanelWidth
16
-            = isVisible ? PanelToggler.getPanelSize()[0] : 0;
26
+    getAvailableVideoWidth: function (isSidePanelVisible) {
27
+        let rightPanelWidth = 0;
28
+
29
+        if (isSidePanelVisible) {
30
+            rightPanelWidth = UIUtil.getSidePanelSize()[0];
31
+        }
17
 
32
 
18
         return window.innerWidth - rightPanelWidth;
33
         return window.innerWidth - rightPanelWidth;
19
     },
34
     },
118
 
133
 
119
     redirect (url) {
134
     redirect (url) {
120
          window.location.href = url;
135
          window.location.href = url;
136
+     },
137
+
138
+     isFullScreen () {
139
+         return document.fullScreen
140
+             || document.mozFullScreen
141
+             || document.webkitIsFullScreen;
121
      }
142
      }
122
 };
143
 };
123
 
144
 

+ 24
- 0
modules/UI/videolayout/LargeContainer.js Ver arquivo

1
+
2
+export default class LargeContainer {
3
+
4
+    /**
5
+     * @returns Promise
6
+     */
7
+    show () {
8
+    }
9
+
10
+    /**
11
+     * @returns Promise
12
+     */
13
+    hide () {
14
+    }
15
+
16
+    resize (containerWidth, containerHeight, animate) {
17
+    }
18
+
19
+    onHoverIn (e) {
20
+    }
21
+
22
+    onHoverOut (e) {
23
+    }
24
+}

+ 302
- 583
modules/UI/videolayout/LargeVideo.js Ver arquivo

1
 /* global $, APP, interfaceConfig */
1
 /* global $, APP, interfaceConfig */
2
 /* jshint -W101 */
2
 /* jshint -W101 */
3
-import Avatar from "../avatar/Avatar";
4
-import ToolbarToggler from "../toolbars/ToolbarToggler";
3
+
5
 import UIUtil from "../util/UIUtil";
4
 import UIUtil from "../util/UIUtil";
6
 import UIEvents from "../../../service/UI/UIEvents";
5
 import UIEvents from "../../../service/UI/UIEvents";
6
+import LargeContainer from './LargeContainer';
7
 
7
 
8
-var RTCBrowserType = require("../../RTC/RTCBrowserType");
9
-
10
-// FIXME: With Temasys we have to re-select everytime
11
-//var video = $('#largeVideo');
8
+const RTCBrowserType = require("../../RTC/RTCBrowserType");
12
 
9
 
13
-var currentVideoWidth = null;
14
-var currentVideoHeight = null;
15
-// By default we use camera
16
-var getVideoSize = getCameraVideoSize;
17
-var getVideoPosition = getCameraVideoPosition;
18
-/**
19
- * The small video instance that is displayed in the large video
20
- * @type {SmallVideo}
21
- */
22
-var currentSmallVideo = null;
23
-/**
24
- * Indicates whether the large video is enabled.
25
- * @type {boolean}
26
- */
27
-var isEnabled = true;
28
-/**
29
- * Current large video state.
30
- * Possible values - video, prezi or etherpad.
31
- * @type {string}
32
- */
33
-var state = "video";
10
+const avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
34
 
11
 
35
-/**
36
- * Returns the html element associated with the passed state of large video
37
- * @param state the state.
38
- * @returns {JQuery|*|jQuery|HTMLElement} the container.
39
- */
40
-function getContainerByState(state) {
41
-    var selector = null;
42
-    switch (state) {
43
-    case "video":
44
-        selector = "#largeVideoWrapper";
45
-        break;
46
-    case "etherpad":
47
-        selector = "#etherpad>iframe";
48
-        break;
49
-    case "prezi":
50
-        selector = "#presentation>iframe";
51
-        break;
52
-    default:
53
-        return null;
54
-    }
55
-    return $(selector);
56
-}
57
-
58
-/**
59
- * Sets the size and position of the given video element.
60
- *
61
- * @param video the video element to position
62
- * @param width the desired video width
63
- * @param height the desired video height
64
- * @param horizontalIndent the left and right indent
65
- * @param verticalIndent the top and bottom indent
66
- */
67
-function positionVideo(video,
68
-                       width,
69
-                       height,
70
-                       horizontalIndent,
71
-                       verticalIndent,
72
-                       animate) {
73
-    if (animate) {
74
-        video.animate({
75
-            width: width,
76
-            height: height,
77
-            top: verticalIndent,
78
-            bottom: verticalIndent,
79
-            left: horizontalIndent,
80
-            right: horizontalIndent
81
-        }, {
82
-            queue: false,
83
-            duration: 500
84
-        });
12
+function getStreamId(stream) {
13
+    if (stream.isLocal()) {
14
+        return APP.conference.localId;
85
     } else {
15
     } else {
86
-        video.width(width);
87
-        video.height(height);
88
-        video.css({
89
-            top:     verticalIndent,
90
-            bottom:  verticalIndent,
91
-            left:    horizontalIndent,
92
-            right:   horizontalIndent
93
-        });
16
+        return stream.getParticipantId();
94
     }
17
     }
95
-
96
 }
18
 }
97
 
19
 
98
 /**
20
 /**
106
                              videoHeight,
28
                              videoHeight,
107
                              videoSpaceWidth,
29
                              videoSpaceWidth,
108
                              videoSpaceHeight) {
30
                              videoSpaceHeight) {
109
-    if (!videoWidth)
110
-        videoWidth = currentVideoWidth;
111
-    if (!videoHeight)
112
-        videoHeight = currentVideoHeight;
113
 
31
 
114
-    var aspectRatio = videoWidth / videoHeight;
32
+    let aspectRatio = videoWidth / videoHeight;
115
 
33
 
116
-    var availableWidth = Math.max(videoWidth, videoSpaceWidth);
117
-    var availableHeight = Math.max(videoHeight, videoSpaceHeight);
34
+    let availableWidth = Math.max(videoWidth, videoSpaceWidth);
35
+    let availableHeight = Math.max(videoHeight, videoSpaceHeight);
118
 
36
 
119
-    var filmstrip = $("#remoteVideos");
37
+    let filmstrip = $("#remoteVideos");
120
 
38
 
121
     if (!filmstrip.hasClass("hidden"))
39
     if (!filmstrip.hasClass("hidden"))
122
         videoSpaceHeight -= filmstrip.outerHeight();
40
         videoSpaceHeight -= filmstrip.outerHeight();
123
 
41
 
124
-    if (availableWidth / aspectRatio >= videoSpaceHeight)
125
-    {
42
+    if (availableWidth / aspectRatio >= videoSpaceHeight) {
126
         availableHeight = videoSpaceHeight;
43
         availableHeight = videoSpaceHeight;
127
         availableWidth = availableHeight * aspectRatio;
44
         availableWidth = availableHeight * aspectRatio;
128
     }
45
     }
129
 
46
 
130
-    if (availableHeight * aspectRatio >= videoSpaceWidth)
131
-    {
47
+    if (availableHeight * aspectRatio >= videoSpaceWidth) {
132
         availableWidth = videoSpaceWidth;
48
         availableWidth = videoSpaceWidth;
133
         availableHeight = availableWidth / aspectRatio;
49
         availableHeight = availableWidth / aspectRatio;
134
     }
50
     }
135
 
51
 
136
-    return [availableWidth, availableHeight];
137
-}
138
-
139
-
140
-/**
141
- * Returns an array of the video horizontal and vertical indents,
142
- * so that if fits its parent.
143
- *
144
- * @return an array with 2 elements, the horizontal indent and the vertical
145
- * indent
146
- */
147
-function getCameraVideoPosition(videoWidth,
148
-                                videoHeight,
149
-                                videoSpaceWidth,
150
-                                videoSpaceHeight) {
151
-    // Parent height isn't completely calculated when we position the video in
152
-    // full screen mode and this is why we use the screen height in this case.
153
-    // Need to think it further at some point and implement it properly.
154
-    var isFullScreen = document.fullScreen ||
155
-        document.mozFullScreen ||
156
-        document.webkitIsFullScreen;
157
-    if (isFullScreen)
158
-        videoSpaceHeight = window.innerHeight;
159
-
160
-    var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
161
-    var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
162
-
163
-    return [horizontalIndent, verticalIndent];
164
-}
165
-
166
-/**
167
- * Returns an array of the video horizontal and vertical indents.
168
- * Centers horizontally and top aligns vertically.
169
- *
170
- * @return an array with 2 elements, the horizontal indent and the vertical
171
- * indent
172
- */
173
-function getDesktopVideoPosition(videoWidth,
174
-                                 videoHeight,
175
-                                 videoSpaceWidth,
176
-                                 videoSpaceHeight) {
177
-
178
-    var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
179
-
180
-    var verticalIndent = 0;// Top aligned
181
-
182
-    return [horizontalIndent, verticalIndent];
52
+    return { availableWidth, availableHeight };
183
 }
53
 }
184
 
54
 
185
 
55
 
199
                             videoSpaceWidth,
69
                             videoSpaceWidth,
200
                             videoSpaceHeight) {
70
                             videoSpaceHeight) {
201
 
71
 
202
-    if (!videoWidth)
203
-        videoWidth = currentVideoWidth;
204
-    if (!videoHeight)
205
-        videoHeight = currentVideoHeight;
72
+    let aspectRatio = videoWidth / videoHeight;
206
 
73
 
207
-    var aspectRatio = videoWidth / videoHeight;
208
-
209
-    var availableWidth = videoWidth;
210
-    var availableHeight = videoHeight;
74
+    let availableWidth = videoWidth;
75
+    let availableHeight = videoHeight;
211
 
76
 
212
     if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
77
     if (interfaceConfig.VIDEO_LAYOUT_FIT == 'height') {
213
         availableHeight = videoSpaceHeight;
78
         availableHeight = videoSpaceHeight;
233
     }
98
     }
234
 
99
 
235
 
100
 
236
-    return [availableWidth, availableHeight];
101
+    return { availableWidth, availableHeight };
237
 }
102
 }
238
 
103
 
239
 /**
104
 /**
240
- * Updates the src of the active speaker avatar
105
+ * Returns an array of the video horizontal and vertical indents,
106
+ * so that if fits its parent.
107
+ *
108
+ * @return an array with 2 elements, the horizontal indent and the vertical
109
+ * indent
241
  */
110
  */
242
-function updateActiveSpeakerAvatarSrc() {
243
-    let avatar = $("#activeSpeakerAvatar");
244
-    let id = currentSmallVideo.id;
245
-    let url = Avatar.getActiveSpeakerUrl(id);
246
-    if (id && avatar.attr('src') !== url) {
247
-        avatar.attr('src', url);
248
-        currentSmallVideo.showAvatar();
111
+function getCameraVideoPosition(videoWidth,
112
+                                videoHeight,
113
+                                videoSpaceWidth,
114
+                                videoSpaceHeight) {
115
+    // Parent height isn't completely calculated when we position the video in
116
+    // full screen mode and this is why we use the screen height in this case.
117
+    // Need to think it further at some point and implement it properly.
118
+    if (UIUtil.isFullScreen()) {
119
+        videoSpaceHeight = window.innerHeight;
249
     }
120
     }
121
+
122
+    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
123
+    let verticalIndent = (videoSpaceHeight - videoHeight) / 2;
124
+
125
+    return { horizontalIndent, verticalIndent };
250
 }
126
 }
251
 
127
 
252
 /**
128
 /**
253
- * Change the video source of the large video.
254
- * @param isVisible
129
+ * Returns an array of the video horizontal and vertical indents.
130
+ * Centers horizontally and top aligns vertically.
131
+ *
132
+ * @return an array with 2 elements, the horizontal indent and the vertical
133
+ * indent
255
  */
134
  */
256
-function changeVideo(isVisible) {
135
+function getDesktopVideoPosition(videoWidth,
136
+                                 videoHeight,
137
+                                 videoSpaceWidth,
138
+                                 videoSpaceHeight) {
139
+
140
+    let horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
141
+
142
+    let verticalIndent = 0;// Top aligned
143
+
144
+    return { horizontalIndent, verticalIndent };
145
+}
146
+
147
+export const VideoContainerType = "video";
257
 
148
 
258
-    if (!currentSmallVideo) {
259
-        console.error("Unable to change large video - no 'currentSmallVideo'");
260
-        return;
149
+class VideoContainer extends LargeContainer {
150
+    // FIXME: With Temasys we have to re-select everytime
151
+    get $video () {
152
+        return $('#largeVideo');
261
     }
153
     }
262
 
154
 
263
-    updateActiveSpeakerAvatarSrc();
264
-    let largeVideoElement = $('#largeVideo');
155
+    get id () {
156
+        if (this.stream) {
157
+            return getStreamId(this.stream);
158
+        }
159
+    }
265
 
160
 
266
-    currentSmallVideo.stream.attach(largeVideoElement);
161
+    constructor (onPlay) {
162
+        super();
163
+        this.stream = null;
267
 
164
 
268
-    let flipX = currentSmallVideo.flipX;
165
+        this.$avatar = $('#activeSpeaker');
166
+        this.$wrapper = $('#largeVideoWrapper');
269
 
167
 
270
-    largeVideoElement.css({
271
-        transform: flipX ? "scaleX(-1)" : "none"
272
-    });
168
+        if (!RTCBrowserType.isIExplorer()) {
169
+            this.$video.volume = 0;
170
+        }
273
 
171
 
274
-    LargeVideo.updateVideoSizeAndPosition(currentSmallVideo.getVideoType());
172
+        this.$video.on('play', onPlay);
173
+    }
275
 
174
 
276
-    // Only if the large video is currently visible.
277
-    if (isVisible) {
278
-        LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo);
175
+    getStreamSize () {
176
+        let video = this.$video[0];
177
+        return {
178
+            width: video.videoWidth,
179
+            height: video.videoHeight
180
+        };
181
+    }
279
 
182
 
280
-        $('#largeVideoWrapper').fadeTo(300, 1);
183
+    getVideoSize (containerWidth, containerHeight) {
184
+        let { width, height } = this.getStreamSize();
185
+        if (this.stream && this.stream.isScreenSharing()) {
186
+            return getDesktopVideoSize(width, height, containerWidth, containerHeight);
187
+        } else {
188
+            return getCameraVideoSize(width, height, containerWidth, containerHeight);
189
+        }
281
     }
190
     }
282
-}
283
 
191
 
284
-/**
285
- * Creates the html elements for the large video.
286
- */
287
-function createLargeVideoHTML()
288
-{
289
-    var html = '<div id="largeVideoContainer" class="videocontainer">';
290
-    html += '<div id="presentation"></div>' +
291
-            '<div id="etherpad"></div>' +
292
-            '<a target="_new"><div class="watermark leftwatermark"></div></a>' +
293
-            '<a target="_new"><div class="watermark rightwatermark"></div></a>' +
294
-            '<a class="poweredby" href="http://jitsi.org" target="_new" >' +
295
-                '<span data-i18n="poweredby"></span> jitsi.org' +
296
-            '</a>'+
297
-            '<div id="activeSpeaker">' +
298
-                '<img id="activeSpeakerAvatar" src=""/>' +
299
-                '<canvas id="activeSpeakerAudioLevel"></canvas>' +
300
-            '</div>' +
301
-            '<div id="largeVideoWrapper">' +
302
-                '<video id="largeVideo" muted="true"' +
303
-                        'autoplay oncontextmenu="return false;"></video>' +
304
-            '</div id="largeVideoWrapper">' +
305
-            '<span id="videoConnectionMessage"></span>';
306
-    html += '</div>';
307
-    $(html).prependTo("#videospace");
308
-
309
-    if (interfaceConfig.SHOW_JITSI_WATERMARK) {
310
-        var leftWatermarkDiv
311
-            = $("#largeVideoContainer div[class='watermark leftwatermark']");
312
-
313
-        leftWatermarkDiv.css({display: 'block'});
314
-        leftWatermarkDiv.parent().get(0).href
315
-            = interfaceConfig.JITSI_WATERMARK_LINK;
316
-    }
317
-
318
-    if (interfaceConfig.SHOW_BRAND_WATERMARK) {
319
-        var rightWatermarkDiv
320
-            = $("#largeVideoContainer div[class='watermark rightwatermark']");
321
-
322
-        rightWatermarkDiv.css({display: 'block'});
323
-        rightWatermarkDiv.parent().get(0).href
324
-            = interfaceConfig.BRAND_WATERMARK_LINK;
325
-        rightWatermarkDiv.get(0).style.backgroundImage
326
-            = "url(images/rightwatermark.png)";
327
-    }
328
-
329
-    if (interfaceConfig.SHOW_POWERED_BY) {
330
-        $("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
331
-    }
332
-
333
-    if (!RTCBrowserType.isIExplorer()) {
334
-        $('#largeVideo').volume = 0;
192
+    getVideoPosition (width, height, containerWidth, containerHeight) {
193
+        if (this.stream && this.stream.isScreenSharing()) {
194
+            return getDesktopVideoPosition(width, height, containerWidth, containerHeight);
195
+        } else {
196
+            return getCameraVideoPosition(width, height, containerWidth, containerHeight);
197
+        }
198
+    }
199
+
200
+    resize (containerWidth, containerHeight, animate = false) {
201
+        let { width, height } = this.getVideoSize(containerWidth, containerHeight);
202
+        let { horizontalIndent, verticalIndent } = this.getVideoPosition(width, height, containerWidth, containerHeight);
203
+
204
+        // update avatar position
205
+        let top = this.containerHeight / 2 - avatarSize / 4 * 3;
206
+        this.$avatar.css('top', top);
207
+
208
+        this.$wrapper.animate({
209
+            width,
210
+            height,
211
+
212
+            top: verticalIndent,
213
+            bottom: verticalIndent,
214
+
215
+            left: horizontalIndent,
216
+            right: horizontalIndent
217
+        }, {
218
+            queue: false,
219
+            duration: animate ? 500 : 0
220
+        });
221
+    }
222
+
223
+    setStream (stream) {
224
+        this.stream = stream;
225
+
226
+        stream.attach(this.$video);
227
+
228
+        let flipX = stream.isLocal() && !stream.isScreenSharing();
229
+        this.$video.css({
230
+            transform: flipX ? 'scaleX(-1)' : 'none'
231
+        });
232
+    }
233
+
234
+    showAvatar (show) {
235
+        this.$avatar.css("visibility", show ? "visible" : "hidden");
236
+    }
237
+
238
+    // We are doing fadeOut/fadeIn animations on parent div which wraps
239
+    // largeVideo, because when Temasys plugin is in use it replaces
240
+    // <video> elements with plugin <object> tag. In Safari jQuery is
241
+    // unable to store values on this plugin object which breaks all
242
+    // animation effects performed on it directly.
243
+
244
+    show () {
245
+        let $wrapper = this.$wrapper;
246
+        return new Promise(resolve => {
247
+            $wrapper.fadeIn(300, function () {
248
+                $wrapper.css({visibility: 'visible'});
249
+                $('.watermark').css({visibility: 'visible'});
250
+            });
251
+            resolve();
252
+        });
253
+    }
254
+
255
+    hide () {
256
+        this.showAvatar(false);
257
+
258
+        let $wrapper = this.$wrapper;
259
+
260
+        return new Promise(resolve => {
261
+            $wrapper.fadeOut(300, function () {
262
+                $wrapper.css({visibility: 'hidden'});
263
+                $('.watermark').css({visibility: 'hidden'});
264
+                resolve();
265
+            });
266
+        });
335
     }
267
     }
336
 }
268
 }
337
 
269
 
338
-var LargeVideo = {
339
 
270
 
340
-    init: function (VideoLayout, emitter) {
341
-        if(!isEnabled)
342
-            return;
343
-        createLargeVideoHTML();
344
-
345
-        this.VideoLayout = VideoLayout;
346
-        this.eventEmitter = emitter;
347
-        this.eventEmitter.emit(UIEvents.LARGEVIDEO_INIT);
348
-        var self = this;
349
-        // Listen for large video size updates
350
-        var largeVideo = $('#largeVideo')[0];
351
-        var onplaying = function (arg1, arg2, arg3) {
352
-            // re-select
353
-            if (RTCBrowserType.isTemasysPluginUsed())
354
-                largeVideo = $('#largeVideo')[0];
355
-            currentVideoWidth = largeVideo.videoWidth;
356
-            currentVideoHeight = largeVideo.videoHeight;
357
-            self.position(currentVideoWidth, currentVideoHeight);
358
-        };
359
-        largeVideo.onplaying = onplaying;
360
-    },
361
-    /**
362
-     * Indicates if the large video is currently visible.
363
-     *
364
-     * @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
365
-     */
366
-    isLargeVideoVisible: function() {
367
-        return $('#largeVideoWrapper').is(':visible');
368
-    },
369
-    /**
370
-     * Returns <tt>true</tt> if the user is currently displayed on large video.
371
-     */
372
-    isCurrentlyOnLarge: function (id) {
373
-        return id && id === this.getId();
374
-    },
375
-    /**
376
-     * Updates the large video with the given new video source.
377
-     */
378
-    updateLargeVideo: function (id, forceUpdate) {
379
-        if(!isEnabled) {
380
-            return;
381
-        }
382
-        let newSmallVideo = this.VideoLayout.getSmallVideo(id);
383
-        console.info(`hover in ${id} , video: `, newSmallVideo);
271
+export default class LargeVideoManager {
272
+    constructor () {
273
+        this.containers = {};
384
 
274
 
385
-        if (!newSmallVideo) {
386
-            console.error("Small video not found for: " + id);
387
-            return;
388
-        }
275
+        this.state = VideoContainerType;
276
+        this.videoContainer = new VideoContainer(() => this.resizeContainer(VideoContainerType));
277
+        this.addContainer(VideoContainerType, this.videoContainer);
389
 
278
 
390
-        if (!LargeVideo.isCurrentlyOnLarge(id) || forceUpdate) {
391
-            $('#activeSpeaker').css('visibility', 'hidden');
279
+        this.width = 0;
280
+        this.height = 0;
392
 
281
 
393
-            let oldId = this.getId();
282
+        this.$container = $('#largeVideoContainer');
394
 
283
 
395
-            currentSmallVideo = newSmallVideo;
284
+        this.$container.css({
285
+            display: 'inline-block'
286
+        });
396
 
287
 
397
-            if (oldId !== id) {
398
-                // we want the notification to trigger even if id is undefined,
399
-                // or null.
400
-                this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
401
-            }
402
-            // We are doing fadeOut/fadeIn animations on parent div which wraps
403
-            // largeVideo, because when Temasys plugin is in use it replaces
404
-            // <video> elements with plugin <object> tag. In Safari jQuery is
405
-            // unable to store values on this plugin object which breaks all
406
-            // animation effects performed on it directly.
407
-            //
408
-            // If for any reason large video was hidden before calling fadeOut
409
-            // changeVideo will never be called, so we call show() in chain just
410
-            // to be sure
411
-            $('#largeVideoWrapper').show().fadeTo(300, 0,
412
-                changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
413
-        } else {
414
-            if (currentSmallVideo) {
415
-                currentSmallVideo.showAvatar();
416
-            }
417
-        }
288
+        if (interfaceConfig.SHOW_JITSI_WATERMARK) {
289
+            let leftWatermarkDiv = this.$container.find("div.watermark.leftwatermark");
418
 
290
 
419
-    },
291
+            leftWatermarkDiv.css({display: 'block'});
420
 
292
 
421
-    /**
422
-     * Shows/hides the large video.
423
-     */
424
-    setLargeVideoVisible: function(isVisible) {
425
-        if(!isEnabled)
426
-            return;
427
-        if (isVisible) {
428
-            $('#largeVideoWrapper').css({visibility: 'visible'});
429
-            $('.watermark').css({visibility: 'visible'});
430
-            if(currentSmallVideo)
431
-                currentSmallVideo.enableDominantSpeaker(true);
432
-        }
433
-        else {
434
-            $('#largeVideoWrapper').css({visibility: 'hidden'});
435
-            $('#activeSpeaker').css('visibility', 'hidden');
436
-            $('.watermark').css({visibility: 'hidden'});
437
-            if(currentSmallVideo)
438
-                currentSmallVideo.enableDominantSpeaker(false);
293
+            leftWatermarkDiv.parent().attr('href', interfaceConfig.JITSI_WATERMARK_LINK);
439
         }
294
         }
440
-    },
441
-    onVideoTypeChanged: function (id, newVideoType) {
442
-        if (!isEnabled)
443
-            return;
444
-        if (LargeVideo.isCurrentlyOnLarge(id)) {
445
-            LargeVideo.updateVideoSizeAndPosition(newVideoType);
446
 
295
 
447
-            this.position(null, null, null, null, true);
296
+        if (interfaceConfig.SHOW_BRAND_WATERMARK) {
297
+            let rightWatermarkDiv = this.$container.find("div.watermark.rightwatermark");
298
+
299
+            rightWatermarkDiv.css({
300
+                display: 'block',
301
+                backgroundImage: 'url(images/rightwatermark.png)'
302
+            });
303
+
304
+            rightWatermarkDiv.parent().attr('href', interfaceConfig.BRAND_WATERMARK_LINK);
448
         }
305
         }
449
-    },
450
-    /**
451
-     * Positions the large video.
452
-     *
453
-     * @param videoWidth the stream video width
454
-     * @param videoHeight the stream video height
455
-     */
456
-    position: function (videoWidth, videoHeight,
457
-                        videoSpaceWidth, videoSpaceHeight, animate) {
458
-        if(!isEnabled)
459
-            return;
460
-        if(!videoSpaceWidth)
461
-            videoSpaceWidth = $('#videospace').width();
462
-        if(!videoSpaceHeight)
463
-            videoSpaceHeight = window.innerHeight;
464
-
465
-        var videoSize = getVideoSize(videoWidth,
466
-            videoHeight,
467
-            videoSpaceWidth,
468
-            videoSpaceHeight);
469
-
470
-        var largeVideoWidth = videoSize[0];
471
-        var largeVideoHeight = videoSize[1];
472
-
473
-        var videoPosition = getVideoPosition(largeVideoWidth,
474
-            largeVideoHeight,
475
-            videoSpaceWidth,
476
-            videoSpaceHeight);
477
-
478
-        var horizontalIndent = videoPosition[0];
479
-        var verticalIndent = videoPosition[1];
480
-
481
-        positionVideo($('#largeVideoWrapper'),
482
-            largeVideoWidth,
483
-            largeVideoHeight,
484
-            horizontalIndent, verticalIndent, animate);
485
-    },
486
-    /**
487
-     * Resizes the large html elements.
488
-     *
489
-     * @param animate boolean property that indicates whether the resize should
490
-     * be animated or not.
491
-     * @param isSideBarVisible boolean property that indicates whether the chat
492
-     * area is displayed or not.
493
-     * If that parameter is null the method will check the chat panel
494
-     * visibility.
495
-     * @param completeFunction a function to be called when the video space is
496
-     * resized
497
-     * @returns {*[]} array with the current width and height values of the
498
-     * largeVideo html element.
499
-     */
500
-    resize: function (animate, isSideBarVisible, completeFunction) {
501
-        if(!isEnabled)
502
-            return;
503
-        var availableHeight = window.innerHeight;
504
-        var availableWidth = UIUtil.getAvailableVideoWidth(isSideBarVisible);
505
-
506
-        if (availableWidth < 0 || availableHeight < 0) return;
507
-
508
-        var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
509
-        var top = availableHeight / 2 - avatarSize / 4 * 3;
510
-        $('#activeSpeaker').css('top', top);
511
-
512
-        this.VideoLayout
513
-            .resizeVideoSpace(animate, isSideBarVisible, completeFunction);
514
-        if(animate) {
515
-            $('#largeVideoContainer').animate({
516
-                    width: availableWidth,
517
-                    height: availableHeight
518
-                },
519
-                {
520
-                    queue: false,
521
-                    duration: 500
522
-                });
523
-        } else {
524
-            $('#largeVideoContainer').width(availableWidth);
525
-            $('#largeVideoContainer').height(availableHeight);
306
+
307
+        if (interfaceConfig.SHOW_POWERED_BY) {
308
+            this.$container.children("a.poweredby").css({display: 'block'});
526
         }
309
         }
527
-        return [availableWidth, availableHeight];
528
-    },
529
-    /**
530
-     * Resizes the large video.
531
-     *
532
-     * @param isSideBarVisible indicating if the side bar is visible
533
-     * @param completeFunction the callback function to be executed after the
534
-     * resize
535
-     */
536
-    resizeVideoAreaAnimated: function (isSideBarVisible, completeFunction) {
537
-        if(!isEnabled)
538
-            return;
539
-        var size = this.resize(true, isSideBarVisible, completeFunction);
540
-        this.position(null, null, size[0], size[1], true);
541
-    },
542
-    /**
543
-     * Updates the video size and position.
544
-     *
545
-     * @param videoType the video type indicating if the stream is of type
546
-     * desktop or web cam
547
-     */
548
-    updateVideoSizeAndPosition: function (videoType) {
549
-        if (!videoType)
550
-            videoType = currentSmallVideo.getVideoType();
551
-
552
-        var isDesktop = videoType === 'desktop';
553
-
554
-        // Change the way we'll be measuring and positioning large video
555
-        getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize;
556
-        getVideoPosition = isDesktop ? getDesktopVideoPosition :
557
-            getCameraVideoPosition;
558
-    },
559
-    getId: function () {
560
-        return currentSmallVideo ? currentSmallVideo.id : null;
561
-    },
562
-    updateAvatar: function (id) {
563
-        if (!isEnabled) {
310
+
311
+        this.$container.hover(
312
+            e => this.onHoverIn(e),
313
+            e => this.onHoverOut(e)
314
+        );
315
+    }
316
+
317
+    onHoverIn (e) {
318
+        if (!this.state) {
564
             return;
319
             return;
565
         }
320
         }
566
-        if (id === this.getId()) {
567
-            updateActiveSpeakerAvatarSrc();
568
-        }
569
-    },
570
-    showAvatar: function (id, show) {
571
-        if (!isEnabled) {
321
+        let container = this.getContainer(this.state);
322
+        container.onHoverIn(e);
323
+    }
324
+
325
+    onHoverOut (e) {
326
+        if (!this.state) {
572
             return;
327
             return;
573
         }
328
         }
574
-        if (this.getId() === id && state === "video") {
575
-            $("#largeVideoWrapper").css("visibility", show ? "hidden" : "visible");
576
-            $('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
577
-            return true;
578
-        }
579
-        return false;
580
-    },
581
-    /**
582
-     * Disables the large video
583
-     */
584
-    disable: function () {
585
-        isEnabled = false;
586
-    },
587
-    /**
588
-     * Enables the large video
589
-     */
590
-    enable: function () {
591
-        isEnabled = true;
592
-    },
593
-    /**
594
-     * Returns true if the video is enabled.
595
-     */
596
-    isEnabled: function () {
597
-        return isEnabled;
598
-    },
599
-    /**
600
-     * Creates the iframe used by the etherpad
601
-     * @param src the value for src attribute
602
-     * @param onloadHandler handler executed when the iframe loads it content
603
-     * @returns {HTMLElement} the iframe
604
-     */
605
-    createEtherpadIframe: function (src, onloadHandler) {
606
-        if(!isEnabled)
607
-            return;
329
+        let container = this.getContainer(this.state);
330
+        container.onHoverOut(e);
331
+    }
608
 
332
 
609
-        var etherpadIFrame = document.createElement('iframe');
610
-        etherpadIFrame.src = src;
611
-        etherpadIFrame.frameBorder = 0;
612
-        etherpadIFrame.scrolling = "no";
613
-        etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
614
-        etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
615
-        etherpadIFrame.setAttribute('style', 'visibility: hidden;');
333
+    get id () {
334
+        return this.videoContainer.id;
335
+    }
616
 
336
 
617
-        document.getElementById('etherpad').appendChild(etherpadIFrame);
337
+    updateLargeVideo (stream) {
338
+        let id = getStreamId(stream);
618
 
339
 
619
-        etherpadIFrame.onload = onloadHandler;
340
+        let container = this.getContainer(this.state);
620
 
341
 
621
-        return etherpadIFrame;
622
-    },
623
-    /**
624
-     * Changes the state of the large video.
625
-     * Possible values - video, prezi, etherpad.
626
-     * @param newState - the new state
627
-     */
628
-    setState: function (newState) {
629
-        if(state === newState)
630
-            return;
631
-        var currentContainer = getContainerByState(state);
632
-        if(!currentContainer)
633
-            return;
342
+        container.hide().then(() => {
343
+            console.info("hover in %s", id);
344
+            this.state = VideoContainerType;
345
+            this.videoContainer.setStream(stream);
346
+            this.videoContainer.show();
347
+        });
348
+    }
634
 
349
 
635
-        var self = this;
636
-        var oldState = state;
637
-
638
-        switch (newState)
639
-        {
640
-            case "etherpad":
641
-                $('#activeSpeaker').css('visibility', 'hidden');
642
-                currentContainer.fadeOut(300, function () {
643
-                    if (oldState === "prezi") {
644
-                        currentContainer.css({opacity: '0'});
645
-                        $('#reloadPresentation').css({display: 'none'});
646
-                    }
647
-                    else {
648
-                        self.setLargeVideoVisible(false);
649
-                    }
650
-                });
651
-
652
-                $('#etherpad>iframe').fadeIn(300, function () {
653
-                    document.body.style.background = '#eeeeee';
654
-                    $('#etherpad>iframe').css({visibility: 'visible'});
655
-                    $('#etherpad').css({zIndex: 2});
656
-                });
657
-                break;
658
-            case "prezi":
659
-                var prezi = $('#presentation>iframe');
660
-                currentContainer.fadeOut(300, function() {
661
-                    document.body.style.background = 'black';
662
-                });
663
-                prezi.fadeIn(300, function() {
664
-                    prezi.css({opacity:'1'});
665
-                    ToolbarToggler.dockToolbar(true);//fix that
666
-                    self.setLargeVideoVisible(false);
667
-                    $('#etherpad>iframe').css({visibility: 'hidden'});
668
-                    $('#etherpad').css({zIndex: 0});
669
-                });
670
-                $('#activeSpeaker').css('visibility', 'hidden');
671
-                break;
672
-
673
-            case "video":
674
-                currentContainer.fadeOut(300, function () {
675
-                    $('#presentation>iframe').css({opacity:'0'});
676
-                    $('#reloadPresentation').css({display:'none'});
677
-                    $('#etherpad>iframe').css({visibility: 'hidden'});
678
-                    $('#etherpad').css({zIndex: 0});
679
-                    document.body.style.background = 'black';
680
-                    ToolbarToggler.dockToolbar(false);//fix that
681
-                });
682
-                $('#largeVideoWrapper').fadeIn(300, function () {
683
-                    self.setLargeVideoVisible(true);
684
-                });
685
-                break;
686
-        }
350
+    updateContainerSize (isSideBarVisible) {
351
+        this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
352
+        this.height = window.innerHeight;
353
+    }
687
 
354
 
688
-        state = newState;
355
+    resizeContainer (type, animate = false) {
356
+        let container = this.getContainer(type);
357
+        container.resize(this.width, this.height, animate);
358
+    }
689
 
359
 
690
-    },
691
-    /**
692
-     * Returns the current state of the large video.
693
-     * @returns {string} the current state - video, prezi or etherpad.
694
-     */
695
-    getState: function () {
696
-        return state;
697
-    },
698
-    /**
699
-     * Sets hover handlers for the large video container div.
700
-     *
701
-     * @param inHandler
702
-     * @param outHandler
703
-     */
704
-    setHover: function(inHandler, outHandler)
705
-    {
706
-        $('#largeVideoContainer').hover(inHandler, outHandler);
707
-    },
360
+    resize (animate) {
361
+        // resize all containers
362
+        Object.keys(this.containers).forEach(type => this.resizeContainer(type, animate));
363
+
364
+        this.$container.animate({
365
+            width: this.width,
366
+            height: this.height
367
+        }, {
368
+            queue: false,
369
+            duration: animate ? 500 : 0
370
+        });
371
+    }
708
 
372
 
709
     /**
373
     /**
710
      * Enables/disables the filter indicating a video problem to the user.
374
      * Enables/disables the filter indicating a video problem to the user.
711
      *
375
      *
712
      * @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
376
      * @param enable <tt>true</tt> to enable, <tt>false</tt> to disable
713
      */
377
      */
714
-    enableVideoProblemFilter: function (enable) {
715
-        $("#largeVideo").toggleClass("videoProblemFilter", enable);
378
+    enableVideoProblemFilter (enable) {
379
+        this.videoContainer.$video.toggleClass("videoProblemFilter", enable);
380
+    }
381
+
382
+    /**
383
+     * Updates the src of the active speaker avatar
384
+     */
385
+    updateAvatar (thumbUrl) {
386
+        $("#activeSpeakerAvatar").attr('src', thumbUrl);
387
+    }
388
+
389
+    showAvatar (show) {
390
+        this.videoContainer.showAvatar(show);
716
     }
391
     }
717
-};
718
 
392
 
719
-export default LargeVideo;
393
+    addContainer (type, container) {
394
+        if (this.containers[type]) {
395
+            throw new Error(`container of type ${type} already exist`);
396
+        }
397
+
398
+        this.containers[type] = container;
399
+        this.resizeContainer(type);
400
+    }
401
+
402
+    getContainer (type) {
403
+        let container = this.containers[type];
404
+
405
+        if (!container) {
406
+            throw new Error(`container of type ${type} doesn't exist`);
407
+        }
408
+
409
+        return container;
410
+    }
411
+
412
+    removeContainer (type) {
413
+        if (!this.containers[type]) {
414
+            throw new Error(`container of type ${type} doesn't exist`);
415
+        }
416
+
417
+        delete this.containers[type];
418
+    }
419
+
420
+    showContainer (type) {
421
+        if (this.state === type) {
422
+            return Promise.resolve();
423
+        }
424
+
425
+        let container = this.getContainer(type);
426
+
427
+        if (this.state) {
428
+            let oldContainer = this.containers[this.state];
429
+            if (oldContainer) {
430
+                oldContainer.hide();
431
+            }
432
+        }
433
+
434
+        this.state = type;
435
+
436
+        return container.show();
437
+    }
438
+}

+ 7
- 4
modules/UI/videolayout/SmallVideo.js Ver arquivo

145
         function () {
145
         function () {
146
             // If the video has been "pinned" by the user we want to
146
             // If the video has been "pinned" by the user we want to
147
             // keep the display name on place.
147
             // keep the display name on place.
148
-            if (!LargeVideo.isLargeVideoVisible() ||
149
-                !LargeVideo.isCurrentlyOnLarge(self.id))
148
+            if (!self.VideoLayout.isLargeVideoVisible() ||
149
+                !self.VideoLayout.isCurrentlyOnLarge(self.id))
150
                 self.showDisplayName(false);
150
                 self.showDisplayName(false);
151
         }
151
         }
152
     );
152
     );
254
     }
254
     }
255
 
255
 
256
     if (isEnable) {
256
     if (isEnable) {
257
-        this.showDisplayName(LargeVideo.getState() === "video");
257
+        this.showDisplayName(this.VideoLayout.isLargeVideoVisible());
258
 
258
 
259
         if (!this.container.classList.contains("dominantspeaker"))
259
         if (!this.container.classList.contains("dominantspeaker"))
260
             this.container.classList.add("dominantspeaker");
260
             this.container.classList.add("dominantspeaker");
388
         }
388
         }
389
     }
389
     }
390
 
390
 
391
-    if (LargeVideo.showAvatar(this.id, show)) {
391
+    if (this.VideoLayout.isCurrentlyOnLarge(this.id)
392
+        && this.VideoLayout.isLargeVideoVisible()) {
393
+
394
+        this.VideoLayout.showLargeVideoAvatar(show);
392
         setVisibility(avatar, false);
395
         setVisibility(avatar, false);
393
         setVisibility(video, false);
396
         setVisibility(video, false);
394
     } else {
397
     } else {

+ 157
- 142
modules/UI/videolayout/VideoLayout.js Ver arquivo

2
 /* jshint -W101 */
2
 /* jshint -W101 */
3
 
3
 
4
 import AudioLevels from "../audio_levels/AudioLevels";
4
 import AudioLevels from "../audio_levels/AudioLevels";
5
+import BottomToolbar from "../toolbars/BottomToolbar";
5
 
6
 
6
 import UIEvents from "../../../service/UI/UIEvents";
7
 import UIEvents from "../../../service/UI/UIEvents";
7
 import UIUtil from "../util/UIUtil";
8
 import UIUtil from "../util/UIUtil";
8
 
9
 
9
 import RemoteVideo from "./RemoteVideo";
10
 import RemoteVideo from "./RemoteVideo";
10
-import LargeVideo from "./LargeVideo";
11
+import LargeVideoManager, {VideoContainerType} from "./LargeVideo";
12
+import {PreziContainerType} from '../prezi/Prezi';
11
 import LocalVideo from "./LocalVideo";
13
 import LocalVideo from "./LocalVideo";
12
 
14
 
13
 var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
15
 var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
88
     }
90
     }
89
 }
91
 }
90
 
92
 
93
+let largeVideo;
91
 
94
 
92
 var VideoLayout = {
95
 var VideoLayout = {
93
     init (emitter) {
96
     init (emitter) {
94
         eventEmitter = emitter;
97
         eventEmitter = emitter;
95
         localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
98
         localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
96
-        if (interfaceConfig.filmStripOnly) {
97
-            LargeVideo.disable();
98
-        } else {
99
-            LargeVideo.init(VideoLayout, emitter);
100
-        }
101
-
102
-        VideoLayout.resizeLargeVideoContainer();
103
 
99
 
104
         emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
100
         emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
105
     },
101
     },
106
 
102
 
103
+    initLargeVideo (isSideBarVisible) {
104
+        largeVideo = new LargeVideoManager();
105
+        largeVideo.updateContainerSize(isSideBarVisible);
106
+        AudioLevels.init();
107
+    },
108
+
109
+    setAudioLevel(id, lvl) {
110
+        if (!largeVideo) {
111
+            return;
112
+        }
113
+        AudioLevels.updateAudioLevel(
114
+            id, lvl, largeVideo.id
115
+        );
116
+    },
117
+
107
     isInLastN (resource) {
118
     isInLastN (resource) {
108
         return lastNCount < 0 || // lastN is disabled
119
         return lastNCount < 0 || // lastN is disabled
109
              // lastNEndpoints cache not built yet
120
              // lastNEndpoints cache not built yet
147
         localVideoThumbnail.changeVideo(stream);
158
         localVideoThumbnail.changeVideo(stream);
148
 
159
 
149
         /* force update if we're currently being displayed */
160
         /* force update if we're currently being displayed */
150
-        if (LargeVideo.isCurrentlyOnLarge(localId)) {
151
-            LargeVideo.updateLargeVideo(localId, true);
161
+        if (this.isCurrentlyOnLarge(localId)) {
162
+            this.updateLargeVideo(localId, true);
152
         }
163
         }
153
     },
164
     },
154
 
165
 
156
         let id = APP.conference.localId;
167
         let id = APP.conference.localId;
157
         localVideoThumbnail.joined(id);
168
         localVideoThumbnail.joined(id);
158
 
169
 
159
-        if (!LargeVideo.id) {
160
-            LargeVideo.updateLargeVideo(id, true);
170
+        if (largeVideo && !largeVideo.id) {
171
+            this.updateLargeVideo(id, true);
161
         }
172
         }
162
     },
173
     },
163
 
174
 
183
      * another one instead.
194
      * another one instead.
184
      */
195
      */
185
     updateRemovedVideo (id) {
196
     updateRemovedVideo (id) {
186
-        if (id !== LargeVideo.getId()) {
197
+        if (!this.isCurrentlyOnLarge(id)) {
187
             return;
198
             return;
188
         }
199
         }
189
 
200
 
198
             newId = this.electLastVisibleVideo();
209
             newId = this.electLastVisibleVideo();
199
         }
210
         }
200
 
211
 
201
-        LargeVideo.updateLargeVideo(newId);
212
+        this.updateLargeVideo(newId);
202
     },
213
     },
203
 
214
 
204
     electLastVisibleVideo () {
215
     electLastVisibleVideo () {
242
         remoteVideos[id].addRemoteStreamElement(stream);
253
         remoteVideos[id].addRemoteStreamElement(stream);
243
     },
254
     },
244
 
255
 
245
-    getLargeVideoId () {
246
-        return LargeVideo.getId();
247
-    },
248
-
249
     /**
256
     /**
250
      * Return the type of the remote video.
257
      * Return the type of the remote video.
251
      * @param id the id for the remote video
258
      * @param id the id for the remote video
255
         return remoteVideoTypes[id];
262
         return remoteVideoTypes[id];
256
     },
263
     },
257
 
264
 
258
-    /**
259
-     * Called when large video update is finished
260
-     * @param currentSmallVideo small video currently displayed on large video
261
-     */
262
-    largeVideoUpdated (currentSmallVideo) {
263
-        // Makes sure that dominant speaker UI
264
-        // is enabled only on current small video
265
-        localVideoThumbnail.enableDominantSpeaker(localVideoThumbnail === currentSmallVideo);
266
-        Object.keys(remoteVideos).forEach(
267
-            function (resourceJid) {
268
-                var remoteVideo = remoteVideos[resourceJid];
269
-                if (remoteVideo) {
270
-                    remoteVideo.enableDominantSpeaker(
271
-                        remoteVideo === currentSmallVideo);
272
-                }
273
-            }
274
-        );
275
-    },
276
-
277
     handleVideoThumbClicked (noPinnedEndpointChangedEvent,
265
     handleVideoThumbClicked (noPinnedEndpointChangedEvent,
278
                                           resourceJid) {
266
                                           resourceJid) {
279
         if(focusedVideoResourceJid) {
267
         if(focusedVideoResourceJid) {
291
             // Enable the currently set dominant speaker.
279
             // Enable the currently set dominant speaker.
292
             if (currentDominantSpeaker) {
280
             if (currentDominantSpeaker) {
293
                 if(smallVideo && smallVideo.hasVideo()) {
281
                 if(smallVideo && smallVideo.hasVideo()) {
294
-                    LargeVideo.updateLargeVideo(currentDominantSpeaker);
282
+                    this.updateLargeVideo(currentDominantSpeaker);
295
                 }
283
                 }
296
             }
284
             }
297
 
285
 
314
             }
302
             }
315
         }
303
         }
316
 
304
 
317
-        LargeVideo.setState("video");
318
-
319
-        LargeVideo.updateLargeVideo(resourceJid);
305
+        this.updateLargeVideo(resourceJid);
320
 
306
 
321
         // Writing volume not allowed in IE
307
         // Writing volume not allowed in IE
322
         if (!RTCBrowserType.isIExplorer()) {
308
         if (!RTCBrowserType.isIExplorer()) {
370
         // the current dominant speaker.
356
         // the current dominant speaker.
371
         if ((!focusedVideoResourceJid &&
357
         if ((!focusedVideoResourceJid &&
372
             !currentDominantSpeaker &&
358
             !currentDominantSpeaker &&
373
-            !require("../prezi/Prezi").isPresentationVisible()) ||
359
+             !this.isLargeContainerTypeVisible(PreziContainerType)) ||
374
             focusedVideoResourceJid === resourceJid ||
360
             focusedVideoResourceJid === resourceJid ||
375
             (resourceJid &&
361
             (resourceJid &&
376
                 currentDominantSpeaker === resourceJid)) {
362
                 currentDominantSpeaker === resourceJid)) {
377
-            LargeVideo.updateLargeVideo(resourceJid, true);
363
+            this.updateLargeVideo(resourceJid, true);
378
         }
364
         }
379
     },
365
     },
380
 
366
 
419
     /**
405
     /**
420
      * Resizes the large video container.
406
      * Resizes the large video container.
421
      */
407
      */
422
-    resizeLargeVideoContainer () {
423
-        if(LargeVideo.isEnabled()) {
424
-            LargeVideo.resize();
408
+    resizeLargeVideoContainer (isSideBarVisible) {
409
+        if (largeVideo) {
410
+            largeVideo.updateContainerSize(isSideBarVisible);
411
+            largeVideo.resize(false);
425
         } else {
412
         } else {
426
-            VideoLayout.resizeVideoSpace();
413
+            this.resizeVideoSpace(false, isSideBarVisible);
427
         }
414
         }
428
-        VideoLayout.resizeThumbnails();
429
-        LargeVideo.position();
415
+        this.resizeThumbnails(false);
430
     },
416
     },
431
 
417
 
432
     /**
418
     /**
433
      * Resizes thumbnails.
419
      * Resizes thumbnails.
434
      */
420
      */
435
-    resizeThumbnails (animate) {
436
-        var videoSpaceWidth = $('#remoteVideos').width();
421
+    resizeThumbnails (animate = false) {
422
+        let videoSpaceWidth = $('#remoteVideos').width();
437
 
423
 
438
-        var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
439
-        var width = thumbnailSize[0];
440
-        var height = thumbnailSize[1];
424
+        let [width, height] = this.calculateThumbnailSize(videoSpaceWidth);
441
 
425
 
442
         $('.userAvatar').css('left', (width - height) / 2);
426
         $('.userAvatar').css('left', (width - height) / 2);
443
 
427
 
444
-        if(animate) {
445
-            $('#remoteVideos').animate({
446
-                    // adds 2 px because of small video 1px border
447
-                    height: height + 2
448
-                },
449
-                {
450
-                    queue: false,
451
-                    duration: 500
452
-                });
453
-
454
-            $('#remoteVideos>span').animate({
455
-                    height: height,
456
-                    width: width
457
-                },
458
-                {
459
-                    queue: false,
460
-                    duration: 500,
461
-                    complete: function () {
462
-                        $(document).trigger(
463
-                            "remotevideo.resized",
464
-                            [width,
465
-                                height]);
466
-                    }
467
-                });
468
-
469
-        } else {
470
-            // size videos so that while keeping AR and max height, we have a
471
-            // nice fit
428
+        $('#remoteVideos').animate({
472
             // adds 2 px because of small video 1px border
429
             // adds 2 px because of small video 1px border
473
-            $('#remoteVideos').height(height + 2);
474
-            $('#remoteVideos>span').width(width);
475
-            $('#remoteVideos>span').height(height);
430
+            height: height + 2
431
+        }, {
432
+            queue: false,
433
+            duration: animate ? 500 : 0
434
+        });
476
 
435
 
477
-            $(document).trigger("remotevideo.resized", [width, height]);
478
-        }
436
+        $('#remoteVideos>span').animate({
437
+            height, width
438
+        }, {
439
+            queue: false,
440
+            duration: animate ? 500 : 0,
441
+            complete: function () {
442
+                BottomToolbar.onRemoteVideoResized(width, height);
443
+                AudioLevels.onRemoteVideoResized(width, height);
444
+            }
445
+        });
479
     },
446
     },
480
 
447
 
481
     /**
448
     /**
604
             // Update the large video if the video source is already available,
571
             // Update the large video if the video source is already available,
605
             // otherwise wait for the "videoactive.jingle" event.
572
             // otherwise wait for the "videoactive.jingle" event.
606
             if (videoSel[0].currentTime > 0) {
573
             if (videoSel[0].currentTime > 0) {
607
-                LargeVideo.updateLargeVideo(id);
574
+                this.updateLargeVideo(id);
608
             }
575
             }
609
         }
576
         }
610
     },
577
     },
696
                 // displayed in the large video we have to switch to another
663
                 // displayed in the large video we have to switch to another
697
                 // user.
664
                 // user.
698
                 if (!updateLargeVideo &&
665
                 if (!updateLargeVideo &&
699
-                    resourceJid === LargeVideo.getId()) {
666
+                    this.isCurrentlyOnLarge(resourceJid)) {
700
                     updateLargeVideo = true;
667
                     updateLargeVideo = true;
701
                 }
668
                 }
702
             }
669
             }
754
                     continue;
721
                     continue;
755
 
722
 
756
                 // videoSrcToSsrc needs to be update for this call to succeed.
723
                 // videoSrcToSsrc needs to be update for this call to succeed.
757
-                LargeVideo.updateLargeVideo(resource);
724
+                this.updateLargeVideo(resource);
758
                 break;
725
                 break;
759
             }
726
             }
760
         }
727
         }
862
         }
829
         }
863
 
830
 
864
         smallVideo.setVideoType(newVideoType);
831
         smallVideo.setVideoType(newVideoType);
865
-        LargeVideo.onVideoTypeChanged(id, newVideoType);
866
-    },
867
-
868
-    /**
869
-     * Updates the video size and position.
870
-     */
871
-    updateLargeVideoSize () {
872
-        LargeVideo.updateVideoSizeAndPosition();
873
-        LargeVideo.position(null, null, null, null, true);
832
+        if (this.isCurrentlyOnLarge(id)) {
833
+            this.updateLargeVideo(id, true);
834
+        }
874
     },
835
     },
875
 
836
 
876
     showMore (jid) {
837
     showMore (jid) {
886
         }
847
         }
887
     },
848
     },
888
 
849
 
889
-    addPreziContainer (id) {
890
-        var container = RemoteVideo.createContainer(id);
891
-        VideoLayout.resizeThumbnails();
892
-        return container;
893
-    },
894
-
895
-    setLargeVideoVisible (isVisible) {
896
-        LargeVideo.setLargeVideoVisible(isVisible);
897
-        if(!isVisible && focusedVideoResourceJid) {
898
-            var smallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
899
-            if(smallVideo) {
900
-                smallVideo.focus(false);
901
-                smallVideo.showAvatar();
902
-            }
903
-            focusedVideoResourceJid = null;
904
-        }
850
+    addRemoteVideoContainer (id) {
851
+        return RemoteVideo.createContainer(id);
905
     },
852
     },
906
 
853
 
907
     /**
854
     /**
912
      * resized.
859
      * resized.
913
      */
860
      */
914
     resizeVideoArea (isSideBarVisible, callback) {
861
     resizeVideoArea (isSideBarVisible, callback) {
915
-        LargeVideo.resizeVideoAreaAnimated(isSideBarVisible, callback);
916
-        VideoLayout.resizeThumbnails(true);
862
+        let animate = true;
863
+
864
+        if (largeVideo) {
865
+            largeVideo.updateContainerSize(isSideBarVisible);
866
+            largeVideo.resize(animate);
867
+            this.resizeVideoSpace(animate, isSideBarVisible, callback);
868
+        }
869
+
870
+        VideoLayout.resizeThumbnails(animate);
917
     },
871
     },
918
 
872
 
919
     /**
873
     /**
945
                     complete: completeFunction
899
                     complete: completeFunction
946
                 });
900
                 });
947
         } else {
901
         } else {
948
-            $('#videospace').width(availableWidth);
949
-            $('#videospace').height(availableHeight);
902
+            $('#videospace').width(availableWidth).height(availableHeight);
950
         }
903
         }
951
 
904
 
952
     },
905
     },
968
                 "Missed avatar update - no small video yet for " + id
921
                 "Missed avatar update - no small video yet for " + id
969
             );
922
             );
970
         }
923
         }
971
-        LargeVideo.updateAvatar(id, thumbUrl);
972
-    },
973
-
974
-    createEtherpadIframe (src, onloadHandler) {
975
-        return LargeVideo.createEtherpadIframe(src, onloadHandler);
976
-    },
977
-
978
-    setLargeVideoState (state) {
979
-        LargeVideo.setState(state);
980
-    },
981
-
982
-    getLargeVideoState () {
983
-        return LargeVideo.getState();
984
-    },
985
-
986
-    setLargeVideoHover (inHandler, outHandler) {
987
-        LargeVideo.setHover(inHandler, outHandler);
924
+        if (this.isCurrentlyOnLarge(id)) {
925
+            largeVideo.updateAvatar(thumbUrl);
926
+        }
988
     },
927
     },
989
 
928
 
990
     /**
929
     /**
991
      * Indicates that the video has been interrupted.
930
      * Indicates that the video has been interrupted.
992
      */
931
      */
993
     onVideoInterrupted () {
932
     onVideoInterrupted () {
994
-        LargeVideo.enableVideoProblemFilter(true);
995
-        var reconnectingKey = "connection.RECONNECTING";
996
-        $('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
933
+        this.enableVideoProblemFilter(true);
934
+        let reconnectingKey = "connection.RECONNECTING";
997
         $('#videoConnectionMessage')
935
         $('#videoConnectionMessage')
998
-            .text(APP.translation.translateString(reconnectingKey));
999
-        $('#videoConnectionMessage').css({display: "block"});
936
+            .attr("data-i18n", reconnectingKey)
937
+            .text(APP.translation.translateString(reconnectingKey))
938
+            .css({display: "block"});
1000
     },
939
     },
1001
 
940
 
1002
     /**
941
     /**
1003
      * Indicates that the video has been restored.
942
      * Indicates that the video has been restored.
1004
      */
943
      */
1005
     onVideoRestored () {
944
     onVideoRestored () {
1006
-        LargeVideo.enableVideoProblemFilter(false);
945
+        this.enableVideoProblemFilter(false);
1007
         $('#videoConnectionMessage').css({display: "none"});
946
         $('#videoConnectionMessage').css({display: "none"});
947
+    },
948
+
949
+    enableVideoProblemFilter (enable) {
950
+        if (!largeVideo) {
951
+            return;
952
+        }
953
+
954
+        largeVideo.enableVideoProblemFilter(enable);
955
+    },
956
+
957
+    isLargeVideoVisible () {
958
+        return this.isLargeContainerTypeVisible(VideoContainerType);
959
+    },
960
+
961
+    isCurrentlyOnLarge (id) {
962
+        return largeVideo && largeVideo.id === id;
963
+    },
964
+
965
+    updateLargeVideo (id, forceUpdate) {
966
+        if (!largeVideo) {
967
+            return;
968
+        }
969
+        let isOnLarge = this.isCurrentlyOnLarge(id);
970
+        let currentId = largeVideo.id;
971
+
972
+        if (!isOnLarge || forceUpdate) {
973
+            if (id !== currentId) {
974
+                eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
975
+            }
976
+            if (currentId) {
977
+                let currentSmallVideo = this.getSmallVideo(currentId);
978
+                currentSmallVideo && currentSmallVideo.enableDominantSpeaker(false);
979
+            }
980
+
981
+            let smallVideo = this.getSmallVideo(id);
982
+
983
+            largeVideo.updateLargeVideo(smallVideo.stream);
984
+
985
+            smallVideo.enableDominantSpeaker(true);
986
+        } else if (currentId) {
987
+            let currentSmallVideo = this.getSmallVideo(currentId);
988
+            currentSmallVideo.showAvatar();
989
+        }
990
+    },
991
+
992
+    showLargeVideoAvatar (show) {
993
+        largeVideo && largeVideo.showAvatar(show);
994
+    },
995
+
996
+    addLargeVideoContainer (type, container) {
997
+        largeVideo && largeVideo.addContainer(type, container);
998
+    },
999
+
1000
+    removeLargeVideoContainer (type) {
1001
+        largeVideo && largeVideo.removeContainer(type);
1002
+    },
1003
+
1004
+    /**
1005
+     * @returns Promise
1006
+     */
1007
+    showLargeVideoContainer (type, show) {
1008
+        if (!largeVideo) {
1009
+            return Promise.reject();
1010
+        }
1011
+
1012
+        let isVisible = this.isLargeContainerTypeVisible(type);
1013
+        if (isVisible === show) {
1014
+            return Promise.resolve();
1015
+        }
1016
+
1017
+        // if !show then use default type - large video
1018
+        return largeVideo.showContainer(show ? type : VideoContainerType);
1019
+    },
1020
+
1021
+    isLargeContainerTypeVisible (type) {
1022
+        return largeVideo && largeVideo.state === type;
1008
     }
1023
     }
1009
 };
1024
 };
1010
 
1025
 

+ 3
- 1
service/UI/UIEvents.js Ver arquivo

2
     NICKNAME_CHANGED: "UI.nickname_changed",
2
     NICKNAME_CHANGED: "UI.nickname_changed",
3
     SELECTED_ENDPOINT: "UI.selected_endpoint",
3
     SELECTED_ENDPOINT: "UI.selected_endpoint",
4
     PINNED_ENDPOINT: "UI.pinned_endpoint",
4
     PINNED_ENDPOINT: "UI.pinned_endpoint",
5
-    LARGEVIDEO_INIT: "UI.largevideo_init",
6
     /**
5
     /**
7
      * Notifies that local user created text message.
6
      * Notifies that local user created text message.
8
      */
7
      */
22
     AUDIO_MUTED: "UI.audio_muted",
21
     AUDIO_MUTED: "UI.audio_muted",
23
     VIDEO_MUTED: "UI.video_muted",
22
     VIDEO_MUTED: "UI.video_muted",
24
     PREZI_CLICKED: "UI.prezi_clicked",
23
     PREZI_CLICKED: "UI.prezi_clicked",
24
+    SHARE_PREZI: "UI.share_prezi",
25
+    PREZI_SLIDE_CHANGED: "UI.prezi_slide_changed",
26
+    STOP_SHARING_PREZI: "UI.stop_sharing_prezi",
25
     ETHERPAD_CLICKED: "UI.etherpad_clicked",
27
     ETHERPAD_CLICKED: "UI.etherpad_clicked",
26
     ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
28
     ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
27
     USER_INVITED: "UI.user_invited",
29
     USER_INVITED: "UI.user_invited",

Carregando…
Cancelar
Salvar