summaryrefslogtreecommitdiff
path: root/resources/js/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/js/main.js')
-rw-r--r--resources/js/main.js390
1 files changed, 390 insertions, 0 deletions
diff --git a/resources/js/main.js b/resources/js/main.js
new file mode 100644
index 0000000..ed5956e
--- /dev/null
+++ b/resources/js/main.js
@@ -0,0 +1,390 @@
+import { AppSocket } from './lib.js';
+
+
+//global constants and variables
+const CHUNK_SIZE = 4 * 1024 * 1024; // 4MB chunks
+const FILESIZE_THRESHOLD = 16 * 1024 * 1024; // 16MB // how big before use chunked upload
+const MAXFILESIZE = 2048 * 1024 * 1024; // 2GB
+const color_bg = "#aaaaaa";
+//TODO change color based on time
+
+//
+let ctx = null; //canvas context
+let cnv = null; //canvas
+let domElems = {}; //references to all the dynamic DOM elements of this website
+let env = {
+ 'WS_URL': 'wss://belthelziquor.com:9002' //default websocket server endpoint
+}; //environment variables from server. dev=true by default because if env not passed in not public/published instance
+let W = 800;
+let H = 600;
+let socket = null; //client's websocket
+let myID; //client's ID
+
+//each connected client. same format as server's model
+let friends = {};
+
+//todo move in appmodel
+let me = [0,0]; //my cursor position
+let myNickname = '';
+
+
+function prepareFileInput(){
+ let file_input = domElems.inputFile;
+ let files = file_input.files;
+ for (f in files) {
+ console.log(f);
+ }
+ //TODO validation
+ //domElems.buttonFileUpload.enabled = true;
+}
+
+//APP START HERE
+$(document).ready(async function() {
+
+ //the core loop of the client application
+ // 1. setup relationship with DOM and grab references to its elements
+ log('init DOM');
+ await initDOM();
+
+ log('getting env vars');
+ await getServerEnvVars();
+
+ log('connect ws');
+ //connectWebSocket();
+
+ //setup canvas stuff
+ if (cnv != null) {
+ ctx = cnv.getContext("2d");
+ W = window.outerWidth;
+ H = window.outerHeight;
+ cnv.width = W;
+ cnv.height = H;
+ ctx.fillStyle = domElems.body.style.backgroundColor || color_bg;
+ ctx.fillRect(0,0,W,H);
+
+ document.onmousemove = (e) => handleMouseMove(e);
+ setInterval(() => {
+ draw();
+ }, 20);
+ resizeCanvas();
+ } else {
+ console.error('Canvas element not found');
+ }
+ window.addEventListener('resize', resizeCanvas);
+
+ //TODO timer to start soft music after some time spent on site
+
+ //background color
+ /*setInterval(() => {
+ let now = new Date();
+ let h = now.getHours();
+ let m = now.getMinutes();
+ let s = now.getSeconds();
+ let ms = now.getMilliseconds();
+
+ let timeS = h * 3600 + m * 60 + s;
+ let timeMs = (timeS) * 1000 + ms;
+
+ let hue = 10; //
+ let saturation = 0; //
+ let lightness = 94+2*Math.sin(timeMs) + Math.random(); //
+ let color = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
+ domElems.body.style.backgroundColor = color;
+ log(`Background color: ${color}`, 0);
+ }, 100);*/
+});
+
+async function getServerEnvVars(){
+ await fetch('/env')
+ .then((res) => res.json())
+ .then((data) => {
+ env = data;
+ log(env);
+ }).catch((err) => {
+ log(err);
+ });
+}
+
+//initial setup such as hiding/showing certain things
+function initDOM(){
+ // temp disabled $('#fileupload')[0].hidden = false;
+ domElems.numConnected = $('#numFriendsConnected')[0];
+ //domElems.inputFile = $('#f')[0];
+ domElems.debugInfo = $('#infolog')[0];
+ if (domElems.debugInfo) {
+ domElems.debugInfo.style.display = env.MODE == 'local' ? 'block' : 'none';
+ }
+ domElems.fileUploadResult = $('#fileupload_result')[0];
+ //domElems.inputFile.onchange = prepareFileInput;
+ //domElems.buttonFileUpload = $('#button_fileupload')[0];
+ domElems.body = $('body')[0];
+ domElems.cnv = $('#bg')[0];
+ domElems.statusWS = $('#statusWS')[0];
+ if (domElems.statusWS){
+ domElems.statusWS.innerHTML = 'Connecting...';
+ domElems.statusWS.style.color = 'yellow';
+ }
+ cnv = domElems.cnv; //for convenience
+
+
+ //was considering moving this into hn.js which would be loaded as another "parellel module" that would also have initDOM() etc.
+ domElems.hackernews = $('#hackernews-feed')[0];
+
+ //domElems.buttonFileUpload.enabled = false;
+ //domElems.buttonFileUpload.onclick = uploadFile;
+ //domElems.inputWho = $('#input_who')[0];
+ //domElems.inputWho.onchange = ()=> { myNickname = domElems.inputWho.value; };
+
+}
+
+function resizeCanvas() {
+ if (cnv == null) return;
+ W = window.outerWidth;
+ H = window.outerHeight;
+ domElems.cnv.width = W;
+ domElems.cnv.height = H;
+}
+
+//todo move the old code from file stuff etc to other file so that this file can be just for the canvas+graphics+networkedgame part of the client app
+async function uploadFile() {
+ let files = domElems.inputFile.files;
+ domElems.buttonFileUpload.enabled = false;
+ let formData = new FormData();
+ console.log(files);
+ for (let i = 0; i < files.length; i++) {
+ formData.append('f[]', files[i], files[i].name);
+ }
+
+ fetch('/f', {method: 'POST', headers: {'X-CSRF-TOKEN': window.csrfToken}, body: formData})
+ .then((res) => res.json())
+ .then((data) => {
+ console.log(data);
+ domElems.fileUploadResult.innerHTML = 'File uploaded successfully<br>';
+ data.files.forEach((f) => {
+ domElems.fileUploadResult.innerHTML += `<a href = "f/${f.filename}">${f.filename}</a><br>`;
+ });
+ domElems.inputFile.value = null;
+ domElems.buttonFileUpload.enabled = true;
+ })
+ .catch((err) => {
+ console.log(err);
+ domElems.fileUploadResult.innerHTML = 'Error uploading file';
+ domElems.buttonFileUpload.enabled = true;
+ });
+}
+
+async function uploadFileChunked(f){
+ const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
+ const filename = file.name;
+
+ for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
+ const chunk = file.slice(chunkNumber * CHUNK_SIZE, (chunkNumber + 1) * CHUNK_SIZE);
+ const formData = new FormData();
+ formData.append('file', chunk);
+ formData.append('filename', filename);
+ formData.append('chunkNumber', chunkNumber);
+ formData.append('totalChunks', totalChunks);
+
+ try {
+ const response = await fetch(`${API_URL}/uploadfiles`, {
+ method: 'POST',
+ body: formData
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ updateProgress(chunkNumber + 1, totalChunks);
+
+ } catch (error) {
+ console.error('Error uploading chunk:', error);
+ alert('Error uploading file. Please try again.');
+ return;
+ }
+ }
+}
+
+async function uploadFileSingle(f){
+ const formData = new FormData();
+ formData.append('f', f);
+ const res = await fetch(`${API_URL}/uploadfiles`, {
+ method: 'POST',
+ body: formData,
+ credentials: 'include'
+ });
+ console.log(res);
+}
+
+function updateProgress(currentChunk, totalChunks) {
+ const progressPercentage = Math.round((currentChunk / totalChunks) * 100);
+ document.getElementById('progress').textContent = `${progressPercentage}% uploaded`;
+}
+
+
+function wsConnected(){
+
+ return (socket != null && socket.connected);
+}
+
+//websocket server source code: github.com:grothedev/websocket-template.git
+function connectWebSocket(){
+ try {
+ socket = new WebSocket(env['WS_URL']);
+ /*socket = io(env['WS_URL'], {
+
+ secure: true,
+ rejectUnauthorized: false
+ });*/
+ socket.onerror = (err)=>{
+ console.log('connection error');
+ console.log(err);
+ domElems.statusWS.innerHTML = 'Connection error';
+ domElems.statusWS.style.color = 'red';
+ //TODO $('').textContent = err;
+ }
+ socket.onclose = (err) => {
+ console.log('disconnected');
+ console.log(err);
+ domElems.statusWS.innerHTML = 'Disconnected';
+ domElems.statusWS.style.color = 'orange';
+ //TODO $('').textContent = err;
+ }
+ socket.onopen = (err) => {
+ log('connected');
+ domElems.statusWS.innerHTML = 'Connected';
+ domElems.statusWS.style.color = 'green';
+ if (err) console.log(err);
+ //TODO $('').textContent = 'Connected';
+ socket.pingTimeout = 1000;
+ socket.pingInterval = 500;
+ setInterval(() => {
+ send('update_pos', {pos: me, nick: myNickname});
+ }, 100);
+ }
+ /*socket.addEventListener("open", (ev)=>{
+ console.log("open");
+ console.log(ev);
+ });
+ socket.addEventListener("close", (ev)=>{
+ console.log("close");
+ console.log(ev);
+ });*/
+
+
+ socket.onmessage = (msg) => {
+ try {
+ // The data is in the .data property of the message event
+ const m = JSON.parse(msg.data);
+ switch (m.action) {
+ case 'init':
+ myID = m.data;
+ break;
+ case 'sync_data':
+ friends = {};
+ Object.keys(m.data).forEach((cid) => {
+ friends[cid] = m.data[cid];
+ });
+ domElems.numConnected.innerHTML = Object.keys(friends).length;
+ break;
+ default:
+ break;
+ }
+ } catch (error) {
+ console.error('Error parsing message:', error);
+ send('error', 'Invalid message format');
+ return;
+ }
+ }
+
+
+ //socket.on('syncCanvas', (data) => {
+ //TODO appShapes = data.shapes;
+ //TODO ClearBake();
+ //TODO DrawCanvas();
+ //TODO $('').textContent = 'Synced';
+ //});
+
+ return true;
+ } catch (err) {
+ console.log(`failed to connec WS ${env['WS_URL']}`)
+ console.error(err);
+ return false;
+ }
+}
+
+
+function draw() {
+ //draw background
+ ctx.fillStyle = color_bg;
+ ctx.fillRect(0,0,W,H);
+
+ //draw me
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(me[0], me[1], 4, 4);
+ if (myNickname != ''){
+ ctx.fillText(myNickname, me[0], me[1]);
+ }
+
+ //draw friends
+ Object.keys(friends).forEach((fid)=>{
+ ctx.fillStyle = "#050505";
+ let x = friends[fid].pos[0];
+ let y =friends[fid].pos[1];
+ ctx.fillRect(x, y, 6, 6);
+ if (friends[fid].nick != ''){
+ ctx.fillText(friends[fid].nick, x, y);
+ }
+ });
+
+ //drawScene(ctx);
+
+}
+
+function getMousePos(canvas, evt) {
+ let rect = canvas.getBoundingClientRect();
+ /*return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top
+ };*/
+ return [evt.clientX - rect.left, evt.clientY - rect.top];
+}
+
+
+function handleMouseMove(e) {
+ let eDoc, doc, body;
+ e = e || window.event; // IE-ism
+ // If pageX/Y aren't available and clientX/Y are,
+ // calculate pageX/Y - logic taken from jQuery.
+ // (This is to support old IE)
+ if (e.pageX == null && e.clientX != null) {
+ eventDoc = (e.target && e.target.ownerDocument) || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ e.pageX = e.clientX +
+ (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
+ (doc && doc.clientLeft || body && body.clientLeft || 0);
+ e.pageY = e.clientY +
+ (doc && doc.scrollTop || body && body.scrollTop || 0) -
+ (doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+ me = getMousePos(cnv, e);
+}
+
+function send(action, payload){
+ if (socket != null && socket.readyState === WebSocket.OPEN) {
+ try {
+ socket.send(JSON.stringify({action: action, data: payload}));
+ } catch (e) {
+ console.error('Error sending message:', e);
+ }
+ }
+}
+
+function log(msg, lvl=1){
+ if (domElems.debugInfo){
+ domElems.debugInfo.innerHTML = msg; //TODO running log + timestamp
+ }
+ console.log(msg);
+}