diff options
Diffstat (limited to 'resources/js/main.js')
| -rw-r--r-- | resources/js/main.js | 390 |
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); +} |
