incorrectly merged these

This commit is contained in:
Anton Georgiev 2016-05-04 22:19:41 +00:00
parent 0deaadfd87
commit eddef1e1b6
11 changed files with 0 additions and 2937 deletions

View File

@ -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>
);
},
});

View File

@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
};
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');
}
};

View File

@ -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;
})();

View File

@ -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;
})();

View File

@ -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>
);
},
});

View File

@ -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>
);
},
});

View File

@ -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};

View File

@ -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>
);
},
});

View File

@ -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>
);
},
});

View File

@ -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>
);
},
});

View File

@ -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>
);
},
});