[mirotalksfu] - add collaborative whiteboard
هذا الالتزام موجود في:
@@ -16,6 +16,7 @@ Powered by `WebRTC` with [SFU](https://mediasoup.org) integrated server.
|
|||||||
- Echo cancellation and noise suppression that makes your audio crystal clear
|
- Echo cancellation and noise suppression that makes your audio crystal clear
|
||||||
- Screen Sharing to present documents, slides, and more ...
|
- Screen Sharing to present documents, slides, and more ...
|
||||||
- Chat with Emoji Picker to show you feeling and possibility to Save the conversations
|
- Chat with Emoji Picker to show you feeling and possibility to Save the conversations
|
||||||
|
- Collaborative whiteboard for the teachers
|
||||||
- Select Microphone - Speaker and Video source
|
- Select Microphone - Speaker and Video source
|
||||||
- Recording your Screen, Audio or Video
|
- Recording your Screen, Audio or Video
|
||||||
- Full Screen Mode on mouse click on the Video element
|
- Full Screen Mode on mouse click on the Video element
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
<script src="https://kit.fontawesome.com/d2f1016e6f.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/d2f1016e6f.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.rawgit.com/muaz-khan/DetectRTC/master/DetectRTC.js"></script>
|
<script src="https://cdn.rawgit.com/muaz-khan/DetectRTC/master/DetectRTC.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/460/fabric.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.1.4"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.1.4"></script>
|
||||||
<script src="https://unpkg.com/emoji-picker-element@1" type="module"></script>
|
<script src="https://unpkg.com/emoji-picker-element@1" type="module"></script>
|
||||||
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||||
@@ -137,6 +138,22 @@ access to use this app.
|
|||||||
<button id="stopVideoButton" class="hidden"><i class="fas fa-video"></i> Stop video</button>
|
<button id="stopVideoButton" class="hidden"><i class="fas fa-video"></i> Stop video</button>
|
||||||
<button id="startScreenButton" class="hidden"><i class="fas fa-desktop"></i> Start screen</button>
|
<button id="startScreenButton" class="hidden"><i class="fas fa-desktop"></i> Start screen</button>
|
||||||
<button id="stopScreenButton" class="hidden"><i class="fas fa-stop-circle"></i> Stop screen</button>
|
<button id="stopScreenButton" class="hidden"><i class="fas fa-stop-circle"></i> Stop screen</button>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="whiteboardButton" class="hidden">
|
||||||
|
<i class="fas fa-chalkboard-teacher"></i> Whiteboard
|
||||||
|
</button>
|
||||||
|
<div id="whiteboardSettings" class="dropdown-content fadein">
|
||||||
|
<div id="whiteboardOptions">
|
||||||
|
<i class="fas fa-pencil-alt"></i> Line width
|
||||||
|
<input id="wbDrawingLineWidthEl" type="range" value="3" min="1" max="15" />
|
||||||
|
<br />
|
||||||
|
<i class="fas fa-palette"></i> Line color
|
||||||
|
<br />
|
||||||
|
<input id="wbDrawingColorEl" type="color" value="#FFFFFF" />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button id="participantsButton" class="hidden"><i class="fas fa-users"></i> Participants</button>
|
<button id="participantsButton" class="hidden"><i class="fas fa-users"></i> Participants</button>
|
||||||
<button id="lockRoomButton" class="hidden"><i class="fas fa-lock-open"></i> Lock room</button>
|
<button id="lockRoomButton" class="hidden"><i class="fas fa-lock-open"></i> Lock room</button>
|
||||||
<button id="unlockRoomButton" class="hidden"><i class="fas fa-lock"></i> Unlock room</button>
|
<button id="unlockRoomButton" class="hidden"><i class="fas fa-lock"></i> Unlock room</button>
|
||||||
@@ -163,6 +180,22 @@ access to use this app.
|
|||||||
</main>
|
</main>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="whiteboard" class="fadein center hidden">
|
||||||
|
<header id="whiteboardHeader" class="whiteboard-header">
|
||||||
|
<div id="whiteboardTitle" class="whiteboard-header-title"></div>
|
||||||
|
<div class="whiteboard-header-options">
|
||||||
|
<button id="whiteboardUndoBtn" class="fas fa-undo"></button>
|
||||||
|
<button id="whiteboardRedoBtn" class="fas fa-redo"></button>
|
||||||
|
<button id="whiteboardSaveBtn" class="fas fa-save"></button>
|
||||||
|
<button id="whiteboardCleanBtn" class="fas fa-trash"></button>
|
||||||
|
<button id="whiteboardCloseBtn" class="fas fa-times"></button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<canvas id="wbCanvas"></canvas>
|
||||||
|
</main>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="chatRoom" class="chat-room center fadein">
|
<section id="chatRoom" class="chat-room center fadein">
|
||||||
<section id="msger" class="msger">
|
<section id="msger" class="msger">
|
||||||
<header id="chatHeader" class="chat-header">
|
<header id="chatHeader" class="chat-header">
|
||||||
|
|||||||
@@ -38,6 +38,9 @@
|
|||||||
--msger-height: 680px;
|
--msger-height: 680px;
|
||||||
--msger-width: 420px;
|
--msger-width: 420px;
|
||||||
--msger-bg: linear-gradient(to left, #1f1e1e, #000000);
|
--msger-bg: linear-gradient(to left, #1f1e1e, #000000);
|
||||||
|
--wb-width: 800px;
|
||||||
|
--wb-height: 600px;
|
||||||
|
--wb-bg: linear-gradient(to left, #1f1e1e, #000000);
|
||||||
--left-msg-bg: #222328;
|
--left-msg-bg: #222328;
|
||||||
--right-msg-bg: #0a0b0c;
|
--right-msg-bg: #0a0b0c;
|
||||||
--box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
--box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||||
@@ -50,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
min-height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -81,7 +84,7 @@ body {
|
|||||||
--------------------------------------------------------------*/
|
--------------------------------------------------------------*/
|
||||||
|
|
||||||
#openNavButton {
|
#openNavButton {
|
||||||
z-index: 2;
|
z-index: 3;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@@ -92,7 +95,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidenav {
|
.sidenav {
|
||||||
z-index: 3;
|
z-index: 4;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 0;
|
width: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -149,7 +152,7 @@ body {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
background: black;
|
background: var(--body-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#control button {
|
#control button {
|
||||||
@@ -162,6 +165,7 @@ body {
|
|||||||
|
|
||||||
#settings {
|
#settings {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background: var(--body-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
@@ -207,6 +211,7 @@ body {
|
|||||||
|
|
||||||
#recording {
|
#recording {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background: var(--body-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#recording button,
|
#recording button,
|
||||||
@@ -222,7 +227,7 @@ body {
|
|||||||
--------------------------------------------------------------*/
|
--------------------------------------------------------------*/
|
||||||
|
|
||||||
.chat-room {
|
.chat-room {
|
||||||
z-index: 4;
|
z-index: 5;
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
height: var(--msger-height);
|
height: var(--msger-height);
|
||||||
@@ -528,7 +533,7 @@ button:hover {
|
|||||||
--------------------------------------------------------------*/
|
--------------------------------------------------------------*/
|
||||||
|
|
||||||
#participants {
|
#participants {
|
||||||
z-index: 5;
|
z-index: 6;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@@ -572,6 +577,37 @@ button:hover {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
# Whiteboard
|
||||||
|
--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#whiteboardSettings {
|
||||||
|
position: relative;
|
||||||
|
background: var(--body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#whiteboard {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
margin: auto;
|
||||||
|
padding: 10px;
|
||||||
|
width: var(--wb-width);
|
||||||
|
height: var(--wb-height);
|
||||||
|
background: var(--wb-bg);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whiteboard-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: rgb(0, 0, 0);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
color: white;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
# Pulse class effect
|
# Pulse class effect
|
||||||
--------------------------------------------------------------*/
|
--------------------------------------------------------------*/
|
||||||
@@ -623,8 +659,10 @@ button:hover {
|
|||||||
/*
|
/*
|
||||||
z-index:
|
z-index:
|
||||||
- 1 videoMediaContainer
|
- 1 videoMediaContainer
|
||||||
- 2 buttonBar
|
- 2 whiteboard
|
||||||
- 3 sidenav
|
- 3 buttonBar
|
||||||
- 4 chat
|
- 4 sidenav
|
||||||
- 5 participants
|
- 4 whiteboard
|
||||||
|
- 5 chat
|
||||||
|
- 6 participants
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -41,6 +41,14 @@ let isVideoOn = true;
|
|||||||
let recTimer = null;
|
let recTimer = null;
|
||||||
let recElapsedTime = null;
|
let recElapsedTime = null;
|
||||||
|
|
||||||
|
const wbWidth = 800;
|
||||||
|
const wbHeight = 600;
|
||||||
|
let wbCanvas = null;
|
||||||
|
let wbIsDrawing = false;
|
||||||
|
let wbIsOpen = false;
|
||||||
|
var wbIsRedoing = false;
|
||||||
|
var wbPop = [];
|
||||||
|
|
||||||
const socket = io();
|
const socket = io();
|
||||||
|
|
||||||
function getRandomNumber(length) {
|
function getRandomNumber(length) {
|
||||||
@@ -56,6 +64,11 @@ function getRandomNumber(length) {
|
|||||||
function initClient() {
|
function initClient() {
|
||||||
if (!DetectRTC.isMobileDevice) {
|
if (!DetectRTC.isMobileDevice) {
|
||||||
setTippy('closeNavButton', 'Close', 'right');
|
setTippy('closeNavButton', 'Close', 'right');
|
||||||
|
setTippy('whiteboardUndoBtn', 'Undo', 'top');
|
||||||
|
setTippy('whiteboardRedoBtn', 'Redo', 'top');
|
||||||
|
setTippy('whiteboardSaveBtn', 'Save', 'top');
|
||||||
|
setTippy('whiteboardCleanBtn', 'Clear', 'top');
|
||||||
|
setTippy('whiteboardCloseBtn', 'Close', 'top');
|
||||||
setTippy('participantsRefreshBtn', 'Refresh', 'top');
|
setTippy('participantsRefreshBtn', 'Refresh', 'top');
|
||||||
setTippy('participantsCloseBtn', 'Close', 'top');
|
setTippy('participantsCloseBtn', 'Close', 'top');
|
||||||
setTippy('chatMessage', 'Press enter to send', 'top-start');
|
setTippy('chatMessage', 'Press enter to send', 'top-start');
|
||||||
@@ -66,6 +79,7 @@ function initClient() {
|
|||||||
setTippy('chatCloseButton', 'Close', 'bottom');
|
setTippy('chatCloseButton', 'Close', 'bottom');
|
||||||
setTippy('sessionTime', 'Session time', 'right');
|
setTippy('sessionTime', 'Session time', 'right');
|
||||||
}
|
}
|
||||||
|
setupWhiteboard();
|
||||||
initEnumerateDevices();
|
initEnumerateDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +408,7 @@ function roomIsReady() {
|
|||||||
} else {
|
} else {
|
||||||
rc.makeDraggable(chatRoom, chatHeader);
|
rc.makeDraggable(chatRoom, chatHeader);
|
||||||
rc.makeDraggable(participants, participantsHeader);
|
rc.makeDraggable(participants, participantsHeader);
|
||||||
|
rc.makeDraggable(whiteboard, whiteboardHeader);
|
||||||
if (navigator.getDisplayMedia || navigator.mediaDevices.getDisplayMedia) {
|
if (navigator.getDisplayMedia || navigator.mediaDevices.getDisplayMedia) {
|
||||||
show(startScreenButton);
|
show(startScreenButton);
|
||||||
}
|
}
|
||||||
@@ -408,6 +423,7 @@ function roomIsReady() {
|
|||||||
show(raiseHandButton);
|
show(raiseHandButton);
|
||||||
if (isAudioAllowed) show(startAudioButton);
|
if (isAudioAllowed) show(startAudioButton);
|
||||||
if (isVideoAllowed) show(startVideoButton);
|
if (isVideoAllowed) show(startVideoButton);
|
||||||
|
show(whiteboardButton);
|
||||||
show(participantsButton);
|
show(participantsButton);
|
||||||
show(lockRoomButton);
|
show(lockRoomButton);
|
||||||
show(aboutButton);
|
show(aboutButton);
|
||||||
@@ -579,6 +595,24 @@ function handleButtons() {
|
|||||||
stopScreenButton.onclick = () => {
|
stopScreenButton.onclick = () => {
|
||||||
rc.closeProducer(RoomClient.mediaType.screen);
|
rc.closeProducer(RoomClient.mediaType.screen);
|
||||||
};
|
};
|
||||||
|
whiteboardButton.onclick = () => {
|
||||||
|
toggleWhiteboard();
|
||||||
|
};
|
||||||
|
whiteboardUndoBtn.onclick = () => {
|
||||||
|
whiteboardAction(getWhiteboardAction('undo'));
|
||||||
|
};
|
||||||
|
whiteboardRedoBtn.onclick = () => {
|
||||||
|
whiteboardAction(getWhiteboardAction('redo'));
|
||||||
|
};
|
||||||
|
whiteboardSaveBtn.onclick = () => {
|
||||||
|
wbCanvasSaveImg();
|
||||||
|
};
|
||||||
|
whiteboardCleanBtn.onclick = () => {
|
||||||
|
whiteboardAction(getWhiteboardAction('clear'));
|
||||||
|
};
|
||||||
|
whiteboardCloseBtn.onclick = () => {
|
||||||
|
whiteboardAction(getWhiteboardAction('close'));
|
||||||
|
};
|
||||||
participantsButton.onclick = () => {
|
participantsButton.onclick = () => {
|
||||||
getRoomParticipants();
|
getRoomParticipants();
|
||||||
};
|
};
|
||||||
@@ -604,6 +638,7 @@ function handleButtons() {
|
|||||||
// ####################################################
|
// ####################################################
|
||||||
|
|
||||||
function handleSelects() {
|
function handleSelects() {
|
||||||
|
// devices options
|
||||||
microphoneSelect.onchange = () => {
|
microphoneSelect.onchange = () => {
|
||||||
rc.closeThenProduce(RoomClient.mediaType.audio, microphoneSelect.value);
|
rc.closeThenProduce(RoomClient.mediaType.audio, microphoneSelect.value);
|
||||||
};
|
};
|
||||||
@@ -613,6 +648,13 @@ function handleSelects() {
|
|||||||
videoSelect.onchange = () => {
|
videoSelect.onchange = () => {
|
||||||
rc.closeThenProduce(RoomClient.mediaType.video, videoSelect.value);
|
rc.closeThenProduce(RoomClient.mediaType.video, videoSelect.value);
|
||||||
};
|
};
|
||||||
|
// whiteboard options
|
||||||
|
wbDrawingLineWidthEl.onchange = () => {
|
||||||
|
wbCanvas.freeDrawingBrush.width = parseInt(wbDrawingLineWidthEl.value, 10) || 1;
|
||||||
|
};
|
||||||
|
wbDrawingColorEl.onchange = () => {
|
||||||
|
wbCanvas.freeDrawingBrush.color = wbDrawingColorEl.value;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
@@ -749,7 +791,7 @@ function handleRoomClientEvents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
// SHOW LOG
|
// UTILITY
|
||||||
// ####################################################
|
// ####################################################
|
||||||
|
|
||||||
function userLog(icon, message, position, timer = 3000) {
|
function userLog(icon, message, position, timer = 3000) {
|
||||||
@@ -766,6 +808,26 @@ function userLog(icon, message, position, timer = 3000) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveDataToFile(dataURL, fileName) {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = dataURL;
|
||||||
|
a.download = fileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(dataURL);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataTimeString() {
|
||||||
|
const d = new Date();
|
||||||
|
const date = d.toISOString().split('T')[0];
|
||||||
|
const time = d.toTimeString().split(' ')[0];
|
||||||
|
return `${date}-${time}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
// SOUND
|
// SOUND
|
||||||
// ####################################################
|
// ####################################################
|
||||||
@@ -780,6 +842,174 @@ async function sound(name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ####################################################
|
||||||
|
// HANDLE WHITEBOARD
|
||||||
|
// ####################################################
|
||||||
|
|
||||||
|
function toggleWhiteboard() {
|
||||||
|
toggleWhiteboardSettings();
|
||||||
|
let whiteboard = rc.getId('whiteboard');
|
||||||
|
whiteboard.classList.toggle('show');
|
||||||
|
whiteboard.style.top = '50%';
|
||||||
|
whiteboard.style.left = '50%';
|
||||||
|
wbIsOpen = wbIsOpen ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWhiteboardSettings() {
|
||||||
|
rc.getId('whiteboardSettings').classList.toggle('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupWhiteboard() {
|
||||||
|
setupWhiteboardCanvas();
|
||||||
|
setupWhiteboardCanvasSize();
|
||||||
|
setupWhiteboardLocalListners();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupWhiteboardCanvas() {
|
||||||
|
wbCanvas = new fabric.Canvas('wbCanvas');
|
||||||
|
wbCanvas.freeDrawingBrush.color = '#FFFFFF';
|
||||||
|
wbCanvas.freeDrawingBrush.width = 3;
|
||||||
|
wbCanvas.isDrawingMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupWhiteboardCanvasSize() {
|
||||||
|
let optimalSize = [wbWidth, wbHeight];
|
||||||
|
let scaleFactorX = window.innerWidth / optimalSize[0];
|
||||||
|
let scaleFactorY = window.innerHeight / optimalSize[1];
|
||||||
|
if (scaleFactorX < scaleFactorY && scaleFactorX < 1) {
|
||||||
|
wbCanvas.setWidth(optimalSize[0] * scaleFactorX);
|
||||||
|
wbCanvas.setHeight(optimalSize[1] * scaleFactorX);
|
||||||
|
wbCanvas.setZoom(scaleFactorX);
|
||||||
|
document.documentElement.style.setProperty('--wb-width', optimalSize[0] * scaleFactorX);
|
||||||
|
document.documentElement.style.setProperty('--wb-height', optimalSize[1] * scaleFactorX);
|
||||||
|
} else if (scaleFactorX > scaleFactorY && scaleFactorY < 1) {
|
||||||
|
wbCanvas.setWidth(optimalSize[0] * scaleFactorY);
|
||||||
|
wbCanvas.setHeight(optimalSize[1] * scaleFactorY);
|
||||||
|
wbCanvas.setZoom(scaleFactorY);
|
||||||
|
document.documentElement.style.setProperty('--wb-width', optimalSize[0] * scaleFactorY);
|
||||||
|
document.documentElement.style.setProperty('--wb-height', optimalSize[1] * scaleFactorY);
|
||||||
|
} else {
|
||||||
|
wbCanvas.setWidth(optimalSize[0]);
|
||||||
|
wbCanvas.setHeight(optimalSize[1]);
|
||||||
|
wbCanvas.setZoom(1);
|
||||||
|
document.documentElement.style.setProperty('--wb-width', optimalSize[0]);
|
||||||
|
document.documentElement.style.setProperty('--wb-height', optimalSize[1]);
|
||||||
|
}
|
||||||
|
wbCanvas.calcOffset();
|
||||||
|
wbCanvas.renderAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupWhiteboardLocalListners() {
|
||||||
|
wbCanvas.on('mouse:down', function () {
|
||||||
|
mouseDown();
|
||||||
|
});
|
||||||
|
wbCanvas.on('mouse:up', function () {
|
||||||
|
mouseUp();
|
||||||
|
});
|
||||||
|
wbCanvas.on('mouse:move', function () {
|
||||||
|
mouseMove();
|
||||||
|
});
|
||||||
|
wbCanvas.on('object:added', function () {
|
||||||
|
objectAdded();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseDown() {
|
||||||
|
wbIsDrawing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseUp() {
|
||||||
|
wbIsDrawing = false;
|
||||||
|
wbCanvasToJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseMove() {
|
||||||
|
if (!wbIsDrawing) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectAdded() {
|
||||||
|
if (!wbIsRedoing) wbPop = [];
|
||||||
|
wbIsRedoing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wbCanvasUndo() {
|
||||||
|
if (wbCanvas._objects.length > 0) {
|
||||||
|
wbPop.push(wbCanvas._objects.pop());
|
||||||
|
wbCanvas.renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wbCanvasRedo() {
|
||||||
|
if (wbPop.length > 0) {
|
||||||
|
wbIsRedoing = true;
|
||||||
|
wbCanvas.add(wbPop.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function wbCanvasSaveImg() {
|
||||||
|
const dataURL = wbCanvas.toDataURL({
|
||||||
|
width: wbCanvas.getWidth(),
|
||||||
|
height: wbCanvas.getHeight(),
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
format: 'png',
|
||||||
|
});
|
||||||
|
const dataNow = getDataTimeString();
|
||||||
|
const fileName = `whiteboard-${dataNow}.png`;
|
||||||
|
saveDataToFile(dataURL, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wbCanvasToJson() {
|
||||||
|
if (rc.thereIsConsumers()) {
|
||||||
|
var wbCanvasJson = JSON.stringify(wbCanvas.toJSON());
|
||||||
|
rc.socket.emit('wbCanvasToJson', wbCanvasJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JsonToWbCanvas(json) {
|
||||||
|
if (!wbIsOpen) toggleWhiteboard();
|
||||||
|
|
||||||
|
wbCanvas.loadFromJSON(json);
|
||||||
|
wbCanvas.renderAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWhiteboardAction(action) {
|
||||||
|
return {
|
||||||
|
peer_name: peer_name,
|
||||||
|
action: action,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function whiteboardAction(data, emit = true) {
|
||||||
|
if (emit) {
|
||||||
|
if (rc.thereIsConsumers()) {
|
||||||
|
rc.socket.emit('whiteboardAction', data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userLog(
|
||||||
|
'info',
|
||||||
|
`${data.peer_name} <i class="fas fa-chalkboard-teacher"></i> whiteboard action: ${data.action}`,
|
||||||
|
'top-end',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.action) {
|
||||||
|
case 'undo':
|
||||||
|
wbCanvasUndo();
|
||||||
|
break;
|
||||||
|
case 'redo':
|
||||||
|
wbCanvasRedo();
|
||||||
|
break;
|
||||||
|
case 'clear':
|
||||||
|
wbCanvas.clear();
|
||||||
|
break;
|
||||||
|
case 'close':
|
||||||
|
if (wbIsOpen) toggleWhiteboard();
|
||||||
|
break;
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
// HANDLE PARTICIPANTS
|
// HANDLE PARTICIPANTS
|
||||||
// ####################################################
|
// ####################################################
|
||||||
|
|||||||
@@ -393,6 +393,22 @@ class RoomClient {
|
|||||||
}.bind(this),
|
}.bind(this),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.socket.on(
|
||||||
|
'wbCanvasToJson',
|
||||||
|
function (data) {
|
||||||
|
console.log('Received whiteboard canvas JSON');
|
||||||
|
JsonToWbCanvas(data);
|
||||||
|
}.bind(this),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.socket.on(
|
||||||
|
'whiteboardAction',
|
||||||
|
function (data) {
|
||||||
|
console.log('Whiteboard action', data);
|
||||||
|
whiteboardAction(data, false);
|
||||||
|
}.bind(this),
|
||||||
|
);
|
||||||
|
|
||||||
this.socket.on(
|
this.socket.on(
|
||||||
'disconnect',
|
'disconnect',
|
||||||
function () {
|
function () {
|
||||||
@@ -1029,7 +1045,6 @@ class RoomClient {
|
|||||||
if (this.consumers.size > 0) {
|
if (this.consumers.size > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this.userLog('info', 'No participants in the room', 'top-end');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1156,6 +1171,7 @@ class RoomClient {
|
|||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (!this.thereIsConsumers()) {
|
if (!this.thereIsConsumers()) {
|
||||||
chatMessage.value = '';
|
chatMessage.value = '';
|
||||||
|
this.userLog('info', 'No participants in the room', 'top-end');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let peer_msg = this.formatMsg(chatMessage.value);
|
let peer_msg = this.formatMsg(chatMessage.value);
|
||||||
|
|||||||
@@ -252,6 +252,17 @@ io.on('connection', (socket) => {
|
|||||||
roomList.get(socket.room_id).broadCast(socket.id, 'updatePeerInfo', data);
|
roomList.get(socket.room_id).broadCast(socket.id, 'updatePeerInfo', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('wbCanvasToJson', (data) => {
|
||||||
|
// let objLength = bytesToSize(Object.keys(data).length);
|
||||||
|
// log.debug('Send Whiteboard canvas JSON', { length: objLength });
|
||||||
|
roomList.get(socket.room_id).broadCast(socket.id, 'wbCanvasToJson', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('whiteboardAction', (data) => {
|
||||||
|
log.debug('Whiteboard', data);
|
||||||
|
roomList.get(socket.room_id).broadCast(socket.id, 'whiteboardAction', data);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('join', (data, cb) => {
|
socket.on('join', (data, cb) => {
|
||||||
if (!roomList.has(socket.room_id)) {
|
if (!roomList.has(socket.room_id)) {
|
||||||
return cb({
|
return cb({
|
||||||
@@ -434,4 +445,11 @@ io.on('connection', (socket) => {
|
|||||||
roomList.get(socket.room_id) && roomList.get(socket.room_id).getPeers().get(socket.id).peer_info.peer_name
|
roomList.get(socket.room_id) && roomList.get(socket.room_id).getPeers().get(socket.id).peer_info.peer_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bytesToSize(bytes) {
|
||||||
|
let sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
if (bytes == 0) return '0 Byte';
|
||||||
|
let i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||||
|
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم