This repository has been archived on 2026-10-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
collavas-src/client-96wMDN-main.js
2025-03-05 15:58:01 -08:00

512 lines
16 KiB
JavaScript

const socket = io({
timeout: 500
});
const canvas = document.getElementById('drawingCanvas');
const canvasContainer = document.getElementById('canvasContainer');
const buttons = document.getElementById('buttons');
const tx = document.getElementById('x');
const ty = document.getElementById('y');
const context = canvas.getContext('2d');
const chat = document.querySelector('.chat-container');
const nameinput = document.getElementById('username');
const drawingEmitDelay = 500;
let lastDrawingEmitTime = 0;
let drawings = [];
let announcementIDs = [];
let currentDrawing = [];
let isDrawing = false;
let uiVisible = true;
let currentColor = '#000000';
let announceCount = 1;
let connectionTimer = null;
let connectionSeconds = 0;
let versionSet = false;
let ping = null;
let uuid = "";
context.imageSmoothingEnabled = true;
canvasContainer.style.transform = `scale(0.08)`;
function getCookie(name) {
const cookies = document.cookie.split(';').map(cookie => cookie.trim());
for (const cookie of cookies) {
const [cookieName, cookieValue] = cookie.split('=').map(part => decodeURIComponent(part));
if (cookieName === name) {
return cookieValue;
}
}
return null;
}
function setCookie(name, value, expire) {
const expirationDate = new Date();
expirationDate.setDate(expirationDate.getDate() + expire);
const cookieValue = encodeURIComponent(value) + (expire ? `; expires=${expirationDate.toUTCString()}` : '');
document.cookie = `${name}=${cookieValue}; path=/`;
}
function pinger() {
const startTime = Date.now();
const pingtext = document.getElementById('ping').querySelector('p');
socket.on('pong', () => {
const latency = Date.now() - startTime;
pingtext.textContent = 'Ping: ' + latency + 'ms';
document.getElementById('ping').style.opacity = 0.25;
});
socket.emit('ping');
}
ping = setInterval(pinger, 1000);
function startConnectionTimer() {
const messages = document.querySelector('.messages');
if(messages.querySelectorAll('[id="connection"]').length > 0) {
return;
}
clearInterval(ping);
const parentMessage = document.createElement('div');
parentMessage.setAttribute('class', 'message fadeup red');
parentMessage.setAttribute('id', 'connection');
parentMessage.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)";
parentMessage.style.width = "300px";
const message = document.createElement('p');
message.setAttribute('id', 'timeout');
message.setAttribute('style', 'white-space: nowrap;margin: 25px 50px 25px 50px');
message.textContent = "Server Connection Timeout: " + connectionSeconds.toFixed(1);
parentMessage.appendChild(message);
messages.appendChild(parentMessage);
canvas.style.opacity = 0;
buttons.style.opacity = 0;
canvas.classList.add('fadedown');
drawings = [];
currentDrawing = [];
draw();
connectionTimer = setInterval(() => {
connectionSeconds += 0.1;
document.getElementById('timeout').textContent = "Server Connection Timeout: " + connectionSeconds.toFixed(1);
}, 100);
}
function stopConnectionTimer() {
clearInterval(connectionTimer);
ping = setInterval(pinger, 1000);
connectionSeconds = 0;
const messages = document.querySelector('.messages');
messages.querySelectorAll('[id="connection"]').forEach((child) => {
child.classList.remove('fadeup');
child.classList.add('fadedown');
setTimeout(() => {
child.remove();
}, 500);
});
const parentMessage = document.createElement('div');
parentMessage.setAttribute('class', 'message fadeup green');
parentMessage.setAttribute('id', 'success');
parentMessage.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)";
parentMessage.setAttribute('style', "min-width: auto;");
const messagetext = document.createElement('p');
messagetext.setAttribute('id', 'timeout');
messagetext.setAttribute('style', 'white-space: nowrap;margin: 25px 50px 25px 50px');
messagetext.textContent = "Server connection has been restored";
parentMessage.appendChild(messagetext);
messages.appendChild(parentMessage);
canvas.style.opacity = 1;
buttons.style.opacity = 1;
canvas.classList.remove('fadedown');
canvas.classList.add('fadeup');
const message = document.getElementById('success');
setTimeout(() => {
message.classList.remove('fadeup');
message.classList.add('fadedown');
setTimeout(() => {
message.remove();
}, 500);
}, 2500);
}
socket.on('connect', () => {
if(connectionSeconds != 0) {
stopConnectionTimer();
}
});
socket.on('connect_error', () => {
startConnectionTimer();
});
socket.on('connect_timeout', () => {
startConnectionTimer();
});
function toggleOP() {
uiVisible = !uiVisible;
buttons.style.display = uiVisible ? '' : 'none';
document.getElementById('resetView').style.display = uiVisible ? '' : 'none';
document.getElementById('xy').style.display = uiVisible ? '' : 'none';
document.getElementById('connectedClients').style.display = uiVisible ? '' : 'none';
document.getElementById('version').style.display = uiVisible ? '' : 'none';
document.getElementById('ping').style.display = uiVisible ? '' : 'none';
document.getElementById('data').style.display = uiVisible ? '' : 'none';
chat.style.display = uiVisible ? 'flex' : 'none';
}
function resetView() {
canvasContainer.style.transform = `scale(0.08)`;
canvas.style.transform = `translate(0px, 0px)`;
zoomLevel = 0.08;
offsetX = 0;
offsetY = 0;
}
function changeColor(event) {
currentColor = event.target.value;
}
document.addEventListener('mouseup', () => {
isDrawing = false;
});
canvas.addEventListener('mouseleave', () => {
isDrawing = false;
});
canvas.addEventListener('mousedown', (event) => {
isDrawing = true;
addPoint(event);
});
canvas.addEventListener('mouseup', () => {
isDrawing = false;
if (currentDrawing.length > 0) {
socket.emit('draw', { type: 'draw', drawing: currentDrawing, valid: uuid });
drawings.push(currentDrawing);
draw();
currentDrawing = [];
lastDrawingEmitTime = Date.now();
}
});
canvas.addEventListener('mousemove', (event) => {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = Math.round((event.clientX - rect.left) * scaleX);
const y = Math.round((event.clientY - rect.top) * scaleY);
tx.innerHTML = x;
ty.innerHTML = y;
if (isDrawing) {
addPoint(event);
const currentTime = Date.now();
if (currentTime - lastDrawingEmitTime >= drawingEmitDelay) {
//socket.emit('draw', { type: 'draw', drawing: currentDrawing, valid: uuid });
lastDrawingEmitTime = currentTime;
}
}
});
document.getElementById('chat-input').addEventListener('keydown', function(event) {
if(event.keyCode == 13) {
socket.emit('message', { uuid: uuid, content: document.getElementById('chat-input').value });
document.getElementById('chat-input').value = "";
}
});
nameinput.addEventListener('keydown', function(event) {
if(event.keyCode == 13) {
socket.emit('changeName', { uuid: uuid, name: nameinput.value });
}
});
document.addEventListener('keydown', function(event) {
if (event.key === 'ArrowUp') {
pan('up');
} else if (event.key === 'ArrowDown') {
pan('down');
} else if (event.key === 'ArrowLeft') {
pan('left');
} else if (event.key === 'ArrowRight') {
pan('right');
}
});
function addPoint(event) {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (event.clientX - rect.left) * scaleX;
const y = (event.clientY - rect.top) * scaleY;
currentDrawing.push({ x: Math.round(x), y: Math.round(y), color: currentColor });
draw();
}
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (const drawing of drawings) {
if (drawing.length < 2) continue;
for (let i = 1; i < drawing.length; i++) {
const startPoint = drawing[i - 1];
const endPoint = drawing[i];
context.beginPath();
context.strokeStyle = startPoint.color;
context.moveTo(Math.round(startPoint.x), Math.round(startPoint.y));
context.lineTo(Math.round(endPoint.x), Math.round(endPoint.y));
context.stroke();
}
}
context.stroke();
if (currentDrawing.length > 0) {
context.beginPath();
context.strokeStyle = currentColor;
context.moveTo(Math.round(currentDrawing[0].x), Math.round(currentDrawing[0].y));
for (let i = 1; i < currentDrawing.length; i++) {
const point = currentDrawing[i];
context.lineTo(Math.round(point.x), Math.round(point.y));
}
context.stroke();
}
}
socket.on('changeName', (data) => {
document.getElementById('nameStatus').textContent = data.status.message;
document.getElementById('nameStatus').style.display = "";
setTimeout(() => {
document.getElementById('nameStatus').style.display = "none";
}, 5000);
});
socket.on('chat', (data) => {
if(data.type == "message") {
const message = document.createElement('li');
message.textContent = "<" + data.name + "> " + data.content;
chat.querySelector('ul').appendChild(message);
chat.querySelector('.chat-messages').querySelector('ul').scrollTop = chat.querySelector('.chat-messages').querySelector('ul').scrollHeight;
} else if(data.type == "existingMessages") {
const messages = JSON.parse(data.messages);
chat.querySelector('ul').innerHTML = "";
messages.forEach(entry => {
const message = document.createElement('li');
message.textContent = "<" + entry.name + "> " + entry.content;
chat.querySelector('ul').appendChild(message);
chat.querySelector('.chat-messages').querySelector('ul').scrollTop = chat.querySelector('.chat-messages').querySelector('ul').scrollHeight;
});
}
});
socket.on('dataSize', (data) => {
document.getElementById('data').querySelector('p').textContent = "Size: " + data.size + "MB";
document.getElementById('data').style.opacity = 0.25;
});
socket.on('play', (data) => {
if(data.audio != "stop") {
document.getElementById('audio-player').src = data.audio;
document.getElementById('audio-player').play();
} else {
document.getElementById('audio-player').pause();
}
});
socket.on('announcement', (data) => {
if (data.message && data.color) {
const announcementMessage = data.message;
const messages = document.querySelector('.messages');
const announcement = document.createElement('div');
announcement.setAttribute('class', 'message fadeup ' + data.color);
announcement.setAttribute('id', announceCount);
announcement.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)";
const message = document.createElement('p');
message.setAttribute('style', 'white-space: normal;margin: 25px 50px 25px 50px');
message.textContent = announcementMessage;
announcement.appendChild(message);
messages.appendChild(announcement);
announceCount++;
announcementIDs.push(announceCount - 1);
setTimeout(() => {
const el = document.getElementById(announceCount - 1);
el.classList.remove('fadeup');
el.classList.add('fadedown');
setTimeout(() => {
el.style.display = "none";
el.remove();
}, 500);
}, 5000);
}
});
function hideMessages() {
const parentDiv = document.querySelector('.messages');
if (parentDiv) {
const childDivs = parentDiv.querySelectorAll('div');
if(childDivs.length > 1) {
childDivs.forEach((child) => {
if (child.getAttribute('id') != "connection") {
child.classList.add('fadedown');
}
});
setTimeout(() => {
childDivs.forEach((child) => {
if (child.getAttribute('id') != "connection") {
child.style.display = 'none';
child.remove();
}
});
}, 500);
}
}
}
setInterval(hideMessages, 5000);
socket.on('connectedClients', (count) => {
document.getElementById('clientNum').textContent = `Connected Clients: ${count}`;
document.getElementById('connectedClients').style.opacity = 0.25;
});
socket.on('reload', (count) => {
location.reload(true);
});
socket.on('version', (version) => {
document.title = "Collavas v" + version;
document.getElementById('version').querySelector('p').textContent = "Version: " + version;
document.getElementById('version').style.opacity = 0.25;
versionSet = true;
document.getElementById('w-version').textContent = "Collavas v" + version;
});
socket.on('draw', (data) => {
if (data.type === 'draw' && data.drawing) {
drawings.push(data.drawing);
draw();
} else if (data.type === 'existingDrawings' && data.drawings) {
const parsedDrawings = JSON.parse(data.drawings);
const messages = JSON.parse(data.chat);
drawings = [];
chat.querySelector('ul').innerHTML = "";
messages.forEach(entry => {
const message = document.createElement('li');
message.textContent = "<" + entry.name + "> " + entry.content;
chat.querySelector('ul').appendChild(message);
chat.querySelector('.chat-messages').querySelector('ul').scrollTop = chat.querySelector('.chat-messages').querySelector('ul').scrollHeight;
});
uuid = data.id;
nameinput.value = data.name;
setCookie("uuid", uuid, 1);
drawings.push(...parsedDrawings);
draw();
document.getElementById('transition').style.opacity = 1;
document.getElementById('transition').style.visibility = "hidden";
canvas.style.opacity = 1;
canvas.style.visibility = "visible";
buttons.style.opacity = 1;
chat.style.display = "flex";
if(!getCookie("visited")) {
var welcome = document.getElementById('welcome');
welcome.style.display = "flex";
welcome.style.opacity = 1;
welcome.querySelector('div').classList.add('fadeup');
setCookie("visited", "true", 1);
document.getElementById('w-close').addEventListener('click', (event) => {
welcome.style.opacity = 0;
welcome.querySelector('div').classList.remove('fadeup');
welcome.querySelector('div').classList.add('fadedown');
setTimeout(() => {
welcome.style.display = "none";
}, 500);
});
}
}
});
let zoomLevel = 0.08;
const zoomFactor = 0.1;
let offsetX = 0;
let offsetY = 0;
function pan(direction) {
const panSpeed = 40;
switch (direction) {
case 'left':
offsetX += panSpeed;
break;
case 'right':
offsetX -= panSpeed;
break;
case 'up':
offsetY += panSpeed;
break;
case 'down':
offsetY -= panSpeed;
break;
default:
break;
}
updateCanvasContainerPosition();
}
function updateCanvasContainerPosition() {
if(zoomLevel == 0.08) {
canvas.style.transform = `0px, 0px)`;
offsetX = 0;
offsetY = 0;
} else {
canvas.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
}
}
canvasContainer.addEventListener('wheel', (event) => {
event.preventDefault();
const scrollAmount = event.deltaY > 0 ? -1 : 1;
const scaleFactor = 1 + scrollAmount * zoomFactor;
zoomLevel *= scaleFactor;
if (zoomLevel < 0.08) { zoomLevel = 0.08; canvas.style.transform = `translate(0px, 0px)`; updateCanvasContainerPosition(); }
if (zoomLevel > 20.0) zoomLevel = 20.0;
canvasContainer.style.transform = `scale(${zoomLevel})`;
});