Initial commit of not working messaging app with working drawing canvas. This is the work that was completed during the 7 hour stint at Hack and Tell.

This commit is contained in:
Justin Russo 2018-04-21 16:06:37 -05:00
commit 724a3b77ff
5 changed files with 335 additions and 0 deletions

91
app.js Normal file
View file

@ -0,0 +1,91 @@
"use strict";
process.title = 'pictonode';
const port = process.env.PORT || 8080;
const historyLength = 100;
const WebSocketServer = require('websocket').server;
const http = require('http');
const history = [];
const clients = [];
function htmlEntities(str) {
return String(str)
.replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function getRandomColor(name) {
let color = '#';
for (let i = 0; i < 6; i++) {
color += name[Math.floor(Math.random() * 16)];
}
return color;
}
const server = http.createServer((req, res) => {
// empty because we are using WebSockets
}).listen(port, 'localhost');
console.log((new Date()) + ` | App server running on port ${port}...`);
const wsServer = new WebSocketServer({
httpServer: server
});
wsServer.on('request', (req) => {
console.log((new Date()) + ` | Connection from: ${req.origin}.`);
const connection = req.accept(null, req.origin);
let index = clients.push(connection) - 1;
let userName = false;
let userColor = false;
console.log((new Date()) + ' | Connection accepted.');
if ( history.length > 0 ) {
connection.sendUTF(
JSON.stringify( { type: 'history', data: history } )
);
}
connection.on('message', (message) => {
if ( message.type === 'utf8') {
if ( userName === false ) {
userName = htmlEntities(message.utf8Data);
userColor = getRandomColor(userName);
connection.sendUTF(
JSON.stringify({ type: 'color', data: userColor })
);
console.log((new Date()) +
` | User is known as ${userName} with ${userColor} color`);
} else {
console.log((new Date()) +
` | Received message from ${userName}: ${message.utf8Data}`);
let obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
};
history.push(obj);
history.slice(historyLength * -1);
const json = JSON.stringify({ type:'message', data: obj });
for ( let i = 0 ; i < clients.length; i++ ) {
clients[i].sendUTF(json);
}
}
}
});
connection.on('close', (conn) => {
if ( userName !== false && userColor !== false ) {
console.log((new Date()) +
` | Peer ${conn.remoteAddress} disconnected.`);
clients.splice(index, 1);
}
});
});

51
css/layout.css Normal file
View file

@ -0,0 +1,51 @@
* {
font-family: tahoma, sans-serif;
font-size:12px;
padding:0;
margin:0;
}
p { line-height:18px; }
div {
width:500px;
margin-left:auto;
margin-right:auto;
}
#content {
padding:5px;
background:#ddd;
border-radius:5px;
overflow-y: scroll;
border:1px solid #CCC;
margin-top:10px;
height: 160px;
}
#input {
border-radius:2px;
border:1px solid #ccc;
margin-top:10px;
padding:5px;
width:400px;
}
#status {
width:88px;
display:block;
float:left;
margin-top:15px;
}
#settings {
display: block;
text-align:center
}
#paint {
border: 1px solid black;
background : #333333;
margin-left: auto;
margin-right: auto;
display: block;
}

32
html/index.html Normal file
View file

@ -0,0 +1,32 @@
<html>
<head>
<meta charset="utf-8">
<title>PictoNode - Picture Messaging powered by Node.js</title>
<link rel="stylesheet" href="../css/layout.css" type="text/css">
</head>
<body>
<div id="content"></div>
<div>
<span id="status">Connecting...</span>
<label for="input">Message text</label><input type="text" id="input" disabled="disabled" />
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="sketch">
<canvas id="paint"></canvas>
</div>
<div> Brush Size
<button onclick="getSize('2');">Small</button>
<button onclick="getSize('5');">Med</button>
<button onclick="getSize('10');">Large</button>
<button onclick="getSize('20');">X Large</button>
<label for="myRange">Brush Size</label>
<input type="range" min="2" max="20" value="5" class="slider" id="myRange">
</div>
<script type="text/javascript" src="../javascript/frontend.js"></script>
</body>
</html>

136
javascript/frontend.js Normal file
View file

@ -0,0 +1,136 @@
$(function () {
"use strict";
let content = document.getElementById('content');
let input = document.getElementById('input');
let status = document.getElementById('status');
let myColor = false;
let myName = false;
window.WebSocket = window.WebSocket || window.MozWebSocket;
if (!window.WebSocket) {
content.html($('<p>',
{ text:'Sorry, but your browser doesn\'t support WebSocket.'}
));
input.hide();
$('span').hide();
return;
}
let connection = new WebSocket('ws://127.0.0.1:8080');
connection.onopen = () => {
input.removeAttribute('disabled');
status.text('Please enter a username:')
};
connection.onerror = ( error ) => {
content.html($('<p>', {
text: 'Sorry, but there\'s some problem with your '
+ 'connection or the server is down. Please, try again later. Error message: '
+ error
}));
};
connection.onmessage = ( message ) => {
let json = false;
try {
json = JSON.parse(message.data);
} catch ( e ) {
console.log(new Date() +
` | Invalid JSON: ${message.data}`);
return;
}
if (json.type === 'color') {
myColor = json.data;
status.text(`${myName}: `).cssText('color', myColor);
input.removeAttribute('disabled').focus();
// from now user can start sending messages
} else if (json.type === 'history') {
for ( let i = 0; i < json.data.length; i++ ) {
addMessage(json.data[i].author, json.data[i].text,
json.data[i].color, new Date(json.data[i].time));
}
} else if (json.type === 'message') {
input.removeAttribute('disabled');
addMessage(json.data.author, json.data.text,
json.data.color, new Date(json.data.time));
} else {
console.log('Hmm..., I\'ve never seen JSON like this:', json);
}
};
input.onkeydown = (e) => {
if (e.code === 13) {
let msg = $(this).val();
if (!msg) {
return;
}
connection.send(msg);
$(this).val('');
input.setAttribute('disabled', 'disabled');
if (myName === false) {
myName = msg;
}
}
};
setInterval(function() {
if (connection.readyState !== 1) {
status.text('Error');
input.setAttribute('disabled', 'disabled').val(
'Unable to communicate with the WebSocket server.');
}
}, 3000);
function addMessage(author, message, color, dt) {
content.append('<p><span style="color:' + color + '">'
+ author + '</span> @ ' + (dt.getHours() < 10 ? '0'
+ dt.getHours() : dt.getHours()) + ':'
+ (dt.getMinutes() < 10
? '0' + dt.getMinutes() : dt.getMinutes())
+ ': ' + message + '</p>');
}
const canvas = document.getElementById('paint');
const ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 250;
let mouse = {x: 0, y: 0};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
/* Drawing on Paint App */
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = myColor;
let slider = document.getElementById("myRange");
ctx.lineWidth = slider.value;
//function getSize(size){ctx.lineWidth = size;}
canvas.addEventListener('mousedown', () => {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', () => {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
const onPaint = () => {
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
};
});

25
package.json Normal file
View file

@ -0,0 +1,25 @@
{
"name": "pictonode",
"version": "1.0.0",
"description": "A PictoChat implementation in Node.js",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rustinjusso/pictonode.git"
},
"author": "Justin Russo",
"license": "ISC",
"bugs": {
"url": "https://github.com/rustinjusso/pictonode/issues"
},
"homepage": "https://github.com/rustinjusso/pictonode#readme",
"dependencies": {
"websocket": "^1.0.25"
},
"devDependencies": {
"nodemon": "^1.17.3"
}
}