incorrectly merged these
This commit is contained in:
parent
0deaadfd87
commit
eddef1e1b6
@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import {Header} from '/imports/ui/main/Header.jsx';
|
||||
import {Whiteboard} from '/imports/ui/whiteboard/Whiteboard.jsx';
|
||||
import {ChatComponent} from '/imports/ui/chat/Chat.jsx';
|
||||
|
||||
MainContainer = React.createClass({
|
||||
handleShield() {
|
||||
$('.tooltip').hide();
|
||||
toggleShield();
|
||||
return closeMenus();
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="testing">
|
||||
<Header />
|
||||
<div id="panels">
|
||||
<div onClick={this.handleShield} className="shield"></div>
|
||||
<Whiteboard />
|
||||
<ChatComponent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,895 +0,0 @@
|
||||
import { Users, Shapes, Meetings, Presentations, Slides, Chat, Cursor } from '/collections/collections';
|
||||
|
||||
this.getBuildInformation = function () {
|
||||
let copyrightYear, defaultWelcomeMessage, defaultWelcomeMessageFooter, html5ClientBuild, link, ref, ref1, ref2, ref3;
|
||||
copyrightYear = ((ref = Meteor.config) != null ? ref.copyrightYear : void 0) || 'DATE';
|
||||
html5ClientBuild = ((ref1 = Meteor.config) != null ? ref1.html5ClientBuild : void 0) || 'VERSION';
|
||||
defaultWelcomeMessage = ((ref2 = Meteor.config) != null ? ref2.defaultWelcomeMessage : void 0) || 'WELCOME MESSAGE';
|
||||
defaultWelcomeMessageFooter = ((ref3 = Meteor.config) != null ? ref3.defaultWelcomeMessageFooter : void 0) || 'WELCOME MESSAGE';
|
||||
link = "<a href='http://bigbluebutton.org/' target='_blank'>http://bigbluebutton.org</a>";
|
||||
return {
|
||||
copyrightYear: copyrightYear,
|
||||
html5ClientBuild: html5ClientBuild,
|
||||
defaultWelcomeMessage: defaultWelcomeMessage,
|
||||
defaultWelcomeMessageFooter: defaultWelcomeMessageFooter,
|
||||
link: link,
|
||||
};
|
||||
};
|
||||
|
||||
// Convert a color `value` as integer to a hex color (e.g. 255 to #0000ff)
|
||||
this.colourToHex = function (value) {
|
||||
let hex;
|
||||
hex = parseInt(value).toString(16);
|
||||
while (hex.length < 6) {
|
||||
hex = `0${hex}`;
|
||||
}
|
||||
|
||||
return `#${hex}`;
|
||||
};
|
||||
|
||||
// color can be a number (a hex converted to int) or a string (e.g. "#ffff00")
|
||||
this.formatColor = function (color) {
|
||||
if (color == null) {
|
||||
color = '0'; // default value
|
||||
}
|
||||
|
||||
if (!color.toString().match(/\#.*/)) {
|
||||
color = colourToHex(color);
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
this.getInSession = function (k) {
|
||||
return SessionAmplify.get(k);
|
||||
};
|
||||
|
||||
// returns epoch in ms
|
||||
this.getTime = function () {
|
||||
return (new Date).valueOf();
|
||||
};
|
||||
|
||||
// checks if the pan gesture is mostly horizontal
|
||||
this.isPanHorizontal = function (event) {
|
||||
return Math.abs(event.deltaX) > Math.abs(event.deltaY);
|
||||
};
|
||||
|
||||
// helper to determine whether the user is in the listen only audio stream
|
||||
Handlebars.registerHelper('isMyMicLocked', () => {
|
||||
return BBB.isMyMicLocked();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('colourToHex', (_this => {
|
||||
return function (value) {
|
||||
return _this.window.colourToHex(value);
|
||||
};
|
||||
})(this));
|
||||
|
||||
Handlebars.registerHelper('equals', (a, b) => { // equals operator was dropped in Meteor's migration from Handlebars to Spacebars
|
||||
return a === b;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getCurrentMeeting', () => {
|
||||
return Meetings.findOne();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getCurrentSlide', () => {
|
||||
let result;
|
||||
result = BBB.getCurrentSlide();
|
||||
|
||||
// console.log "result=#{JSON.stringify result}"
|
||||
return result;
|
||||
});
|
||||
|
||||
// Allow access through all templates
|
||||
Handlebars.registerHelper('getInSession', k => {
|
||||
return SessionAmplify.get(k);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getMeetingName', () => {
|
||||
return BBB.getMeetingName();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getShapesForSlide', () => {
|
||||
let currentSlide, ref;
|
||||
currentSlide = BBB.getCurrentSlide();
|
||||
|
||||
// try to reuse the lines above
|
||||
return Shapes.find({
|
||||
whiteboardId: currentSlide != null ? (ref = currentSlide.slide) != null ? ref.id : void 0 : void 0,
|
||||
});
|
||||
});
|
||||
|
||||
// retrieves all users in the meeting
|
||||
Handlebars.registerHelper('getUsersInMeeting', () => {
|
||||
let users;
|
||||
users = Users.find().fetch();
|
||||
if ((users != null ? users.length : void 0) > 1) {
|
||||
return getSortedUserList(users);
|
||||
} else {
|
||||
return users;
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getCurrentUserEmojiStatus', () => {
|
||||
let ref, ref1;
|
||||
return (ref = BBB.getCurrentUser()) != null ? (ref1 = ref.user) != null ? ref1.emoji_status : void 0 : void 0;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isCurrentUser', userId => {
|
||||
let ref;
|
||||
return userId === null || userId === ((ref = BBB.getCurrentUser()) != null ? ref.userId : void 0);
|
||||
});
|
||||
|
||||
//Retreives a username for a private chat tab from the database if it exists
|
||||
Handlebars.registerHelper('privateChatName', () => {
|
||||
let obj, ref;
|
||||
obj = Users.findOne({
|
||||
userId: getInSession('inChatWith'),
|
||||
});
|
||||
if (obj != null) {
|
||||
return obj != null ? (ref = obj.user) != null ? ref.name : void 0 : void 0;
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isCurrentUserEmojiStatusSet', () => {
|
||||
return BBB.isCurrentUserEmojiStatusSet();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isCurrentUserSharingVideo', () => {
|
||||
return BBB.amISharingVideo();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isCurrentUserPresenter', () => {
|
||||
return BBB.isUserPresenter(getInSession('userId'));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isCurrentUserModerator', () => {
|
||||
return BBB.getMyRole() === 'MODERATOR';
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isDisconnected', () => {
|
||||
return !Meteor.status().connected;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isUserInAudio', userId => {
|
||||
return BBB.isUserInAudio(userId);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isUserListenOnlyAudio', userId => {
|
||||
return BBB.isUserListenOnlyAudio(userId);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isUserMuted', userId => {
|
||||
return BBB.isUserMuted(userId);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isUserSharingVideo', userId => {
|
||||
return BBB.isUserSharingWebcam(userId);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isUserTalking', userId => {
|
||||
return BBB.isUserTalking(userId);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isMobile', () => {
|
||||
return isMobile();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isPortraitMobile', () => {
|
||||
return isPortraitMobile();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('isMobileChromeOrFirefox', () => {
|
||||
return isMobile() && ((getBrowserName() === 'Chrome') || (getBrowserName() === 'Firefox'));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('meetingIsRecording', () => {
|
||||
return BBB.isMeetingRecording();
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('messageFontSize', () => {
|
||||
return {
|
||||
style: `font-size: ${getInSession('messageFontSize')}px;`,
|
||||
};
|
||||
});
|
||||
|
||||
//#TODO REMOVE
|
||||
Handlebars.registerHelper('pointerLocation', () => {
|
||||
let currentPresentation, currentSlideDoc, pointer, presentationId, ref;
|
||||
currentPresentation = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
});
|
||||
presentationId = currentPresentation != null ? (ref = currentPresentation.presentation) != null ? ref.id : void 0 : void 0;
|
||||
currentSlideDoc = Slides.findOne({
|
||||
presentationId: presentationId,
|
||||
'slide.current': true,
|
||||
});
|
||||
pointer = Cursor.findOne();
|
||||
pointer.x = (-currentSlideDoc.slide.x_offset * 2 + currentSlideDoc.slide.width_ratio * pointer.x) / 100;
|
||||
pointer.y = (-currentSlideDoc.slide.y_offset * 2 + currentSlideDoc.slide.height_ratio * pointer.y) / 100;
|
||||
return pointer;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('safeName', str => {
|
||||
return safeString(str);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('canJoinWithMic', () => {
|
||||
if ((BBB.isUserPresenter(getInSession('userId')) || !Meteor.config.app.listenOnly) && !BBB.isMyMicLocked()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('containerPosition', section => {
|
||||
if (getInSession('display_usersList')) {
|
||||
return 'moved-to-right';
|
||||
} else if (getInSession('display_menu')) {
|
||||
return 'moved-to-left';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
Template.registerHelper('emojiIcons', function () {
|
||||
return [
|
||||
{ name: 'sad', icon: 'sad-face', title: '' },
|
||||
{ name: 'happy', icon: 'happy-face', title: '' },
|
||||
{ name: 'confused', icon: 'confused-face', title: '' },
|
||||
{ name: 'neutral', icon: 'neutral-face', title: '' },
|
||||
{ name: 'away', icon: 'clock', title: '' },
|
||||
{ name: 'raiseHand', icon: 'hand', title: 'Lower your Hand' },
|
||||
];
|
||||
});
|
||||
|
||||
// scale the whiteboard to adapt to the resized window
|
||||
this.scaleWhiteboard = function (whiteboardPaperModel) {
|
||||
let adjustedDimensions;
|
||||
adjustedDimensions = scaleSlide(getInSession('slideOriginalWidth'), getInSession('slideOriginalHeight'));
|
||||
if (typeof whiteboardPaperModel !== 'undefined' && whiteboardPaperModel !== null) {
|
||||
whiteboardPaperModel.scale(adjustedDimensions.width, adjustedDimensions.height);
|
||||
}
|
||||
/* if(callback) {
|
||||
callback();
|
||||
} */
|
||||
};
|
||||
|
||||
this.getSortedUserList = function (users) {
|
||||
if ((users != null ? users.length : void 0) > 1) {
|
||||
users.sort((a, b) => {
|
||||
let aTime, bTime;
|
||||
if (a.user.role === 'MODERATOR' && b.user.role === 'MODERATOR') {
|
||||
if (a.user.set_emoji_time && b.user.set_emoji_time) {
|
||||
aTime = a.user.set_emoji_time.getTime();
|
||||
bTime = b.user.set_emoji_time.getTime();
|
||||
if (aTime < bTime) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else if (a.user.set_emoji_time) {
|
||||
return -1;
|
||||
} else if (b.user.set_emoji_time) {
|
||||
return 1;
|
||||
}
|
||||
} else if (a.user.role === 'MODERATOR') {
|
||||
return -1;
|
||||
} else if (b.user.role === 'MODERATOR') {
|
||||
return 1;
|
||||
} else if (a.user.set_emoji_time && b.user.set_emoji_time) {
|
||||
aTime = a.user.set_emoji_time.getTime();
|
||||
bTime = b.user.set_emoji_time.getTime();
|
||||
if (aTime < bTime) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else if (a.user.set_emoji_time) {
|
||||
return -1;
|
||||
} else if (b.user.set_emoji_time) {
|
||||
return 1;
|
||||
} else if (!a.user.phone_user && !b.user.phone_user) {
|
||||
|
||||
} else if (!a.user.phone_user) {
|
||||
return -1;
|
||||
} else if (!b.user.phone_user) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Check name (case-insensitive) in the event of a tie up above. If the name
|
||||
//is the same then use userID which should be unique making the order the same
|
||||
//across all clients.
|
||||
|
||||
if (a.user._sort_name < b.user._sort_name) {
|
||||
return -1;
|
||||
} else if (a.user._sort_name > b.user._sort_name) {
|
||||
return 1;
|
||||
} else if (a.user.userid.toLowerCase() > b.user.userid.toLowerCase()) {
|
||||
return -1;
|
||||
} else if (a.user.userid.toLowerCase() < b.user.userid.toLowerCase()) {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return users;
|
||||
};
|
||||
|
||||
// transform plain text links into HTML tags compatible with Flash client
|
||||
this.linkify = function (str) {
|
||||
return str = str.replace(re_weburl, "<a href='event:$&'><u>$&</u></a>");
|
||||
};
|
||||
|
||||
this.setInSession = function (k, v) {
|
||||
return SessionAmplify.set(k, v);
|
||||
};
|
||||
|
||||
this.safeString = function (str) {
|
||||
if (typeof str === 'string') {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
};
|
||||
|
||||
this.toggleCam = function (event) {
|
||||
// Users.update {_id: context._id} , {$set:{"user.sharingVideo": !context.sharingVideo}}
|
||||
// Meteor.call('userToggleCam', context._id, !context.sharingVideo)
|
||||
};
|
||||
|
||||
this.toggleChatbar = function () {
|
||||
setInSession('display_chatbar', !getInSession('display_chatbar'));
|
||||
if (!getInSession('display_chatbar')) {
|
||||
$('#whiteboard').css('width', '100%');
|
||||
$('#whiteboard .ui-resizable-handle').css('display', 'none');
|
||||
} else {
|
||||
$('#whiteboard').css('width', '');
|
||||
$('#whiteboard .ui-resizable-handle').css('display', '');
|
||||
}
|
||||
|
||||
return setTimeout(scaleWhiteboard, 0);
|
||||
};
|
||||
|
||||
this.toggleMic = function (event) {
|
||||
return BBB.toggleMyMic();
|
||||
};
|
||||
|
||||
this.toggleUsersList = function () {
|
||||
if ($('.userlistMenu').hasClass('hiddenInLandscape')) {
|
||||
$('.userlistMenu').removeClass('hiddenInLandscape');
|
||||
} else {
|
||||
$('.userlistMenu').addClass('hiddenInLandscape');
|
||||
}
|
||||
|
||||
return setTimeout(scaleWhiteboard, 0);
|
||||
};
|
||||
|
||||
this.populateNotifications = function (msg) {
|
||||
let chat, chats, initChats, j, l, len, len1, myPrivateChats, myUserId, new_msg_userid, results, u, uniqueArray, users;
|
||||
myUserId = getInSession('userId');
|
||||
users = Users.find().fetch();
|
||||
|
||||
// assuming that I only have access only to private messages where I am the sender or the recipient
|
||||
myPrivateChats = Chat.find({
|
||||
'message.chat_type': 'PRIVATE_CHAT',
|
||||
}).fetch();
|
||||
uniqueArray = [];
|
||||
for (j = 0, len = myPrivateChats.length; j < len; j++) {
|
||||
chat = myPrivateChats[j];
|
||||
if (chat.message.to_userid === myUserId) {
|
||||
uniqueArray.push({
|
||||
userId: chat.message.from_userid,
|
||||
username: chat.message.from_username,
|
||||
});
|
||||
}
|
||||
|
||||
if (chat.message.from_userid === myUserId) {
|
||||
uniqueArray.push({
|
||||
userId: chat.message.to_userid,
|
||||
username: chat.message.to_username,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//keep unique entries only
|
||||
uniqueArray = uniqueArray.filter((itm, i, a) => {
|
||||
return i === a.indexOf(itm);
|
||||
});
|
||||
if (msg.message.to_userid === myUserId) {
|
||||
new_msg_userid = msg.message.from_userid;
|
||||
}
|
||||
|
||||
if (msg.message.from_userid === myUserId) {
|
||||
new_msg_userid = msg.message.to_userid;
|
||||
}
|
||||
|
||||
chats = getInSession('chats');
|
||||
if (chats === void 0) {
|
||||
initChats = [
|
||||
{
|
||||
userId: 'PUBLIC_CHAT',
|
||||
gotMail: false,
|
||||
number: 0,
|
||||
},
|
||||
];
|
||||
setInSession('chats', initChats);
|
||||
}
|
||||
|
||||
results = [];
|
||||
|
||||
//insert the unique entries in the collection
|
||||
for (l = 0, len1 = uniqueArray.length; l < len1; l++) {
|
||||
u = uniqueArray[l];
|
||||
chats = getInSession('chats');
|
||||
if (chats.filter(chat => {
|
||||
return chat.userId === u.userId;
|
||||
}).length === 0 && u.userId === new_msg_userid) {
|
||||
chats.push({
|
||||
userId: u.userId,
|
||||
gotMail: false,
|
||||
number: 0,
|
||||
});
|
||||
results.push(setInSession('chats', chats));
|
||||
} else {
|
||||
results.push(void 0);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
this.toggleShield = function () {
|
||||
if (parseFloat($('.shield').css('opacity')) === 0.5) {
|
||||
$('.shield').css('opacity', '');
|
||||
}
|
||||
|
||||
if (!$('.shield').hasClass('darken') && !$('.shield').hasClass('animatedShield')) {
|
||||
return $('.shield').addClass('darken');
|
||||
} else {
|
||||
$('.shield').removeClass('darken');
|
||||
return $('.shield').removeClass('animatedShield');
|
||||
}
|
||||
};
|
||||
|
||||
this.removeFullscreenStyles = function () {
|
||||
$('#whiteboard-paper').removeClass('vertically-centered');
|
||||
$('#chat').removeClass('invisible');
|
||||
$('#users').removeClass('invisible');
|
||||
$('#navbar').removeClass('invisible');
|
||||
$('.FABTriggerButton').removeClass('invisible');
|
||||
$('.fullscreenButton').removeClass('exitFullscreenButton');
|
||||
$('.fullscreenButton').addClass('whiteboardFullscreenButton');
|
||||
$('.fullscreenButton i').removeClass('ion-arrow-shrink');
|
||||
return $('.fullscreenButton i').addClass('ion-arrow-expand');
|
||||
};
|
||||
|
||||
this.enterWhiteboardFullscreen = function () {
|
||||
let element;
|
||||
element = document.getElementById('whiteboard');
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
element.mozRequestFullScreen();
|
||||
$('.fullscreenButton').addClass('iconFirefox');
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
element.webkitRequestFullscreen();
|
||||
$('.fullscreenButton').addClass('iconChrome');
|
||||
} else if (element.msRequestFullscreen) {
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
|
||||
$('#chat').addClass('invisible');
|
||||
$('#users').addClass('invisible');
|
||||
$('#navbar').addClass('invisible');
|
||||
$('.FABTriggerButton').addClass('invisible');
|
||||
$('.fullscreenButton').removeClass('whiteboardFullscreenButton');
|
||||
$('.fullscreenButton').addClass('exitFullscreenButton');
|
||||
$('.fullscreenButton i').removeClass('ion-arrow-expand');
|
||||
$('.fullscreenButton i').addClass('ion-arrow-shrink');
|
||||
$('#whiteboard-paper').addClass('vertically-centered');
|
||||
$('#whiteboard').bind('webkitfullscreenchange', e => {
|
||||
if (document.webkitFullscreenElement === null) {
|
||||
$('#whiteboard').unbind('webkitfullscreenchange');
|
||||
$('.fullscreenButton').removeClass('iconChrome');
|
||||
removeFullscreenStyles();
|
||||
return scaleWhiteboard();
|
||||
}
|
||||
});
|
||||
return $(document).bind('mozfullscreenchange', e => {
|
||||
if (document.mozFullScreenElement === null) {
|
||||
$(document).unbind('mozfullscreenchange');
|
||||
$('.fullscreenButton').removeClass('iconFirefox');
|
||||
removeFullscreenStyles();
|
||||
return scaleWhiteboard();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.closeMenus = function () {
|
||||
if ($('.userlistMenu').hasClass('menuOut')) {
|
||||
return toggleUserlistMenu();
|
||||
} else if ($('.settingsMenu').hasClass('menuOut')) {
|
||||
return toggleSettingsMenu();
|
||||
}
|
||||
};
|
||||
|
||||
// Periodically check the status of the WebRTC call, when a call has been established attempt to hangup,
|
||||
// retry if a call is in progress, send the leave voice conference message to BBB
|
||||
this.exitVoiceCall = function (event, afterExitCall) {
|
||||
let checkToHangupCall, hangupCallback;
|
||||
|
||||
// To be called when the hangup is initiated
|
||||
hangupCallback = function () {
|
||||
return console.log('Exiting Voice Conference');
|
||||
};
|
||||
|
||||
// Checks periodically until a call is established so we can successfully end the call
|
||||
// clean state
|
||||
getInSession('triedHangup', false);
|
||||
|
||||
// function to initiate call
|
||||
(checkToHangupCall = function (context) {
|
||||
// if an attempt to hang up the call is made when the current session is not yet finished, the request has no effect
|
||||
// keep track in the session if we haven't tried a hangup
|
||||
if (BBB.getCallStatus() !== null && !getInSession('triedHangup')) {
|
||||
console.log('Attempting to hangup on WebRTC call');
|
||||
if (BBB.amIListenOnlyAudio()) {
|
||||
Meteor.call(
|
||||
'listenOnlyRequestToggle',
|
||||
BBB.getMeetingId(),
|
||||
getInSession('userId'),
|
||||
getInSession('authToken'),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
BBB.leaveVoiceConference(hangupCallback);
|
||||
getInSession('triedHangup', true);
|
||||
notification_WebRTCAudioExited();
|
||||
if (afterExitCall) {
|
||||
return afterExitCall(this, Meteor.config.app.listenOnly);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
`RETRYING hangup on WebRTC call in ${Meteor.config.app.WebRTCHangupRetryInterval} ms`
|
||||
);
|
||||
return setTimeout(checkToHangupCall, Meteor.config.app.WebRTCHangupRetryInterval);
|
||||
}
|
||||
})(this); // automatically run function
|
||||
return false;
|
||||
};
|
||||
|
||||
// close the daudio UI, then join the conference. If listen only send the request to the server
|
||||
this.joinVoiceCall = function (event, arg) {
|
||||
let isListenOnly, joinCallback;
|
||||
isListenOnly = (arg != null ? arg : {}).isListenOnly;
|
||||
if (!isWebRTCAvailable()) {
|
||||
notification_WebRTCNotSupported();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isListenOnly == null) {
|
||||
isListenOnly = true;
|
||||
}
|
||||
|
||||
// create voice call params
|
||||
joinCallback = function (message) {
|
||||
return console.log('Beginning WebRTC Conference Call');
|
||||
};
|
||||
|
||||
notification_WebRTCAudioJoining();
|
||||
if (isListenOnly) {
|
||||
Meteor.call(
|
||||
'listenOnlyRequestToggle',
|
||||
BBB.getMeetingId(),
|
||||
getInSession('userId'),
|
||||
getInSession('authToken'),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
BBB.joinVoiceConference(joinCallback, isListenOnly); // make the call //TODO should we apply role permissions to this action?
|
||||
return false;
|
||||
};
|
||||
|
||||
// Starts the entire logout procedure.
|
||||
// meeting: the meeting the user is in
|
||||
// the user's userId
|
||||
this.userLogout = function (meeting, user) {
|
||||
Meteor.call('userLogout', meeting, user, getInSession('authToken'));
|
||||
console.log('logging out');
|
||||
return clearSessionVar(document.location = getInSession('logoutURL')); // navigate to logout
|
||||
};
|
||||
|
||||
this.kickUser = function (meetingId, toKickUserId, requesterUserId, authToken) {
|
||||
return Meteor.call('kickUser', meetingId, toKickUserId, requesterUserId, authToken);
|
||||
};
|
||||
|
||||
this.setUserPresenter = function (
|
||||
meetingId,
|
||||
newPresenterId,
|
||||
requesterSetPresenter,
|
||||
newPresenterName,
|
||||
authToken) {
|
||||
return Meteor.call('setUserPresenter', meetingId, newPresenterId, requesterSetPresenter, newPresenterName, authToken);
|
||||
};
|
||||
|
||||
// Clear the local user session
|
||||
this.clearSessionVar = function (callback) {
|
||||
amplify.store('authToken', null);
|
||||
amplify.store('bbbServerVersion', null);
|
||||
amplify.store('chats', null);
|
||||
amplify.store('dateOfBuild', null);
|
||||
amplify.store('display_chatPane', null);
|
||||
amplify.store('display_chatbar', null);
|
||||
amplify.store('display_navbar', null);
|
||||
amplify.store('display_usersList', null);
|
||||
amplify.store('display_whiteboard', null);
|
||||
amplify.store('inChatWith', null);
|
||||
amplify.store('logoutURL', null);
|
||||
amplify.store('meetingId', null);
|
||||
amplify.store('messageFontSize', null);
|
||||
amplify.store('tabsRenderedTime', null);
|
||||
amplify.store('userId', null);
|
||||
amplify.store('display_menu', null);
|
||||
if (callback != null) {
|
||||
return callback();
|
||||
}
|
||||
};
|
||||
|
||||
// assign the default values for the Session vars
|
||||
this.setDefaultSettings = function () {
|
||||
let initChats;
|
||||
setInSession('display_navbar', true);
|
||||
setInSession('display_chatbar', true);
|
||||
setInSession('display_whiteboard', true);
|
||||
setInSession('display_chatPane', true);
|
||||
|
||||
//if it is a desktop version of the client
|
||||
if (isPortraitMobile() || isLandscapeMobile()) {
|
||||
setInSession('messageFontSize', Meteor.config.app.mobileFont);
|
||||
} else { //if this is a mobile version of the client
|
||||
setInSession('messageFontSize', Meteor.config.app.desktopFont);
|
||||
}
|
||||
|
||||
setInSession('display_slidingMenu', false);
|
||||
setInSession('display_hiddenNavbarSection', false);
|
||||
if (isLandscape()) {
|
||||
setInSession('display_usersList', true);
|
||||
} else {
|
||||
setInSession('display_usersList', false);
|
||||
}
|
||||
|
||||
setInSession('display_menu', false);
|
||||
setInSession('chatInputMinHeight', 0);
|
||||
|
||||
//keep notifications and an opened private chat tab if page was refreshed
|
||||
//reset to default if that's a new user
|
||||
if (loginOrRefresh()) {
|
||||
initChats = [
|
||||
{
|
||||
userId: 'PUBLIC_CHAT',
|
||||
gotMail: false,
|
||||
number: 0,
|
||||
},
|
||||
];
|
||||
setInSession('chats', initChats);
|
||||
setInSession('inChatWith', 'PUBLIC_CHAT');
|
||||
}
|
||||
|
||||
return TimeSync.loggingEnabled = false; // suppresses the log messages from timesync
|
||||
};
|
||||
|
||||
//true if it is a new user, false if the client was just refreshed
|
||||
this.loginOrRefresh = function () {
|
||||
let checkId, userId;
|
||||
userId = getInSession('userId');
|
||||
checkId = getInSession('checkId');
|
||||
if (checkId === void 0) {
|
||||
setInSession('checkId', userId);
|
||||
return true;
|
||||
} else if (userId !== checkId) {
|
||||
setInSession('checkId', userId);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
this.onLoadComplete = function () {
|
||||
let ref;
|
||||
document.title = `BigBlueButton ${(ref = BBB.getMeetingName()) != null ? ref : 'HTML5'}`;
|
||||
setDefaultSettings();
|
||||
Users.find().observe({
|
||||
removed(oldDocument) {
|
||||
if (oldDocument.userId === getInSession('userId')) {
|
||||
return document.location = getInSession('logoutURL');
|
||||
}
|
||||
},
|
||||
});
|
||||
return Users.find().observe({
|
||||
changed(newUser, oldUser) {
|
||||
if (Meteor.config.app.listenOnly === true && newUser.user.presenter === false && oldUser.user.presenter === true && BBB.getCurrentUser().userId === newUser.userId && oldUser.user.listenOnly === false) {
|
||||
return exitVoiceCall(this, joinVoiceCall);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Detects a mobile device
|
||||
this.isMobile = function () {
|
||||
return navigator.userAgent.match(/Android/i) ||
|
||||
navigator.userAgent.match(/iPhone|iPad|iPod/i) ||
|
||||
navigator.userAgent.match(/BlackBerry/i) ||
|
||||
navigator.userAgent.match(/Windows Phone/i) ||
|
||||
navigator.userAgent.match(/IEMobile/i) ||
|
||||
navigator.userAgent.match(/BlackBerry/i) ||
|
||||
navigator.userAgent.match(/webOS/i);
|
||||
};
|
||||
|
||||
this.isLandscape = function () {
|
||||
return !isMobile() &&
|
||||
window.matchMedia('(orientation: landscape)').matches && // browser is landscape
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches; // device is landscape
|
||||
};
|
||||
|
||||
this.isPortrait = function () {
|
||||
return !isMobile() &&
|
||||
window.matchMedia('(orientation: portrait)').matches && // browser is portrait
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches; // device is landscape
|
||||
};
|
||||
|
||||
// Checks if the view is portrait and a mobile device is being used
|
||||
this.isPortraitMobile = function () {
|
||||
return isMobile() &&
|
||||
window.matchMedia('(orientation: portrait)').matches && // browser is portrait
|
||||
window.matchMedia('(max-device-aspect-ratio: 1/1)').matches; // device is portrait
|
||||
};
|
||||
|
||||
// Checks if the view is landscape and mobile device is being used
|
||||
this.isLandscapeMobile = function () {
|
||||
return isMobile() &&
|
||||
window.matchMedia('(orientation: landscape)').matches && // browser is landscape
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches; // device is landscape
|
||||
};
|
||||
|
||||
this.isLandscapePhone = function () {
|
||||
// @phone-landscape media query:
|
||||
return window.matchMedia('(orientation: landscape)').matches &&
|
||||
window.matchMedia('(min-device-aspect-ratio: 1/1)').matches &&
|
||||
window.matchMedia('(max-device-width: 959px)').matches;
|
||||
};
|
||||
|
||||
this.isPortraitPhone = function () {
|
||||
// @phone-portrait media query:
|
||||
return (window.matchMedia('(orientation: portrait)').matches &&
|
||||
window.matchMedia('(max-device-aspect-ratio: 1/1)').matches &&
|
||||
window.matchMedia('(max-device-width: 480px)').matches) ||
|
||||
|
||||
// @phone-portrait-with-keyboard media query:
|
||||
(window.matchMedia('(orientation: landscape)').matches &&
|
||||
window.matchMedia('(max-device-aspect-ratio: 1/1)').matches &&
|
||||
window.matchMedia('(max-device-width: 480px)').matches);
|
||||
};
|
||||
|
||||
this.isPhone = function () {
|
||||
return isLandscapePhone() || isPortraitPhone();
|
||||
};
|
||||
|
||||
// The webpage orientation is now landscape
|
||||
this.orientationBecameLandscape = function () {
|
||||
return adjustChatInputHeight();
|
||||
};
|
||||
|
||||
// The webpage orientation is now portrait
|
||||
this.orientationBecamePortrait = function () {
|
||||
return adjustChatInputHeight();
|
||||
};
|
||||
|
||||
// Checks if only one panel (userlist/whiteboard/chatbar) is currently open
|
||||
this.isOnlyOnePanelOpen = function () {
|
||||
//return (getInSession("display_usersList") ? 1 : 0) + (getInSession("display_whiteboard") ? 1 : 0) + (getInSession("display_chatbar") ? 1 : 0) === 1
|
||||
return getInSession('display_usersList') + getInSession('display_whiteboard') + getInSession('display_chatbar') === 1;
|
||||
};
|
||||
|
||||
// determines which browser is being used
|
||||
this.getBrowserName = function () {
|
||||
if (navigator.userAgent.match(/Chrome/i)) {
|
||||
return 'Chrome';
|
||||
} else if (navigator.userAgent.match(/Firefox/i)) {
|
||||
return 'Firefox';
|
||||
} else if (navigator.userAgent.match(/Safari/i)) {
|
||||
return 'Safari';
|
||||
} else if (navigator.userAgent.match(/Trident/i)) {
|
||||
return 'IE';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
this.scrollChatDown = function () {
|
||||
let ref;
|
||||
return $('#chatbody').scrollTop((ref = $('#chatbody')[0]) != null ? ref.scrollHeight : void 0);
|
||||
};
|
||||
|
||||
// changes the height of the chat input area if needed (based on the textarea content)
|
||||
this.adjustChatInputHeight = function () {
|
||||
let projectedHeight, ref;
|
||||
if (isLandscape()) {
|
||||
$('#newMessageInput').css('height', 'auto');
|
||||
projectedHeight = $('#newMessageInput')[0].scrollHeight + 23;
|
||||
if (projectedHeight !== $('.panel-footer').height() && projectedHeight >= getInSession('chatInputMinHeight')) {
|
||||
$('#newMessageInput').css('overflow', 'hidden'); // prevents a scroll bar
|
||||
|
||||
// resizes the chat input area
|
||||
$('.panel-footer').css('top', `${-(projectedHeight - 70)}px`);
|
||||
$('.panel-footer').css('height', `${projectedHeight}px`);
|
||||
|
||||
$('#newMessageInput').height($('#newMessageInput')[0].scrollHeight);
|
||||
|
||||
// resizes the chat messages container
|
||||
$('#chatbody').height($('#chat').height() - projectedHeight - 45);
|
||||
$('#chatbody').scrollTop((ref = $('#chatbody')[0]) != null ? ref.scrollHeight : void 0);
|
||||
}
|
||||
|
||||
return $('#newMessageInput').css('height', '');
|
||||
} else if (isPortrait()) {
|
||||
$('.panel-footer').attr('style', '');
|
||||
$('#chatbody').attr('style', '');
|
||||
return $('#newMessageInput').attr('style', '');
|
||||
}
|
||||
};
|
||||
|
||||
this.toggleEmojisFAB = function () {
|
||||
if ($('.FABContainer').hasClass('openedFAB')) {
|
||||
$('.FABContainer > button:nth-child(2)').css('opacity', $('.FABContainer > button:nth-child(2)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(3)').css('opacity', $('.FABContainer > button:nth-child(3)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(4)').css('opacity', $('.FABContainer > button:nth-child(4)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(5)').css('opacity', $('.FABContainer > button:nth-child(5)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(6)').css('opacity', $('.FABContainer > button:nth-child(6)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(7)').css('opacity', $('.FABContainer > button:nth-child(7)').css('opacity'));
|
||||
$('.FABContainer').removeClass('openedFAB');
|
||||
return $('.FABContainer').addClass('closedFAB');
|
||||
} else {
|
||||
$('.FABContainer > button:nth-child(2)').css('opacity', $('.FABContainer > button:nth-child(2)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(3)').css('opacity', $('.FABContainer > button:nth-child(3)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(4)').css('opacity', $('.FABContainer > button:nth-child(4)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(5)').css('opacity', $('.FABContainer > button:nth-child(5)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(6)').css('opacity', $('.FABContainer > button:nth-child(6)').css('opacity'));
|
||||
$('.FABContainer > button:nth-child(7)').css('opacity', $('.FABContainer > button:nth-child(7)').css('opacity'));
|
||||
$('.FABContainer').removeClass('closedFAB');
|
||||
return $('.FABContainer').addClass('openedFAB');
|
||||
}
|
||||
};
|
||||
|
||||
this.toggleUserlistMenu = function () {
|
||||
// menu
|
||||
if ($('.userlistMenu').hasClass('menuOut')) {
|
||||
$('.userlistMenu').removeClass('menuOut');
|
||||
} else {
|
||||
$('.userlistMenu').addClass('menuOut');
|
||||
}
|
||||
|
||||
// icon
|
||||
if ($('.toggleUserlistButton').hasClass('menuToggledOn')) {
|
||||
return $('.toggleUserlistButton').removeClass('menuToggledOn');
|
||||
} else {
|
||||
return $('.toggleUserlistButton').addClass('menuToggledOn');
|
||||
}
|
||||
};
|
||||
|
||||
this.toggleSettingsMenu = function () {
|
||||
// menu
|
||||
if ($('.settingsMenu').hasClass('menuOut')) {
|
||||
$('.settingsMenu').removeClass('menuOut');
|
||||
} else {
|
||||
$('.settingsMenu').addClass('menuOut');
|
||||
}
|
||||
|
||||
// icon
|
||||
if ($('.toggleMenuButton').hasClass('menuToggledOn')) {
|
||||
return $('.toggleMenuButton').removeClass('menuToggledOn');
|
||||
} else {
|
||||
return $('.toggleMenuButton').addClass('menuToggledOn');
|
||||
}
|
||||
};
|
@ -1,580 +0,0 @@
|
||||
/*
|
||||
This file contains the BigBlueButton client APIs that will allow 3rd-party applications
|
||||
to embed the HTML5 client and interact with it through Javascript.
|
||||
|
||||
HOW TO USE:
|
||||
Some APIs allow synchronous and asynchronous calls. When using asynchronous, the 3rd-party
|
||||
JS should register as listener for events listed at the bottom of this file. For synchronous,
|
||||
3rd-party JS should pass in a callback function when calling the API.
|
||||
|
||||
For an example on how to use these APIs, see:
|
||||
|
||||
https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/resources/prod/lib/3rd-party.js
|
||||
https://github.com/bigbluebutton/bigbluebutton/blob/master/bigbluebutton-client/resources/prod/3rd-party.html
|
||||
*/
|
||||
import { Users, Polls, Meetings, Presentations, Slides } from '/collections/collections';
|
||||
|
||||
this.BBB = (function () {
|
||||
let BBB, listeners, returnOrCallback;
|
||||
BBB = {};
|
||||
returnOrCallback = function (res, callback) {
|
||||
if ((callback != null) && typeof callback === 'function') {
|
||||
return callback(res);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
BBB.isPollGoing = function (userId) {
|
||||
if (userId !== void 0 && Polls.findOne({
|
||||
'poll_info.users': userId,
|
||||
})) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
BBB.getCurrentPoll = function (userId) {
|
||||
if (userId !== void 0 && Polls.findOne({
|
||||
'poll_info.users': userId,
|
||||
})) {
|
||||
return Polls.findOne({
|
||||
'poll_info.users': userId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BBB.sendPollResponseMessage = function (key, pollAnswerId) {
|
||||
return Meteor.call('publishVoteMessage', BBB.getMeetingId(), pollAnswerId, getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
BBB.getMeetingId = function () {
|
||||
let ref;
|
||||
return (ref = Meetings.findOne()) != null ? ref.meetingId : void 0;
|
||||
};
|
||||
|
||||
BBB.getInternalMeetingId = function (callback) {};
|
||||
|
||||
/*
|
||||
Queryies the user object via it's id
|
||||
*/
|
||||
BBB.getUser = function (userId) {
|
||||
return Users.findOne({
|
||||
userId: userId,
|
||||
});
|
||||
};
|
||||
|
||||
BBB.getCurrentUser = function () {
|
||||
return BBB.getUser(getInSession('userId'));
|
||||
};
|
||||
|
||||
/*
|
||||
Query if the current user is sharing webcam.
|
||||
|
||||
Param:
|
||||
callback - function to return the result
|
||||
|
||||
If you want to instead receive an event with the result, register a listener
|
||||
for AM_I_SHARING_CAM_RESP (see below).
|
||||
*/
|
||||
BBB.amISharingWebcam = function (callback) {
|
||||
// BBB.isUserSharingWebcam BBB.getCurrentUser()?.userId
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Query if another user is sharing her camera.
|
||||
|
||||
Param:
|
||||
userID : the id of the user that may be sharing the camera
|
||||
callback: function if you want to be informed synchronously. Don't pass a function
|
||||
if you want to be informed through an event. You have to register for
|
||||
IS_USER_PUBLISHING_CAM_RESP (see below).
|
||||
*/
|
||||
BBB.isUserSharingWebcam = function (userId, callback) {
|
||||
// BBB.getUser(userId)?.user?.webcam_stream?.length isnt 0
|
||||
return false;
|
||||
};
|
||||
|
||||
// returns whether the user has joined any type of audio
|
||||
BBB.amIInAudio = function (callback) {
|
||||
let ref, ref1, ref2, user;
|
||||
user = BBB.getCurrentUser();
|
||||
return (user != null ? (ref = user.user) != null ? ref.listenOnly : void 0 : void 0) || (user != null ? (ref1 = user.user) != null ? (ref2 = ref1.voiceUser) != null ? ref2.joined : void 0 : void 0 : void 0);
|
||||
};
|
||||
|
||||
// returns true if the user has joined the listen only audio stream
|
||||
BBB.amIListenOnlyAudio = function (callback) {
|
||||
let ref, ref1;
|
||||
return (ref = BBB.getCurrentUser()) != null ? (ref1 = ref.user) != null ? ref1.listenOnly : void 0 : void 0;
|
||||
};
|
||||
|
||||
// returns whether the user has joined the voice conference and is sharing audio through a microphone
|
||||
BBB.amISharingAudio = function (callback) {
|
||||
let ref;
|
||||
return BBB.isUserSharingAudio((ref = BBB.getCurrentUser()) != null ? ref.userId : void 0);
|
||||
};
|
||||
|
||||
// returns whether the user is currently talking
|
||||
BBB.amITalking = function (callback) {
|
||||
let ref;
|
||||
return BBB.isUserTalking((ref = BBB.getCurrentUser()) != null ? ref.userId : void 0);
|
||||
};
|
||||
|
||||
BBB.isUserInAudio = function (userId, callback) {
|
||||
let ref, ref1, ref2, user;
|
||||
user = BBB.getUser(userId);
|
||||
return (user != null ? (ref = user.user) != null ? ref.listenOnly : void 0 : void 0) || (user != null ? (ref1 = user.user) != null ? (ref2 = ref1.voiceUser) != null ? ref2.joined : void 0 : void 0 : void 0);
|
||||
};
|
||||
|
||||
BBB.isUserListenOnlyAudio = function (userId, callback) {
|
||||
let ref, ref1;
|
||||
return (ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? ref1.listenOnly : void 0 : void 0;
|
||||
};
|
||||
|
||||
BBB.isUserSharingAudio = function (userId, callback) {
|
||||
let ref, ref1, ref2;
|
||||
return (ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? (ref2 = ref1.voiceUser) != null ? ref2.joined : void 0 : void 0 : void 0;
|
||||
};
|
||||
|
||||
BBB.isUserTalking = function (userId, callback) {
|
||||
let ref, ref1, ref2;
|
||||
return (ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? (ref2 = ref1.voiceUser) != null ? ref2.talking : void 0 : void 0 : void 0;
|
||||
};
|
||||
|
||||
BBB.isUserPresenter = function (userId, callback) {
|
||||
let ref, ref1;
|
||||
return (ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? ref1.presenter : void 0 : void 0;
|
||||
};
|
||||
|
||||
// returns true if the current user is marked as locked
|
||||
BBB.amILocked = function () {
|
||||
let ref;
|
||||
return (ref = BBB.getCurrentUser()) != null ? ref.user.locked : void 0;
|
||||
};
|
||||
|
||||
// check whether the user is locked AND the current lock settings for the room
|
||||
// includes locking the microphone of viewers (listenOnly is still alowed)
|
||||
BBB.isMyMicLocked = function () {
|
||||
let lockedMicForRoom, ref;
|
||||
lockedMicForRoom = (ref = Meetings.findOne()) != null ? ref.roomLockSettings.disableMic : void 0;
|
||||
|
||||
// note that voiceUser.locked is not used in BigBlueButton at this stage (April 2015)
|
||||
|
||||
return lockedMicForRoom && BBB.amILocked();
|
||||
};
|
||||
|
||||
BBB.getCurrentSlide = function () {
|
||||
let currentPresentation, currentSlide, presentationId, ref;
|
||||
currentPresentation = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
});
|
||||
presentationId = currentPresentation != null ? (ref = currentPresentation.presentation) != null ? ref.id : void 0 : void 0;
|
||||
currentSlide = Slides.findOne({
|
||||
presentationId: presentationId,
|
||||
'slide.current': true,
|
||||
});
|
||||
return currentSlide;
|
||||
};
|
||||
|
||||
BBB.getMeetingName = function () {
|
||||
let ref;
|
||||
return ((ref = Meetings.findOne()) != null ? ref.meetingName : void 0) || null;
|
||||
};
|
||||
|
||||
BBB.getNumberOfUsers = function () {
|
||||
return Users.find().count();
|
||||
};
|
||||
|
||||
BBB.currentPresentationName = function () {
|
||||
let ref, ref1;
|
||||
return (ref = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
})) != null ? (ref1 = ref.presentation) != null ? ref1.name : void 0 : void 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Raise user's hand.
|
||||
Param:
|
||||
*/
|
||||
BBB.lowerHand = function (meetingId, toUserId, byUserId, byAuthToken) {
|
||||
return Meteor.call('userLowerHand', meetingId, toUserId, byUserId, byAuthToken);
|
||||
};
|
||||
|
||||
BBB.raiseHand = function (meetingId, toUserId, byUserId, byAuthToken) {
|
||||
return Meteor.call('userRaiseHand', meetingId, toUserId, byUserId, byAuthToken);
|
||||
};
|
||||
|
||||
BBB.setEmojiStatus = function (meetingId, toUserId, byUserId, byAuthToken, status) {
|
||||
return Meteor.call('userSetEmoji', meetingId, toUserId, byUserId, byAuthToken, status);
|
||||
};
|
||||
|
||||
BBB.isUserEmojiStatusSet = function (userId) {
|
||||
let ref, ref1, ref2, ref3;
|
||||
return ((ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? ref1.emoji_status : void 0 : void 0) !== 'none' && ((ref2 = BBB.getUser(userId)) != null ? (ref3 = ref2.user) != null ? ref3.emoji_status : void 0 : void 0) !== void 0;
|
||||
};
|
||||
|
||||
BBB.isCurrentUserEmojiStatusSet = function () {
|
||||
let ref;
|
||||
return BBB.isUserEmojiStatusSet((ref = BBB.getCurrentUser()) != null ? ref.userId : void 0);
|
||||
};
|
||||
|
||||
BBB.isMeetingRecording = function () {
|
||||
let ref;
|
||||
return (ref = Meetings.findOne()) != null ? ref.recorded : void 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Issue a switch presenter command.
|
||||
|
||||
Param:
|
||||
newPresenterUserID - the user id of the new presenter
|
||||
|
||||
3rd-party JS must listen for SWITCHED_PRESENTER (see below) to get notified
|
||||
of switch presenter events.
|
||||
*/
|
||||
BBB.switchPresenter = function (newPresenterUserID) {};
|
||||
|
||||
/*
|
||||
Query if current user is presenter.
|
||||
|
||||
Params:
|
||||
callback - function if you want a callback as response. Otherwise, you need to listen
|
||||
for AM_I_PRESENTER_RESP (see below).
|
||||
*/
|
||||
BBB.amIPresenter = function (callback) {
|
||||
return returnOrCallback(false, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Eject a user.
|
||||
|
||||
Params:
|
||||
userID - userID of the user you want to eject.
|
||||
*/
|
||||
BBB.ejectUser = function (userID) {};
|
||||
|
||||
/*
|
||||
Query who is presenter.
|
||||
|
||||
Params:
|
||||
callback - function that gets executed for the response.
|
||||
*/
|
||||
BBB.getPresenterUserID = function (callback) {};
|
||||
|
||||
/*
|
||||
Query the current user's role.
|
||||
Params:
|
||||
callback - function if you want a callback as response. Otherwise, you need to listen
|
||||
for GET_MY_ROLE_RESP (see below).
|
||||
*/
|
||||
BBB.getMyRole = function (callback) {
|
||||
return returnOrCallback('VIEWER', callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Query the current user's id.
|
||||
|
||||
Params:
|
||||
callback - function that gets executed for the response.
|
||||
*/
|
||||
BBB.getMyUserID = function (callback) {
|
||||
return returnOrCallback(getInSession('userId'), callback);
|
||||
};
|
||||
|
||||
BBB.getMyDBID = function (callback) {
|
||||
let ref;
|
||||
return returnOrCallback((ref = Users.findOne({
|
||||
userId: getInSession('userId'),
|
||||
})) != null ? ref._id : void 0, callback);
|
||||
};
|
||||
|
||||
BBB.getMyUserName = function (callback) {
|
||||
let ref;
|
||||
return BBB.getUserName((ref = BBB.getCurrentUser()) != null ? ref.userId : void 0);
|
||||
};
|
||||
|
||||
BBB.getMyVoiceBridge = function (callback) {
|
||||
let res;
|
||||
res = Meetings.findOne({}).voiceConf;
|
||||
return returnOrCallback(res, callback);
|
||||
};
|
||||
|
||||
BBB.getUserName = function (userId, callback) {
|
||||
let ref, ref1;
|
||||
return returnOrCallback((ref = BBB.getUser(userId)) != null ? (ref1 = ref.user) != null ? ref1.name : void 0 : void 0, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Query the current user's role.
|
||||
Params:
|
||||
callback - function if you want a callback as response. Otherwise, you need to listen
|
||||
for GET_MY_ROLE_RESP (see below).
|
||||
*/
|
||||
BBB.getMyUserInfo = function (callback) {
|
||||
let result;
|
||||
result = {
|
||||
myUserID: BBB.getMyUserID(),
|
||||
myUsername: BBB.getMyUserName(),
|
||||
myInternalUserID: BBB.getMyUserID(),
|
||||
myAvatarURL: null,
|
||||
myRole: BBB.getMyRole(),
|
||||
amIPresenter: BBB.amIPresenter(),
|
||||
voiceBridge: BBB.getMyVoiceBridge(),
|
||||
dialNumber: null,
|
||||
};
|
||||
return returnOrCallback(result, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Query the meeting id.
|
||||
|
||||
Params:
|
||||
callback - function that gets executed for the response.
|
||||
*/
|
||||
|
||||
/*
|
||||
Join the voice conference.
|
||||
isListenOnly: signifies whether the user joining the conference audio requests to join the listen only stream
|
||||
*/
|
||||
BBB.joinVoiceConference = function (callback, isListenOnly) {
|
||||
if (BBB.isMyMicLocked()) {
|
||||
callIntoConference(BBB.getMyVoiceBridge(), callback, true);
|
||||
}
|
||||
|
||||
return callIntoConference(BBB.getMyVoiceBridge(), callback, isListenOnly);
|
||||
};
|
||||
|
||||
/*
|
||||
Leave the voice conference.
|
||||
*/
|
||||
BBB.leaveVoiceConference = function (callback) {
|
||||
return webrtc_hangup(callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Get a hold of the object containing the call information
|
||||
*/
|
||||
BBB.getCallStatus = function () {
|
||||
return getCallStatus();
|
||||
};
|
||||
|
||||
/*
|
||||
Share user's webcam.
|
||||
|
||||
Params:
|
||||
publishInClient : (DO NOT USE - Unimplemented)
|
||||
*/
|
||||
BBB.shareVideoCamera = function (publishInClient) {};
|
||||
|
||||
/*
|
||||
Stop share user's webcam.
|
||||
*/
|
||||
BBB.stopSharingCamera = function () {};
|
||||
|
||||
/*
|
||||
Indicates if a user is muted
|
||||
*/
|
||||
BBB.isUserMuted = function (id) {
|
||||
let ref, ref1, ref2;
|
||||
return (ref = BBB.getUser(id)) != null ? (ref1 = ref.user) != null ? (ref2 = ref1.voiceUser) != null ? ref2.muted : void 0 : void 0 : void 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Indicates if the current user is muted
|
||||
*/
|
||||
BBB.amIMuted = function () {
|
||||
return BBB.isUserMuted(BBB.getCurrentUser().userId);
|
||||
};
|
||||
|
||||
/*
|
||||
Mute the current user.
|
||||
*/
|
||||
BBB.muteMe = function () {
|
||||
return BBB.muteUser(getInSession('userId'), getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
/*
|
||||
Unmute the current user.
|
||||
*/
|
||||
BBB.unmuteMe = function () {
|
||||
return BBB.unmuteUser(getInSession('userId'), getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
BBB.muteUser = function (meetingId, userId, toMuteId, requesterId, requestToken) {
|
||||
return Meteor.call('muteUser', meetingId, toMuteId, requesterId, getInSession('authToken'));
|
||||
};
|
||||
|
||||
BBB.unmuteUser = function (meetingId, userId, toMuteId, requesterId, requestToken) {
|
||||
return Meteor.call('unmuteUser', meetingId, toMuteId, requesterId, getInSession('authToken'));
|
||||
};
|
||||
|
||||
BBB.toggleMyMic = function () {
|
||||
let request;
|
||||
request = BBB.amIMuted() ? 'unmuteUser' : 'muteUser';
|
||||
return Meteor.call(request, BBB.getMeetingId(), getInSession('userId'), getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
/*
|
||||
Mute all the users.
|
||||
*/
|
||||
BBB.muteAll = function () {};
|
||||
|
||||
/*
|
||||
Unmute all the users.
|
||||
*/
|
||||
BBB.unmuteAll = function () {};
|
||||
|
||||
/*
|
||||
Switch to a new layout.
|
||||
|
||||
Param:
|
||||
newLayout : name of the layout as defined in layout.xml (found in /var/www/bigbluebutton/client/conf/layout.xml)
|
||||
*/
|
||||
BBB.switchLayout = function (newLayout) {};
|
||||
|
||||
/*
|
||||
Lock the layout.
|
||||
|
||||
Locking the layout means that users will have the same layout with the moderator that issued the lock command.
|
||||
Other users won't be able to move or resize the different windows.
|
||||
*/
|
||||
BBB.lockLayout = function (lock) {};
|
||||
|
||||
/*
|
||||
Request to send a public chat
|
||||
fromUserID - the external user id for the sender
|
||||
fontColor - the color of the font to display the message
|
||||
localeLang - the 2-char locale code (e.g. en) for the sender
|
||||
message - the message to send
|
||||
*/
|
||||
BBB.sendPublicChatMessage = function (fontColor, localeLang, message) {
|
||||
let messageForServer;
|
||||
messageForServer = {
|
||||
message: message,
|
||||
chat_type: 'PUBLIC_CHAT',
|
||||
from_userid: getInSession('userId'),
|
||||
from_username: BBB.getMyUserName(),
|
||||
from_tz_offset: '240',
|
||||
to_username: 'public_chat_username',
|
||||
to_userid: 'public_chat_userid',
|
||||
from_lang: localeLang,
|
||||
from_time: getTime(),
|
||||
from_color: fontColor,
|
||||
};
|
||||
return Meteor.call('sendChatMessagetoServer', BBB.getMeetingId(), messageForServer, getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
/*
|
||||
Request to send a private chat
|
||||
fromUserID - the external user id for the sender
|
||||
fontColor - the color of the font to display the message
|
||||
localeLang - the 2-char locale code (e.g. en) for the sender
|
||||
message - the message to send
|
||||
toUserID - the external user id of the receiver
|
||||
*/
|
||||
BBB.sendPrivateChatMessage = function (fontColor, localeLang, message, toUserID, toUserName) {
|
||||
let messageForServer;
|
||||
messageForServer = {
|
||||
message: message,
|
||||
chat_type: 'PRIVATE_CHAT',
|
||||
from_userid: getInSession('userId'),
|
||||
from_username: BBB.getMyUserName(),
|
||||
from_tz_offset: '240',
|
||||
to_username: toUserName,
|
||||
to_userid: toUserID,
|
||||
from_lang: localeLang,
|
||||
from_time: getTime(),
|
||||
from_color: fontColor,
|
||||
};
|
||||
return Meteor.call('sendChatMessagetoServer', BBB.getMeetingId(), messageForServer, getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
/*
|
||||
Request to display a presentation.
|
||||
presentationID - the presentation to display
|
||||
*/
|
||||
BBB.displayPresentation = function (presentationID) {};
|
||||
|
||||
/*
|
||||
Query the list of uploaded presentations.
|
||||
*/
|
||||
BBB.queryListOfPresentations = function () {};
|
||||
|
||||
/*
|
||||
Request to delete a presentation.
|
||||
presentationID - the presentation to delete
|
||||
*/
|
||||
BBB.deletePresentation = function (presentationID) {};
|
||||
|
||||
// Request to switch the presentation to the previous slide
|
||||
BBB.goToPreviousPage = function () {
|
||||
return Meteor.call('publishSwitchToPreviousSlideMessage', getInSession('meetingId'), getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
// Request to switch the presentation to the next slide
|
||||
BBB.goToNextPage = function () {
|
||||
return Meteor.call('publishSwitchToNextSlideMessage', getInSession('meetingId'), getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
BBB.webRTCConferenceCallStarted = function () {};
|
||||
|
||||
BBB.webRTCConferenceCallConnecting = function () {};
|
||||
|
||||
BBB.webRTCConferenceCallEnded = function () {};
|
||||
|
||||
BBB.webRTCConferenceCallFailed = function (errorcode) {};
|
||||
|
||||
BBB.webRTCConferenceCallWaitingForICE = function () {};
|
||||
|
||||
BBB.webRTCCallProgressCallback = function (progress) {};
|
||||
|
||||
BBB.webRTCEchoTestStarted = function () {};
|
||||
|
||||
BBB.webRTCEchoTestConnecting = function () {};
|
||||
|
||||
BBB.webRTCEchoTestFailed = function (reason) {};
|
||||
|
||||
BBB.webRTCEchoTestWaitingForICE = function () {};
|
||||
|
||||
BBB.webRTCEchoTestEnded = function () {};
|
||||
|
||||
BBB.webRTCMediaRequest = function () {};
|
||||
|
||||
BBB.webRTCMediaSuccess = function () {};
|
||||
|
||||
BBB.webRTCMediaFail = function () {};
|
||||
|
||||
BBB.webRTCWebcamRequest = function () {};
|
||||
|
||||
BBB.webRTCWebcamRequestSuccess = function () {};
|
||||
|
||||
BBB.webRTCWebcamRequestFail = function (reason) {};
|
||||
|
||||
// Third-party JS apps should use this to query if the BBB SWF file is ready to handle calls.
|
||||
|
||||
// ***********************************************************************************
|
||||
// * Broadcasting of events to 3rd-party apps.
|
||||
// ************************************************************************************
|
||||
|
||||
/*
|
||||
Stores the 3rd-party app event listeners **
|
||||
*/
|
||||
listeners = {};
|
||||
|
||||
/*
|
||||
3rd-party apps should use this method to register to listen for events.
|
||||
*/
|
||||
BBB.listen = function (eventName, handler) {};
|
||||
|
||||
/*
|
||||
3rd-party app should use this method to unregister listener for a given event.
|
||||
*/
|
||||
BBB.unlisten = function (eventName, handler) {};
|
||||
|
||||
BBB.init = function (callback) {};
|
||||
|
||||
return BBB;
|
||||
})();
|
@ -1,482 +0,0 @@
|
||||
// "Paper" which is the Raphael term for the entire SVG object on the webpage.
|
||||
// This class deals with this SVG component only.
|
||||
import { Presentations, Slides } from '/collections/collections';
|
||||
|
||||
WhiteboardPaperModel = (function () {
|
||||
class WhiteboardPaperModel {
|
||||
|
||||
// Container must be a DOM element
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
|
||||
// a WhiteboardCursorModel
|
||||
this.cursor = null;
|
||||
|
||||
// all slides in the presentation indexed by url
|
||||
this.slides = {};
|
||||
this.panX = null;
|
||||
this.panY = null;
|
||||
this.current = {};
|
||||
|
||||
// the slide being shown
|
||||
this.current.slide = null;
|
||||
|
||||
// a raphaeljs set with all the shapes in the current slide
|
||||
this.current.shapes = null;
|
||||
|
||||
// a list of shapes as passed to this client when it receives `all_slides`
|
||||
// (se we are able to redraw the shapes whenever needed)
|
||||
this.current.shapeDefinitions = [];
|
||||
this.zoomLevel = 1;
|
||||
this.shiftPressed = false;
|
||||
this.currentPathCount = 0;
|
||||
this._updateContainerDimensions();
|
||||
this.zoomObserver = null;
|
||||
this.adjustedWidth = 0;
|
||||
this.adjustedHeight = 0;
|
||||
this.widthRatio = 100;
|
||||
this.heightRatio = 100;
|
||||
}
|
||||
|
||||
// Initializes the paper in the page.
|
||||
// Can't do these things in initialize() because by then some elements
|
||||
// are not yet created in the page.
|
||||
create() {
|
||||
// paper is embedded within the div#slide of the page.
|
||||
// @raphaelObj ?= ScaleRaphael(@container, "900", "500")
|
||||
|
||||
let h, w;
|
||||
h = $(`#${this.container}`).height();
|
||||
w = $(`#${this.container}`).width();
|
||||
if (this.raphaelObj == null) {
|
||||
this.raphaelObj = ScaleRaphael(this.container, w, h);
|
||||
}
|
||||
|
||||
if (this.raphaelObj == null) {
|
||||
this.raphaelObj = ScaleRaphael(this.container, $container.innerHeight(), $container.innerWidth());
|
||||
}
|
||||
|
||||
this.raphaelObj.canvas.setAttribute('preserveAspectRatio', 'xMinYMin slice');
|
||||
this.createCursor();
|
||||
if (this.slides) {
|
||||
this.rebuild();
|
||||
} else {
|
||||
this.slides = {}; // if previously loaded
|
||||
}
|
||||
|
||||
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||
this.raphaelObj.renderfix();
|
||||
}
|
||||
|
||||
return this.raphaelObj;
|
||||
}
|
||||
|
||||
// Re-add the images to the paper that are found
|
||||
// in the slides array (an object of urls and dimensions).
|
||||
rebuild() {
|
||||
let results, url;
|
||||
this.current.slide = null;
|
||||
results = [];
|
||||
for (url in this.slides) {
|
||||
if (this.slides.hasOwnProperty(url)) {
|
||||
results.push(
|
||||
this.addImageToPaper(url, this.slides[url].getWidth(), this.slides[url].getHeight())
|
||||
);
|
||||
} else {
|
||||
results.push(void 0);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
scale(width, height) {
|
||||
let ref;
|
||||
return (ref = this.raphaelObj) != null ? ref.changeSize(width, height) : void 0;
|
||||
}
|
||||
|
||||
// Add an image to the paper.
|
||||
// @param {string} url the URL of the image to add to the paper
|
||||
// @param {number} width the width of the image (in pixels)
|
||||
// @param {number} height the height of the image (in pixels)
|
||||
// @return {Raphael.image} the image object added to the whiteboard
|
||||
addImageToPaper(url, width, height) {
|
||||
let cx, cy, img, max, sh, sw;
|
||||
this._updateContainerDimensions();
|
||||
|
||||
// solve for the ratio of what length is going to fit more than the other
|
||||
max = Math.max(width / this.containerWidth, height / this.containerHeight);
|
||||
|
||||
// fit it all in appropriately
|
||||
url = this._slideUrl(url);
|
||||
sw = width / max;
|
||||
sh = height / max;
|
||||
|
||||
//cx = (@containerWidth / 2) - (width / 2)
|
||||
//cy = (@containerHeight / 2) - (height / 2)
|
||||
img = this.raphaelObj.image(url, cx = 0, cy = 0, width, height);
|
||||
|
||||
// sw slide width as percentage of original width of paper
|
||||
// sh slide height as a percentage of original height of paper
|
||||
// x-offset from top left corner as percentage of original width of paper
|
||||
// y-offset from top left corner as percentage of original height of paper
|
||||
this.slides[url] = new WhiteboardSlideModel(img.id, url, img, width, height, sw, sh, cx, cy);
|
||||
if (this.current.slide == null) {
|
||||
img.toBack();
|
||||
this.current.slide = this.slides[url];
|
||||
} else if (this.current.slide.url === url) {
|
||||
img.toBack();
|
||||
} else {
|
||||
img.hide();
|
||||
}
|
||||
|
||||
// TODO: other places might also required an update in these dimensions
|
||||
this._updateContainerDimensions();
|
||||
|
||||
this._updateZoomRatios();
|
||||
if (this.raphaelObj.w === 100) { // on first load: Raphael object is initially tiny
|
||||
this.cursor.setRadius(0.65 * this.widthRatio / 100);
|
||||
} else {
|
||||
this.cursor.setRadius(6 * this.widthRatio / 100);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
// Removes all the images from the Raphael paper.
|
||||
removeAllImagesFromPaper() {
|
||||
let ref, ref1, url;
|
||||
for (url in this.slides) {
|
||||
if (this.slides.hasOwnProperty(url)) {
|
||||
if ((ref = this.raphaelObj.getById((ref1 = this.slides[url]) != null ? ref1.getId() : void 0)) != null) {
|
||||
ref.remove();
|
||||
}
|
||||
|
||||
//@trigger('paper:image:removed', @slides[url].getId()) # Removes the previous image preventing images from being redrawn over each other repeatedly
|
||||
}
|
||||
}
|
||||
|
||||
this.slides = {};
|
||||
return this.current.slide = null;
|
||||
}
|
||||
|
||||
// Switches the tool and thus the functions that get
|
||||
// called when certain events are fired from Raphael.
|
||||
// @param {string} tool the tool to turn on
|
||||
// @return {undefined}
|
||||
setCurrentTool(tool) {
|
||||
this.currentTool = tool;
|
||||
console.log('setting current tool to', tool);
|
||||
switch (tool) {
|
||||
case 'line':
|
||||
this.cursor.undrag();
|
||||
this.current.line = this._createTool(tool);
|
||||
return this.cursor.drag(this.current.line.dragOnMove, this.current.line.dragOnStart, this.current.line.dragOnEnd);
|
||||
case 'rectangle':
|
||||
this.cursor.undrag();
|
||||
this.current.rectangle = this._createTool(tool);
|
||||
return this.cursor.drag(this.current.rectangle.dragOnMove, this.current.rectangle.dragOnStart, this.current.rectangle.dragOnEnd);
|
||||
default:
|
||||
return console.log('ERROR: Cannot set invalid tool:', tool);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all shapes from this paper.
|
||||
clearShapes() {
|
||||
if (this.current.shapes != null) {
|
||||
this.current.shapes.forEach(element => {
|
||||
return element.remove();
|
||||
});
|
||||
this.current.shapeDefinitions = [];
|
||||
this.current.shapes.clear();
|
||||
}
|
||||
|
||||
this.clearCursor();
|
||||
return this.createCursor();
|
||||
}
|
||||
|
||||
clearCursor() {
|
||||
let ref;
|
||||
return (ref = this.cursor) != null ? ref.remove() : void 0;
|
||||
}
|
||||
|
||||
createCursor() {
|
||||
if (this.raphaelObj.w === 100) { // on first load: Raphael object is initially tiny
|
||||
this.cursor = new WhiteboardCursorModel(this.raphaelObj, 0.65);
|
||||
this.cursor.setRadius(0.65 * this.widthRatio / 100);
|
||||
} else {
|
||||
this.cursor = new WhiteboardCursorModel(this.raphaelObj);
|
||||
this.cursor.setRadius(6 * this.widthRatio / 100);
|
||||
}
|
||||
|
||||
return this.cursor.draw();
|
||||
}
|
||||
|
||||
// Updated a shape `shape` with the data in `data`.
|
||||
// TODO: check if the objects exist before calling update, if they don't they should be created
|
||||
updateShape(shape, data) {
|
||||
return this.current[shape].update(data);
|
||||
}
|
||||
|
||||
// Make a shape `shape` with the data in `data`.
|
||||
makeShape(shape, data) {
|
||||
let base, base1, i, len, obj, tool, toolModel;
|
||||
data.thickness *= this.adjustedWidth / 1000;
|
||||
tool = null;
|
||||
this.current[shape] = this._createTool(shape);
|
||||
toolModel = this.current[shape];
|
||||
tool = this.current[shape].make(data);
|
||||
if ((tool != null) && shape !== 'poll_result') {
|
||||
if ((base = this.current).shapes == null) {
|
||||
base.shapes = this.raphaelObj.set();
|
||||
}
|
||||
|
||||
this.current.shapes.push(tool);
|
||||
this.current.shapeDefinitions.push(toolModel.getDefinition());
|
||||
}
|
||||
|
||||
//We have a separate case for Poll as it returns an array instead of just one object
|
||||
if ((tool != null) && shape === 'poll_result') {
|
||||
if ((base1 = this.current).shapes == null) {
|
||||
base1.shapes = this.raphaelObj.set();
|
||||
}
|
||||
|
||||
for (i = 0, len = tool.length; i < len; i++) {
|
||||
obj = tool[i];
|
||||
this.current.shapes.push(obj);
|
||||
}
|
||||
|
||||
return this.current.shapeDefinitions.push(toolModel.getDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
// Update the cursor position on screen
|
||||
// @param {number} x the x value of the cursor as a percentage of the width
|
||||
// @param {number} y the y value of the cursor as a percentage of the height
|
||||
moveCursor(x, y) {
|
||||
let cx, cy, ref, ref1, slideHeight, slideWidth;
|
||||
ref = this._currentSlideOffsets(), cx = ref[0], cy = ref[1];
|
||||
ref1 = this._currentSlideOriginalDimensions(), slideWidth = ref1[0], slideHeight = ref1[1];
|
||||
this.cursor.setPosition(x * slideWidth + cx, y * slideHeight + cy);
|
||||
|
||||
//if the slide is zoomed in then move the cursor based on where the viewBox is looking
|
||||
if ((this.viewBoxXpos != null) && (this.viewBoxYPos != null) && (this.viewBoxWidth != null) && (this.viewBoxHeight != null)) {
|
||||
return this.cursor.setPosition(this.viewBoxXpos + x * this.viewBoxWidth, this.viewBoxYPos + y * this.viewBoxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
zoomAndPan(widthRatio, heightRatio, xOffset, yOffset) {
|
||||
// console.log "zoomAndPan #{widthRatio} #{heightRatio} #{xOffset} #{yOffset}"
|
||||
let newHeight, newWidth, newX, newY;
|
||||
newX = -xOffset * 2 * this.adjustedWidth / 100;
|
||||
newY = -yOffset * 2 * this.adjustedHeight / 100;
|
||||
newWidth = this.adjustedWidth * widthRatio / 100;
|
||||
newHeight = this.adjustedHeight * heightRatio / 100;
|
||||
return this.raphaelObj.setViewBox(newX, newY, newWidth, newHeight);
|
||||
}
|
||||
|
||||
setAdjustedDimensions(width, height) {
|
||||
this.adjustedWidth = width;
|
||||
return this.adjustedHeight = height;
|
||||
}
|
||||
|
||||
// Update the dimensions of the container.
|
||||
_updateContainerDimensions() {
|
||||
let $container, containerDimensions, ref, ref1;
|
||||
$container = $('#whiteboard-paper');
|
||||
containerDimensions = scaleSlide(getInSession('slideOriginalWidth'), getInSession('slideOriginalHeight'));
|
||||
if ($container.innerWidth() === 0) {
|
||||
this.containerWidth = containerDimensions.boardWidth;
|
||||
} else {
|
||||
this.containerWidth = $container.innerWidth();
|
||||
}
|
||||
|
||||
if ($container.innerHeight() === 0) {
|
||||
this.containerHeight = containerDimensions.boardHeight;
|
||||
} else {
|
||||
this.containerHeight = $container.innerHeight();
|
||||
}
|
||||
|
||||
this.containerOffsetLeft = (ref = $container.offset()) != null ? ref.left : void 0;
|
||||
return this.containerOffsetTop = (ref1 = $container.offset()) != null ? ref1.top : void 0;
|
||||
}
|
||||
|
||||
_updateZoomRatios() {
|
||||
let currentSlideDoc;
|
||||
currentSlideDoc = BBB.getCurrentSlide();
|
||||
this.widthRatio = (currentSlideDoc != null ? currentSlideDoc.slide.width_ratio : void 0) || 100;
|
||||
return this.heightRatio = currentSlideDoc != null ? currentSlideDoc.slide.height_ratio : void 0;
|
||||
}
|
||||
|
||||
// Retrieves an image element from the paper.
|
||||
// The url must be in the slides array.
|
||||
// @param {string} url the url of the image (must be in slides array)
|
||||
// @return {Raphael.image} return the image or null if not found
|
||||
_getImageFromPaper(url) {
|
||||
let id;
|
||||
if (this.slides[url]) {
|
||||
id = this.slides[url].getId();
|
||||
if (id != null) {
|
||||
return this.raphaelObj.getById(id);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_currentSlideDimensions() {
|
||||
if (this.current.slide != null) {
|
||||
return this.current.slide.getDimensions();
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
_currentSlideOriginalDimensions() {
|
||||
if (this.current.slide != null) {
|
||||
return this.current.slide.getOriginalDimensions();
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
_currentSlideOffsets() {
|
||||
if (this.current.slide != null) {
|
||||
return this.current.slide.getOffsets();
|
||||
} else {
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper method to create a tool for the whiteboard
|
||||
_createTool(type) {
|
||||
let height, model, ref, ref1, ref2, slideHeight, slideWidth, tool, width, xOffset, yOffset;
|
||||
switch (type) {
|
||||
case 'pencil':
|
||||
model = WhiteboardLineModel;
|
||||
break;
|
||||
case 'path':
|
||||
case 'line':
|
||||
model = WhiteboardLineModel;
|
||||
break;
|
||||
case 'rectangle':
|
||||
model = WhiteboardRectModel;
|
||||
break;
|
||||
case 'ellipse':
|
||||
model = WhiteboardEllipseModel;
|
||||
break;
|
||||
case 'triangle':
|
||||
model = WhiteboardTriangleModel;
|
||||
break;
|
||||
case 'text':
|
||||
model = WhiteboardTextModel;
|
||||
break;
|
||||
case 'poll_result':
|
||||
model = WhiteboardPollModel;
|
||||
}
|
||||
if (model != null) {
|
||||
ref = this._currentSlideOriginalDimensions(), slideWidth = ref[0], slideHeight = ref[1];
|
||||
ref1 = this._currentSlideOffsets(), xOffset = ref1[0], yOffset = ref1[1];
|
||||
ref2 = this._currentSlideDimensions(), width = ref2[0], height = ref2[1];
|
||||
tool = new model(this.raphaelObj);
|
||||
|
||||
// TODO: why are the parameters inverted and it works?
|
||||
tool.setPaperSize(slideHeight, slideWidth);
|
||||
tool.setOffsets(xOffset, yOffset);
|
||||
tool.setPaperDimensions(width, height);
|
||||
return tool;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the base url (the protocol+server part) to `url` if needed.
|
||||
_slideUrl(url) {
|
||||
if (url != null ? url.match(/http[s]?:/) : void 0) {
|
||||
return url;
|
||||
} else {
|
||||
return console.log(`The url '${url}' did not match the expected format of: http/s`);
|
||||
|
||||
//globals.presentationServer + url
|
||||
}
|
||||
}
|
||||
|
||||
//Changes the currently displayed page/slide (if any) with this one
|
||||
//@param {data} message object containing the "presentation" object
|
||||
_displayPage(data, originalWidth, originalHeight) {
|
||||
let _this, boardHeight, boardWidth, currentPresentation, currentSlide, currentSlideCursor, presentationId, ref;
|
||||
this.removeAllImagesFromPaper();
|
||||
this._updateContainerDimensions();
|
||||
boardWidth = this.containerWidth;
|
||||
boardHeight = this.containerHeight;
|
||||
currentSlide = BBB.getCurrentSlide();
|
||||
currentPresentation = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
});
|
||||
presentationId = currentPresentation != null ? (ref = currentPresentation.presentation) != null ? ref.id : void 0 : void 0;
|
||||
currentSlideCursor = Slides.find({
|
||||
presentationId: presentationId,
|
||||
'slide.current': true,
|
||||
});
|
||||
if (this.zoomObserver !== null) {
|
||||
this.zoomObserver.stop();
|
||||
}
|
||||
|
||||
_this = this;
|
||||
this.zoomObserver = currentSlideCursor.observe({ // watching the current slide changes
|
||||
changed(newDoc, oldDoc) {
|
||||
let newRatio, oldRatio, ref1, ref2;
|
||||
if (originalWidth <= originalHeight) {
|
||||
this.adjustedWidth = boardHeight * originalWidth / originalHeight;
|
||||
this.adjustedHeight = boardHeight;
|
||||
} else {
|
||||
this.adjustedHeight = boardWidth * originalHeight / originalWidth;
|
||||
this.adjustedWidth = boardWidth;
|
||||
}
|
||||
|
||||
_this.zoomAndPan(
|
||||
newDoc.slide.width_ratio,
|
||||
newDoc.slide.height_ratio,
|
||||
newDoc.slide.x_offset,
|
||||
newDoc.slide.y_offset
|
||||
);
|
||||
oldRatio = (oldDoc.slide.width_ratio + oldDoc.slide.height_ratio) / 2;
|
||||
newRatio = (newDoc.slide.width_ratio + newDoc.slide.height_ratio) / 2;
|
||||
if (_this != null) {
|
||||
if ((ref1 = _this.current) != null) {
|
||||
if ((ref2 = ref1.shapes) != null) {
|
||||
ref2.forEach(shape => {
|
||||
return shape.attr('stroke-width', shape.attr('stroke-width') * oldRatio / newRatio);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_this.raphaelObj === 100) { // on first load: Raphael object is initially tiny
|
||||
return _this.cursor.setRadius(0.65 * newDoc.slide.width_ratio / 100);
|
||||
} else {
|
||||
return _this.cursor.setRadius(6 * newDoc.slide.width_ratio / 100);
|
||||
}
|
||||
},
|
||||
});
|
||||
if (originalWidth <= originalHeight) {
|
||||
// square => boardHeight is the shortest side
|
||||
this.adjustedWidth = boardHeight * originalWidth / originalHeight;
|
||||
$('#whiteboard-paper').width(this.adjustedWidth);
|
||||
this.addImageToPaper(data, this.adjustedWidth, boardHeight);
|
||||
this.adjustedHeight = boardHeight;
|
||||
} else {
|
||||
this.adjustedHeight = boardWidth * originalHeight / originalWidth;
|
||||
$('#whiteboard-paper').height(this.adjustedHeight);
|
||||
this.addImageToPaper(data, boardWidth, this.adjustedHeight);
|
||||
this.adjustedWidth = boardWidth;
|
||||
}
|
||||
|
||||
if (currentSlide != null) {
|
||||
return this.zoomAndPan(currentSlide.slide.width_ratio, currentSlide.slide.height_ratio, currentSlide.slide.x_offset, currentSlide.slide.y_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WhiteboardPaperModel;
|
||||
})();
|
@ -1,156 +0,0 @@
|
||||
import React from 'react';
|
||||
import { PrivateChatToolBar } from './PrivateChatToolBar.jsx';
|
||||
import { ChatInputControls } from './ChatInputControls.jsx';
|
||||
import { ChatMessage } from './ChatMessage.jsx';
|
||||
import { Users, Chat } from '/collections/collections';
|
||||
|
||||
export let ChatComponent = React.createClass({
|
||||
mixins: [ReactMeteorData],
|
||||
getMeteorData() {
|
||||
let chatMessages, privateChatName, chattingWith, user, messageFontSize, temp, user_exists;
|
||||
|
||||
messageFontSize = { fontSize: getInSession('messageFontSize') + 'px' };
|
||||
chattingWith = getInSession('inChatWith');
|
||||
if (chattingWith === 'PUBLIC_CHAT') { // find all public and system messages
|
||||
chatMessages = Chat.find({ 'message.chat_type': { $in: ['SYSTEM_MESSAGE', 'PUBLIC_CHAT'] } },
|
||||
{ sort: { 'message.from_time': 1 } }).fetch();
|
||||
} else {
|
||||
chatMessages = Chat.find({ 'message.chat_type': 'PRIVATE_CHAT', $or: [{ 'message.to_userid': chattingWith },
|
||||
{ 'message.from_userid': chattingWith }, ], }).fetch();
|
||||
}
|
||||
|
||||
user = Users.findOne({ userId: chattingWith });
|
||||
if (user != null) {
|
||||
privateChatName = user.user.name;
|
||||
}
|
||||
|
||||
if (getInSession('inChatWith') === 'PUBLIC_CHAT' || Users.findOne({
|
||||
userId: getInSession('inChatWith'),
|
||||
}) != null) {
|
||||
user_exists = true;
|
||||
} else {
|
||||
user_exists = false;
|
||||
}
|
||||
|
||||
return {
|
||||
chatMessages: chatMessages,
|
||||
privateChatName: privateChatName,
|
||||
messageFontSize: messageFontSize,
|
||||
user_exists: user_exists,
|
||||
};
|
||||
},
|
||||
|
||||
detectUnreadChat: function () {
|
||||
//if the current tab is not the same as the tab we just published in
|
||||
return Chat.find({}).observe({
|
||||
added: (_this => {
|
||||
return function (chatMessage) {
|
||||
let findDestinationTab;
|
||||
findDestinationTab = function () {
|
||||
let ref, ref1;
|
||||
if (((ref = chatMessage.message) != null ? ref.chat_type : void 0) === 'PUBLIC_CHAT') {
|
||||
return 'PUBLIC_CHAT';
|
||||
} else {
|
||||
return (ref1 = chatMessage.message) != null ? ref1.from_userid : void 0;
|
||||
}
|
||||
};
|
||||
|
||||
return Tracker.autorun(comp => {
|
||||
let destinationTab, tabsTime;
|
||||
tabsTime = getInSession('userListRenderedTime');
|
||||
if ((tabsTime != null) && chatMessage.message.from_userid !== 'SYSTEM_MESSAGE' && chatMessage.message.from_time - tabsTime > 0) {
|
||||
populateNotifications(chatMessage); // check if we need to show a new notification
|
||||
destinationTab = findDestinationTab();
|
||||
if (destinationTab !== getInSession('inChatWith')) {
|
||||
setInSession('chats', getInSession('chats').map(tab => {
|
||||
if (tab.userId === destinationTab) {
|
||||
tab.gotMail = true;
|
||||
tab.number++;
|
||||
}
|
||||
|
||||
return tab;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return comp.stop();
|
||||
});
|
||||
};
|
||||
})(this),
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
this.detectUnreadChat();
|
||||
},
|
||||
|
||||
getCombinedMessagesForChat(msgs) {
|
||||
let deleted, i, j, len;
|
||||
len = msgs != null ? msgs.length : void 0; // get length of messages
|
||||
i = 0;
|
||||
while (i < len) { // Must be a do while, for loop compiles and stores the length of array which can change inside the loop!
|
||||
if (msgs[i].message.from_userid !== 'System') { // skip system messages
|
||||
j = i + 1; // Start looking at messages right after the current one
|
||||
while (j < len) {
|
||||
deleted = false;
|
||||
if (msgs[j].message.from_userid !== 'System') { // Ignore system messages
|
||||
// Check if the time discrepancy between the two messages exceeds window for grouping
|
||||
if ((parseFloat(msgs[j].message.from_time) - parseFloat(msgs[i].message.from_time)) >= 60000) { // 60 seconds/1 minute
|
||||
break; // Messages are too far between, so them seperated and stop joining here
|
||||
}
|
||||
|
||||
if (msgs[i].message.from_userid === msgs[j].message.from_userid) { // Both messages are from the same user
|
||||
// insert a '\r' carriage return character between messages to put them on a new line
|
||||
msgs[i].message.message += `${CARRIAGE_RETURN}${msgs[j].message.message}`; // Combine the messages
|
||||
msgs.splice(j, 1); // Delete the message from the collection
|
||||
deleted = true;
|
||||
} else {
|
||||
break; // Messages are from different people, move on
|
||||
}
|
||||
} else {
|
||||
break; // This is the break point in the chat, don't merge
|
||||
}
|
||||
|
||||
len = msgs.length;
|
||||
if (!deleted) {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
len = msgs.length;
|
||||
}
|
||||
|
||||
return msgs;
|
||||
},
|
||||
|
||||
inPrivateChat() {
|
||||
return (getInSession('inChatWith') !== 'PUBLIC_CHAT');
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="chat" className="component">
|
||||
<div className="chatBodyContainer">
|
||||
{this.inPrivateChat() ?
|
||||
<PrivateChatToolBar inPrivateChat={this.inPrivateChat} privateChatName={this.data.privateChatName}/>
|
||||
: null }
|
||||
<div id="chatbody">
|
||||
<ul className="chat" style={this.data.messageFontSize}>
|
||||
{this.getCombinedMessagesForChat(this.data.chatMessages).map((message) =>
|
||||
<ChatMessage key={message._id} message={message} messageFontSize={this.data.messageFontSize}/>
|
||||
)}
|
||||
{this.data.user_exists ? null : <li>The user has left</li> }
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{this.data.user_exists ?
|
||||
<div className="panel-footer">
|
||||
<ChatInputControls inPrivateChat={this.inPrivateChat()}/>
|
||||
</div>
|
||||
: null }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,159 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Users, Meetings } from '/collections/collections';
|
||||
|
||||
export let ChatInputControls = React.createClass({
|
||||
//#TODO MessageFontSize dynamic change
|
||||
componentDidMount: function () {
|
||||
$('.panel-footer').resizable({
|
||||
handles: 'n',
|
||||
minHeight: 70,
|
||||
resize(event, ui) {
|
||||
let ref;
|
||||
if ($('.panel-footer').css('top') === '0px') {
|
||||
$('.panel-footer').height(70); // prevents the element from shrinking vertically for 1-2 px
|
||||
} else {
|
||||
$('.panel-footer').css('top', `${parseInt($('.panel-footer').css('top'))}${1}px`);
|
||||
}
|
||||
|
||||
$('#chatbody').height($('#chat').height() - $('.panel-footer').height() - 45);
|
||||
return $('#chatbody').scrollTop((ref = $('#chatbody')[0]) != null ? ref.scrollHeight : void 0);
|
||||
},
|
||||
|
||||
start(event, ui) {
|
||||
$('#newMessageInput').css('overflow', '');
|
||||
return $('.panel-footer').resizable('option', 'maxHeight', Math.max($('.panel-footer').height(), $('#chat').height() / 2));
|
||||
},
|
||||
|
||||
stop(event, ui) {
|
||||
return setInSession('chatInputMinHeight', $('.panel-footer').height() + 1);
|
||||
},
|
||||
});
|
||||
|
||||
$('#newMessageInput').on('keydown paste cut', () => {
|
||||
return setTimeout(() => {
|
||||
return this.adjustChatInputHeight();
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
|
||||
sendMessage: function () {
|
||||
let chattingWith, color, message, ref, toUsername;
|
||||
message = linkify($('#newMessageInput').val()); // get the message from the input box
|
||||
if (!((message != null ? message.length : void 0) > 0 && (/\S/.test(message)))) { // check the message has content and it is not whitespace
|
||||
return; // do nothing if invalid message
|
||||
}
|
||||
|
||||
color = '0x000000'; //"0x#{getInSession("messageColor")}"
|
||||
if ((chattingWith = getInSession('inChatWith')) !== 'PUBLIC_CHAT') {
|
||||
toUsername = (ref = Users.findOne({
|
||||
userId: chattingWith,
|
||||
})) != null ? ref.user.name : void 0;
|
||||
BBB.sendPrivateChatMessage(color, 'en', message, chattingWith, toUsername);
|
||||
} else {
|
||||
BBB.sendPublicChatMessage(color, 'en', message);
|
||||
}
|
||||
|
||||
return $('#newMessageInput').val(''); // Clear message box
|
||||
},
|
||||
|
||||
adjustChatInputHeight: function () {
|
||||
let projectedHeight, ref;
|
||||
if (isLandscape()) {
|
||||
$('#newMessageInput').css('height', 'auto');
|
||||
projectedHeight = $('#newMessageInput')[0].scrollHeight + 23;
|
||||
if (projectedHeight !== $('.panel-footer').height() && projectedHeight >= getInSession('chatInputMinHeight')) {
|
||||
$('#newMessageInput').css('overflow', 'hidden'); // prevents a scroll bar
|
||||
|
||||
// resizes the chat input area
|
||||
$('.panel-footer').css('top', `${-(projectedHeight - 70)}px`);
|
||||
$('.panel-footer').css('height', `${projectedHeight}px`);
|
||||
|
||||
$('#newMessageInput').height($('#newMessageInput')[0].scrollHeight);
|
||||
|
||||
// resizes the chat messages container
|
||||
$('#chatbody').height($('#chat').height() - projectedHeight - 45);
|
||||
$('#chatbody').scrollTop((ref = $('#chatbody')[0]) != null ? ref.scrollHeight : void 0);
|
||||
}
|
||||
|
||||
return $('#newMessageInput').css('height', '');
|
||||
} else if (isPortrait()) {
|
||||
$('.panel-footer').attr('style', '');
|
||||
$('#chatbody').attr('style', '');
|
||||
return $('#newMessageInput').attr('style', '');
|
||||
}
|
||||
},
|
||||
|
||||
handleClick: function () {
|
||||
$('#sendMessageButton').blur();
|
||||
this.sendMessage();
|
||||
return this.adjustChatInputHeight();
|
||||
},
|
||||
|
||||
// user pressed a button inside the chatbox
|
||||
keyPressedHandler: function (event) {
|
||||
let key;
|
||||
key = event.charCode ? event.charCode : (event.keyCode ? event.keyCode : 0);
|
||||
if (event.shiftKey && (key === 13)) {
|
||||
event.preventDefault();
|
||||
|
||||
// append a '\r' carriage return character to the input box dropping the cursor to a new line
|
||||
document.getElementById('newMessageInput').value += CARRIAGE_RETURN; // Change newline character
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === 13) { // Check for pressing enter to submit message
|
||||
event.preventDefault();
|
||||
this.sendMessage();
|
||||
$('#newMessageInput').val('');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
publicChatDisabled: function () {
|
||||
let presenter, publicChatIsDisabled, ref, ref1, ref2, userIsLocked;
|
||||
userIsLocked = (ref = Users.findOne({
|
||||
userId: getInSession('userId'),
|
||||
})) != null ? ref.user.locked : void 0;
|
||||
publicChatIsDisabled = (ref1 = Meetings.findOne({})) != null ? ref1.roomLockSettings.disablePublicChat : void 0;
|
||||
presenter = (ref2 = Users.findOne({
|
||||
userId: getInSession('userId'),
|
||||
})) != null ? ref2.user.presenter : void 0;
|
||||
return userIsLocked && publicChatIsDisabled && !presenter;
|
||||
},
|
||||
|
||||
privateChatDisabled: function () {
|
||||
let presenter, privateChatIsDisabled, ref, ref1, ref2, userIsLocked;
|
||||
userIsLocked = (ref = Users.findOne({
|
||||
userId: getInSession('userId'),
|
||||
})) != null ? ref.user.locked : void 0;
|
||||
privateChatIsDisabled = (ref1 = Meetings.findOne({})) != null ? ref1.roomLockSettings.disablePrivateChat : void 0;
|
||||
presenter = (ref2 = Users.findOne({
|
||||
userId: getInSession('userId'),
|
||||
})) != null ? ref2.user.presenter : void 0;
|
||||
return userIsLocked && privateChatIsDisabled && !presenter;
|
||||
},
|
||||
|
||||
getInputControls: function () {
|
||||
if (this.props.inPrivateChat && this.privateChatDisabled() || !this.props.inPrivateChat && this.publicChatDisabled()) {
|
||||
return (
|
||||
<textarea id="newMessageInput" className="disabledChat" style={this.props.messageFontSize()} placeholder="Private chat is temporarily locked (disabled)" rel="tooltip"
|
||||
data-placement="top" title="Private chat is temporarily locked" disabled></textarea>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="button-group radius">
|
||||
<textarea onKeyPress={this.keyPressedHandler} id="newMessageInput" rel="tooltip" data-placement="top" title="Write a new message"></textarea>
|
||||
<button onClick={this.handleClick} type="submit" id="sendMessageButton" className="button radius" rel="tooltip" data-placement="top">Send</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="chatInput" className="chat-input-wrapper">
|
||||
{ this.getInputControls() }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
import { Users, Meetings } from '/collections/collections';
|
||||
|
||||
let shouldUserBeLocked = function (userId) {
|
||||
let lockInAction, locked, meeting, settings;
|
||||
locked = (locked = BBB.getUser(userId)) !== null ? locked.user.locked : null;
|
||||
settings = (meeting = Meetings.findOne()) !== null ? meeting.roomLockSettings : null;
|
||||
lockInAction = settings.disablePrivateChat || settings.disableCam || settings.disableMic || settings.lockedLayout || settings.disablePublicChat;
|
||||
return locked && lockInAction;
|
||||
};
|
||||
|
||||
let setUserPresenter = function (user) {
|
||||
setUserPresenter(BBB.getMeetingId(), user.id, getInSession('userId'), user.name, getInSession('authToken'));
|
||||
};
|
||||
|
||||
let openChat = function (user) {
|
||||
const currentUser = BBB.getCurrentUser();
|
||||
const currentUserId = currentUser.userId;
|
||||
|
||||
let userIdSelected = user.id;
|
||||
|
||||
if (userIdSelected !== null) {
|
||||
if (userIdSelected === currentUserId) {
|
||||
setInSession('inChatWith', 'PUBLIC_CHAT');
|
||||
} else {
|
||||
setInSession('inChatWith', userIdSelected);
|
||||
}
|
||||
}
|
||||
|
||||
if (isPortrait() || isPortraitMobile()) {
|
||||
toggleUserlistMenu();
|
||||
toggleShield();
|
||||
}
|
||||
|
||||
return setTimeout(() => { // waits until the end of execution queue
|
||||
return $('#newMessageInput').focus();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
let kickUser = function (user) {
|
||||
kickUser(BBB.getMeetingId(), user.id, getInSession('userId'), getInSession('authToken'));
|
||||
};
|
||||
|
||||
let muteUser = function () {
|
||||
BBB.toggleMyMic();
|
||||
};
|
||||
|
||||
let mapUsers = function () {
|
||||
const currentUser = BBB.getCurrentUser();
|
||||
const isCurrentUserModerator = currentUser.user.role === 'MODERATOR';
|
||||
const currentUserId = currentUser.userId;
|
||||
|
||||
const chats = getInSession('chats');
|
||||
|
||||
let users = Users.find().fetch().map(u => u.user).map(u => {
|
||||
let user = {
|
||||
id: u.userid,
|
||||
name: u.name,
|
||||
isCurrent: u.userid === currentUserId,
|
||||
isPresenter: u.presenter,
|
||||
isModerator: u.role === 'MODERATOR',
|
||||
emoji: u.emoji_status,
|
||||
unreadMessagesCount: 0,
|
||||
sharingStatus: {
|
||||
isInAudio: BBB.isUserInAudio(u.userid),
|
||||
isLocked: shouldUserBeLocked(u.userid), //TODO: Migrate blaze logic
|
||||
isWebcamOpen: u.webcam_stream.length,
|
||||
isListenOnly: u.listenOnly,
|
||||
isMuted: u.voiceUser.muted,
|
||||
isTalking: u.voiceUser.talking,
|
||||
},
|
||||
actions: {
|
||||
kick(user) {
|
||||
kickUser(user);
|
||||
},
|
||||
|
||||
setPresenter(user) {
|
||||
setUserPresenter(user);
|
||||
},
|
||||
|
||||
openChat(user) {
|
||||
openChat(user);
|
||||
},
|
||||
|
||||
muteUser() {
|
||||
muteUser();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
chats.forEach(c => {
|
||||
let key = c.userId;
|
||||
|
||||
// show unread count for public chat on the user itself
|
||||
if (user.isCurrent) {
|
||||
key = 'PUBLIC_CHAT';
|
||||
}
|
||||
|
||||
if (c.gotMail && c.userId === user.id) {
|
||||
user.unreadMessagesCount = c.number;
|
||||
}
|
||||
});
|
||||
|
||||
return user;
|
||||
});
|
||||
|
||||
return {
|
||||
// All this mapping should be on a service and not on the component itself
|
||||
currentUser: users.find(u => u.isCurrent),
|
||||
users: users,
|
||||
};
|
||||
};
|
||||
|
||||
export {mapUsers};
|
@ -1,87 +0,0 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button } from '../shared/Button.jsx';
|
||||
import { Presentations } from '/collections/collections';
|
||||
|
||||
export let EmojiContainer = React.createClass({
|
||||
mixins: [ReactMeteorData],
|
||||
getMeteorData() {
|
||||
let user, emoji_status, current_presentation;
|
||||
user = BBB.getCurrentUser();
|
||||
if (user != null) {
|
||||
emoji_status = user.user.emoji_status;
|
||||
} else {
|
||||
emoji_status = 'none';
|
||||
}
|
||||
|
||||
current_presentation = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
});
|
||||
current_presentation ? current_presentation = true : current_presentation = false;
|
||||
return {
|
||||
emoji_status: emoji_status,
|
||||
current_presentation: current_presentation,
|
||||
};
|
||||
},
|
||||
|
||||
getCurrentUserEmojiStatus(name) {
|
||||
return name == this.data.emoji_status;
|
||||
},
|
||||
|
||||
emojiIcons() {
|
||||
return [
|
||||
{ name: 'sad', icon: 'sad-face', title: '' },
|
||||
{ name: 'happy', icon: 'happy-face', title: '' },
|
||||
{ name: 'confused', icon: 'confused-face', title: '' },
|
||||
{ name: 'neutral', icon: 'neutral-face', title: '' },
|
||||
{ name: 'away', icon: 'clock', title: '' },
|
||||
{ name: 'raiseHand', icon: 'hand', title: 'Lower your Hand' },
|
||||
];
|
||||
},
|
||||
|
||||
handleInactive(name, event) {
|
||||
if ($(event.target).css('opacity') === '1') {
|
||||
BBB.setEmojiStatus(
|
||||
BBB.getMeetingId(),
|
||||
getInSession('userId'),
|
||||
getInSession('userId'),
|
||||
getInSession('authToken'),
|
||||
name
|
||||
);
|
||||
$('.FABTriggerButton').blur();
|
||||
return toggleEmojisFAB();
|
||||
}
|
||||
},
|
||||
|
||||
handleActive(event) {
|
||||
if ($('.activeEmojiButton').css('opacity') === '1') {
|
||||
BBB.setEmojiStatus(
|
||||
BBB.getMeetingId(),
|
||||
getInSession('userId'),
|
||||
getInSession('userId'),
|
||||
getInSession('authToken'),
|
||||
'none'
|
||||
);
|
||||
$('.FABTriggerButton').blur();
|
||||
return toggleEmojisFAB();
|
||||
}
|
||||
},
|
||||
|
||||
handleFABTriggerButton(event) {
|
||||
$('.FABTriggerButton').blur();
|
||||
return toggleEmojisFAB();
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={ classNames('FABContainer', !this.data.current_presentation ? 'noPresentation' : '') }>
|
||||
<Button onClick={ this.handleFABTriggerButton } btn_class=" FABTriggerButton" i_class="ion-android-hand"/>
|
||||
{this.emojiIcons().map((emoji) =>
|
||||
<Button btn_class={ classNames(' ' + emoji.name + 'EmojiButton', this.getCurrentUserEmojiStatus(emoji.name) ? 'activeEmojiButton' : 'inactiveEmojiButton') }
|
||||
onClick={ this.getCurrentUserEmojiStatus(emoji.name) ? this.handleActive : this.handleInactive.bind(null, emoji.name) }
|
||||
key={emoji.name} emoji={ emoji.icon }/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,234 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Shapes, WhiteboardCleanStatus } from '/collections/collections';
|
||||
|
||||
this.scaleSlide = function (originalWidth, originalHeight) {
|
||||
let adjustedHeight, adjustedWidth, boardHeight, boardWidth;
|
||||
|
||||
// set the size of the whiteboard space (frame) where the slide will be displayed
|
||||
if (window.matchMedia('(orientation: landscape)').matches) {
|
||||
// for landscape orientation we want "fit to height" so that we can
|
||||
// minimize the empty space above and below the slide (for best readability)
|
||||
boardWidth = $('#whiteboard-container').width();
|
||||
boardHeight = $('#whiteboard-container').height();
|
||||
} else {
|
||||
// for portrait orientation we want "fit to width" so that we can
|
||||
// minimize the empty space on the sides of the slide (for best readability)
|
||||
boardWidth = $('#whiteboard-container').width();
|
||||
boardHeight = 1.4 * $('#whiteboard-container').width();
|
||||
}
|
||||
|
||||
// this is the best fitting pair
|
||||
adjustedWidth = null;
|
||||
adjustedHeight = null;
|
||||
|
||||
// the slide image is in portrait orientation
|
||||
if (originalWidth <= originalHeight) {
|
||||
adjustedWidth = boardHeight * originalWidth / originalHeight;
|
||||
if (boardWidth < adjustedWidth) {
|
||||
adjustedHeight = boardHeight * boardWidth / adjustedWidth;
|
||||
adjustedWidth = boardWidth;
|
||||
} else {
|
||||
adjustedHeight = boardHeight;
|
||||
}
|
||||
|
||||
// ths slide image is in landscape orientation
|
||||
} else {
|
||||
adjustedHeight = boardWidth * originalHeight / originalWidth;
|
||||
if (boardHeight < adjustedHeight) {
|
||||
adjustedWidth = boardWidth * boardHeight / adjustedHeight;
|
||||
adjustedHeight = boardHeight;
|
||||
} else {
|
||||
adjustedWidth = boardWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: adjustedWidth,
|
||||
height: adjustedHeight,
|
||||
boardWidth: boardWidth,
|
||||
boardHeight: boardHeight,
|
||||
};
|
||||
};
|
||||
|
||||
export let Slide = React.createClass({
|
||||
mixins: [ReactMeteorData],
|
||||
getMeteorData() {
|
||||
let currentSlide, shapes, pointer;
|
||||
currentSlide = BBB.getCurrentSlide();
|
||||
|
||||
if (currentSlide != null) {
|
||||
shapes = Shapes.find({
|
||||
whiteboardId: currentSlide.slide.id,
|
||||
}).fetch();
|
||||
}
|
||||
|
||||
return {
|
||||
current_slide: currentSlide,
|
||||
shapes: shapes,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
//console.log('componentDidMount');
|
||||
this.reactOnSlideChange();
|
||||
},
|
||||
|
||||
componentWillUpdate: function () {
|
||||
if (typeof this.whiteboardPaperModel !== 'undefined' && this.whiteboardPaperModel !== null) {
|
||||
wpm = this.whiteboardPaperModel;
|
||||
wpm.clearShapes();
|
||||
|
||||
//this.manuallyDisplayShapes();
|
||||
}
|
||||
|
||||
//console.log('componentWillUpdate');
|
||||
},
|
||||
|
||||
componentDidUpdate: function () {
|
||||
//console.log('componentDidUpdate');
|
||||
if (this.data.current_slide) {
|
||||
this.reactOnSlideChange();
|
||||
if (this.data.shapes) {
|
||||
this.data.shapes.map((shape) =>
|
||||
this.renderShape(shape));
|
||||
}
|
||||
} else {
|
||||
this.clearSlide();
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function () {
|
||||
//console.log('componentWillUnmount');
|
||||
},
|
||||
|
||||
createWhiteboardPaper: function (callback) {
|
||||
this.whiteboardPaperModel = new WhiteboardPaperModel('whiteboard-paper');
|
||||
return callback(this.whiteboardPaperModel);
|
||||
},
|
||||
|
||||
displaySlide: function (wpm) {
|
||||
let adjustedDimensions, currentSlide, ref;
|
||||
currentSlide = BBB.getCurrentSlide();
|
||||
wpm.create();
|
||||
adjustedDimensions = scaleSlide(getInSession('slideOriginalWidth'), getInSession('slideOriginalHeight'));
|
||||
wpm._displayPage(
|
||||
currentSlide != null ? (ref = currentSlide.slide) != null ? ref.img_uri : void 0 : void 0,
|
||||
getInSession('slideOriginalWidth'),
|
||||
getInSession('slideOriginalHeight')
|
||||
);
|
||||
this.manuallyDisplayShapes();
|
||||
return wpm.scale(adjustedDimensions.width, adjustedDimensions.height);
|
||||
},
|
||||
|
||||
clearSlide() {
|
||||
let ref;
|
||||
|
||||
//clear the slide
|
||||
if (typeof this.whiteboardPaperModel !== 'undefined' && this.whiteboardPaperModel !== null) {
|
||||
this.whiteboardPaperModel.removeAllImagesFromPaper();
|
||||
}
|
||||
|
||||
//hide the cursor
|
||||
return typeof this.whiteboardPaperModel !== 'undefined' && this.whiteboardPaperModel !== null ? (ref = this.whiteboardPaperModel.cursor) != null ? ref.remove() : void 0 : void 0;
|
||||
},
|
||||
|
||||
manuallyDisplayShapes: function () {
|
||||
let currentSlide, i, j, len, len1, num, ref, ref1, ref2, results, s, shapeInfo, shapeType, shapes, wpm;
|
||||
if (WhiteboardCleanStatus.findOne({
|
||||
in_progress: true,
|
||||
}) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSlide = BBB.getCurrentSlide();
|
||||
wpm = this.whiteboardPaperModel;
|
||||
shapes = Shapes.find({
|
||||
whiteboardId: currentSlide != null ? (ref = currentSlide.slide) != null ? ref.id : void 0 : void 0,
|
||||
}).fetch();
|
||||
|
||||
results = [];
|
||||
for (i = 0, len1 = shapes.length; i < len1; i++) {
|
||||
s = shapes[i];
|
||||
shapeInfo = ((ref1 = s.shape) != null ? ref1.shape : void 0) || (s != null ? s.shape : void 0);
|
||||
shapeType = shapeInfo != null ? shapeInfo.type : void 0;
|
||||
if (shapeType !== 'text') {
|
||||
len = shapeInfo.points.length;
|
||||
for (num = j = 0, ref2 = len; 0 <= ref2 ? j <= ref2 : j >= ref2; num = 0 <= ref2 ? ++j : --j) { // the coordinates must be in the range 0 to 1
|
||||
if (shapeInfo != null) {
|
||||
shapeInfo.points[num] = (shapeInfo != null ? shapeInfo.points[num] : void 0) / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wpm != null) {
|
||||
wpm.makeShape(shapeType, shapeInfo);
|
||||
}
|
||||
|
||||
results.push(wpm != null ? wpm.updateShape(shapeType, shapeInfo) : void 0);
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
reactOnSlideChange: function () {
|
||||
var _this = this;
|
||||
var currentSlide, pic, ref;
|
||||
currentSlide = BBB.getCurrentSlide('slide.rendered');
|
||||
pic = new Image();
|
||||
pic.onload = function () {
|
||||
var ref;
|
||||
setInSession('slideOriginalWidth', this.width);
|
||||
setInSession('slideOriginalHeight', this.height);
|
||||
$(window).resize(function () {
|
||||
if (!$('.panel-footer').hasClass('ui-resizable-resizing')) {
|
||||
scaleWhiteboard(_this.whiteboardPaperModel);
|
||||
}
|
||||
});
|
||||
|
||||
if ((currentSlide != null ? (ref = currentSlide.slide) != null ? ref.img_uri : void 0 : void 0) != null) {
|
||||
return _this.createWhiteboardPaper(function (wpm) {
|
||||
return _this.displaySlide(wpm);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pic.src = currentSlide != null ? (ref = currentSlide.slide) != null ? ref.img_uri : void 0 : void 0;
|
||||
return '';
|
||||
},
|
||||
|
||||
updatePointerLocation(pointer) {
|
||||
if (typeof this.whiteboardPaperModel !== 'undefined' && this.whiteboardPaperModel !== null) {
|
||||
this.whiteboardPaperModel.moveCursor(pointer.x, pointer.y);
|
||||
}
|
||||
},
|
||||
|
||||
renderShape(data) {
|
||||
let i, len, num, ref, ref1, shapeInfo, shapeType, wpm;
|
||||
|
||||
// @data is the shape object coming from the {{#each}} in the html file
|
||||
shapeInfo = ((ref = data.shape) != null ? ref.shape : void 0) || data.shape;
|
||||
shapeType = shapeInfo != null ? shapeInfo.type : void 0;
|
||||
if (shapeType !== 'text') {
|
||||
len = shapeInfo.points.length;
|
||||
for (num = i = 0, ref1 = len; 0 <= ref1 ? i <= ref1 : i >= ref1; num = 0 <= ref1 ? ++i : --i) { // the coordinates must be in the range 0 to 1
|
||||
shapeInfo.points[num] = shapeInfo.points[num] / 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.whiteboardPaperModel !== 'undefined' && this.whiteboardPaperModel !== null) {
|
||||
wpm = this.whiteboardPaperModel;
|
||||
if (wpm != null) {
|
||||
wpm.makeShape(shapeType, shapeInfo);
|
||||
}
|
||||
|
||||
return wpm != null ? wpm.updateShape(shapeType, shapeInfo) : void 0;
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="whiteboard-paper">
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,131 +0,0 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button } from '../shared/Button.jsx';
|
||||
import { PresentationList } from './PresentationList.jsx';
|
||||
|
||||
export let UploaderControls = React.createClass({
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
isOpen: new ReactiveVar(false),
|
||||
files: new ReactiveList({
|
||||
sort(a, b) {
|
||||
// Put the ones who still uploading first
|
||||
let ref, ref1;
|
||||
return (ref = a.isUploading === b.isUploading) != null ? ref : {
|
||||
0: (ref1 = a.isUploading) != null ? ref1 : -{
|
||||
1: 1,
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
||||
mixins: [ReactMeteorData],
|
||||
getMeteorData() {
|
||||
let presentations;
|
||||
presentations = Presentations.find({}, {
|
||||
sort: {
|
||||
'presentation.current': -1,
|
||||
'presentation.name': 1,
|
||||
},
|
||||
fields: {
|
||||
presentation: 1,
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
return {
|
||||
presentations: presentations,
|
||||
};
|
||||
},
|
||||
|
||||
fakeUpload(file, list) {
|
||||
return setTimeout((() => {
|
||||
file.uploadedSize = file.uploadedSize + (Math.floor(Math.random() * file.size + file.uploadedSize) / 10);
|
||||
file.percUploaded = Math.round((file.uploadedSize / file.size) * 100) + '%';
|
||||
if (!(file.size > file.uploadedSize)) {
|
||||
file.uploadedSize = file.size;
|
||||
file.isUploading = false;
|
||||
}
|
||||
|
||||
list.update(file.name, file);
|
||||
this.forceUpdate();
|
||||
if (file.isUploading === true) {
|
||||
return this.fakeUpload(file, list);
|
||||
} else {
|
||||
list.remove(file.name); // TODO: Here we should remove and update te presentation on mongo
|
||||
this.forceUpdate();
|
||||
return;
|
||||
}
|
||||
}), 200);
|
||||
},
|
||||
|
||||
isOpen() {
|
||||
return this.props.isOpen.get() ? 'is-open' : '';
|
||||
},
|
||||
|
||||
files() {
|
||||
return this.props.files ? this.props.files.fetch() : null;
|
||||
},
|
||||
|
||||
presentations() {
|
||||
return this.data.presentations.map(x => {
|
||||
return x.presentation;
|
||||
});
|
||||
},
|
||||
|
||||
handleInput(event) {
|
||||
let files;
|
||||
event.preventDefault();
|
||||
files = (event.dataTransfer || event.target).files;
|
||||
return _.each(files, file => {
|
||||
file.isUploading = true;
|
||||
file.uploadedSize = 0;
|
||||
file.percUploaded = '0';
|
||||
this.props.files.insert(file.name, file);
|
||||
return this.fakeUpload(file, this.props.files);
|
||||
});
|
||||
},
|
||||
|
||||
handleDragLeave(event) {
|
||||
event.preventDefault();
|
||||
return $(event.currentTarget).removeClass('hover');
|
||||
},
|
||||
|
||||
handleDragOver(event) {
|
||||
event.preventDefault();
|
||||
$(event.currentTarget).addClass('hover');
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.props.isOpen.set(false);
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
handleOpen() {
|
||||
this.props.isOpen.set(true);
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={classNames('presenter-uploader-control', this.isOpen() ? 'is-open' : '')} >
|
||||
<div className="presenter-uploader-container">
|
||||
<PresentationList files={this.files()} presentations={this.presentations()} />
|
||||
<div onDrop={this.handleInput} onDragOver={this.handleDragOver} onDragLeave={this.handleDragLeave} className="presenter-uploader-dropzone" data-dropzone>
|
||||
<input onChange={this.handleInput} type="file" className="presenter-uploader-dropzone-fileinput" multiple />
|
||||
<i className="presenter-uploader-dropzone-icon ion-archive"></i>
|
||||
<span className="presenter-uploader-dropzone-label">Drop files here <br/>or click to upload</span>
|
||||
</div>
|
||||
<Button onClick={this.handleClose} btn_class=" presenter-uploader-control-btn js-close" i_class="ion-ios-close-outline"/>
|
||||
<div className="presenter-uploader-tip">
|
||||
UPLOAD ANY OFFICE DOCUMENT OR PORTABLE DOCUMENT FORMAT (PDF) FILE.
|
||||
<br/>
|
||||
FOR BEST RESULTS UPLOAD PDF.
|
||||
</div>
|
||||
</div>
|
||||
<Button onClick={this.handleOpen} btn_class=" presenter-uploader-control-btn js-open" i_class="ion-ios-upload-outline"/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@ -1,75 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button } from '../shared/Button.jsx';
|
||||
import { UploaderControls } from './UploaderControls.jsx';
|
||||
|
||||
export let WhiteboardControls = React.createClass({
|
||||
mixins: [ReactMeteorData],
|
||||
getMeteorData() {
|
||||
let currentPresentation, currentSlide, currentSlideNum, ref, ref1, totalSlideNum;
|
||||
currentSlideNum = 0;
|
||||
totalSlideNum = 0;
|
||||
currentPresentation = Presentations.findOne({
|
||||
'presentation.current': true,
|
||||
});
|
||||
if (currentPresentation != null) {
|
||||
currentSlide = Slides.findOne({
|
||||
presentationId: currentPresentation.presentation.id,
|
||||
'slide.current': true,
|
||||
});
|
||||
if (currentSlide != null) {
|
||||
currentSlideNum = currentSlide.slide.num;
|
||||
}
|
||||
|
||||
totalSlides = Slides.find({ presentationId: currentPresentation.presentation.id });
|
||||
if (totalSlides != null) {
|
||||
totalSlideNum = totalSlides.count();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentSlideNum: currentSlideNum,
|
||||
totalSlideNum: totalSlideNum,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(scaleWhiteboard, 0);
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
setTimeout(scaleWhiteboard, 0);
|
||||
},
|
||||
|
||||
handlePrevious() {
|
||||
BBB.goToPreviousPage();
|
||||
},
|
||||
|
||||
handleNext() {
|
||||
BBB.goToNextPage();
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="whiteboard-controls">
|
||||
<div className="whiteboard-buttons-left">
|
||||
{/* TODO: Adjust the presenter uploader for mobile views on iOS devices
|
||||
you cant upload PDFs, only images from camera/gallery */
|
||||
!isMobile() ?
|
||||
<UploaderControls />
|
||||
: null }
|
||||
</div>
|
||||
<div className="whiteboard-buttons-right">
|
||||
</div>
|
||||
<div className="whiteboard-buttons-center">
|
||||
<div className="whiteboard-buttons-slide">
|
||||
<Button onClick={this.handlePrevious} btn_class=" prev" i_class="ion-arrow-left-a" rel="tooltip" data_placement="top" title="Previous"/>
|
||||
<span className="current">
|
||||
{this.data.currentSlideNum + '/' + this.data.totalSlideNum}
|
||||
</span>
|
||||
<Button onClick={this.handleNext} btn_class=" next" i_class="ion-arrow-right-a" rel="tooltip" data_placement="top" title="Next"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user