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:
parent
2f2eddc564
commit
724a3b77ff
5 changed files with 335 additions and 0 deletions
91
app.js
Normal file
91
app.js
Normal 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, '&').replace(/</g, '<')
|
||||
.replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
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
51
css/layout.css
Normal 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
32
html/index.html
Normal 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
136
javascript/frontend.js
Normal 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
25
package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue