Skip to content

Commit 21626fb

Browse files
committed
Initial commit
0 parents  commit 21626fb

File tree

277 files changed

+32440
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

277 files changed

+32440
-0
lines changed

chat-frontend.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
$(function () {
2+
"use strict";
3+
4+
// for better performance - to avoid searching in DOM
5+
var content = $('#content');
6+
var input = $('#input');
7+
var status = $('#status');
8+
9+
// my color assigned by the server
10+
var myColor = false;
11+
// my name sent to the server
12+
var myName = false;
13+
14+
// if user is running mozilla then use it's built-in WebSocket
15+
window.WebSocket = window.WebSocket || window.MozWebSocket;
16+
17+
// if browser doesn't support WebSocket, just show some notification and exit
18+
if (!window.WebSocket) {
19+
content.html($('<p>', { text: 'Sorry, but your browser doesn\'t '
20+
+ 'support WebSockets.'} ));
21+
input.hide();
22+
$('span').hide();
23+
return;
24+
}
25+
26+
// open connection
27+
var connection = new WebSocket('ws://127.0.0.1:1337');
28+
29+
connection.onopen = function () {
30+
// first we want users to enter their names
31+
input.removeAttr('disabled');
32+
status.text('Choose name:');
33+
};
34+
35+
connection.onerror = function (error) {
36+
// just in there were some problems with conenction...
37+
content.html($('<p>', { text: 'Sorry, but there\'s some problem with your '
38+
+ 'connection or the server is down.' } ));
39+
};
40+
41+
// most important part - incoming messages
42+
connection.onmessage = function (message) {
43+
// try to parse JSON message. Because we know that the server always returns
44+
// JSON this should work without any problem but we should make sure that
45+
// the massage is not chunked or otherwise damaged.
46+
try {
47+
var json = JSON.parse(message.data);
48+
} catch (e) {
49+
console.log('This doesn\'t look like a valid JSON: ', message.data);
50+
return;
51+
}
52+
53+
// NOTE: if you're not sure about the JSON structure
54+
// check the server source code above
55+
if (json.type === 'color') { // first response from the server with user's color
56+
myColor = json.data;
57+
status.text(myName + ': ').css('color', myColor);
58+
input.removeAttr('disabled').focus();
59+
// from now user can start sending messages
60+
} else if (json.type === 'history') { // entire message history
61+
// insert every single message to the chat window
62+
for (var i=0; i < json.data.length; i++) {
63+
addMessage(json.data[i].author, json.data[i].text,
64+
json.data[i].color, new Date(json.data[i].time));
65+
}
66+
} else if (json.type === 'message') { // it's a single message
67+
input.removeAttr('disabled'); // let the user write another message
68+
addMessage(json.data.author, json.data.text,
69+
json.data.color, new Date(json.data.time));
70+
} else {
71+
console.log('Hmm..., I\'ve never seen JSON like this: ', json);
72+
}
73+
};
74+
75+
/**
76+
* Send mesage when user presses Enter key
77+
*/
78+
input.keydown(function(e) {
79+
if (e.keyCode === 13) {
80+
var msg = $(this).val();
81+
if (!msg) {
82+
return;
83+
}
84+
// send the message as an ordinary text
85+
connection.send(msg);
86+
$(this).val('');
87+
// disable the input field to make the user wait until server
88+
// sends back response
89+
input.attr('disabled', 'disabled');
90+
91+
// we know that the first message sent from a user their name
92+
if (myName === false) {
93+
myName = msg;
94+
}
95+
}
96+
});
97+
98+
/**
99+
* This method is optional. If the server wasn't able to respond to the
100+
* in 3 seconds then show some error message to notify the user that
101+
* something is wrong.
102+
*/
103+
setInterval(function() {
104+
if (connection.readyState !== 1) {
105+
status.text('Error');
106+
input.attr('disabled', 'disabled').val('Unable to comminucate '
107+
+ 'with the WebSocket server.');
108+
}
109+
}, 3000);
110+
111+
/**
112+
* Add message to the chat window
113+
*/
114+
function addMessage(author, message, color, dt) {
115+
content.prepend('<p><span style="color:' + color + '">' + author + '</span> @ ' +
116+
+ (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
117+
+ (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
118+
+ ': ' + message + '</p>');
119+
}
120+
});

chat-server.js

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
2+
"use strict";
3+
4+
// Optional. You will see this name in eg. 'ps' or 'top' command
5+
process.title = 'node-chat';
6+
7+
// Port where we'll run the websocket server
8+
var webSocketsServerPort = 1337;
9+
10+
// websocket and http servers
11+
var webSocketServer = require('websocket').server;
12+
var http = require('http');
13+
14+
/**
15+
* Global variables
16+
*/
17+
// latest 100 messages
18+
var history = [ ];
19+
// list of currently connected clients (users)
20+
var clients = [ ];
21+
var finalhandler = require('finalhandler');
22+
var serveStatic = require('serve-static');
23+
24+
var serve = serveStatic("./");
25+
26+
/**
27+
* Helper function for escaping input strings
28+
*/
29+
function htmlEntities(str) {
30+
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;')
31+
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
32+
}
33+
34+
// Array with some colors
35+
var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
36+
// ... in random order
37+
colors.sort(function(a,b) { return Math.random() > 0.5; } );
38+
39+
/**
40+
* HTTP server
41+
*/
42+
var server = http.createServer(function(request, response) {
43+
// Not important for us. We're writing WebSocket server, not HTTP server
44+
var done = finalhandler(request, response);
45+
serve(request, response, done);
46+
});
47+
server.listen(webSocketsServerPort, function() {
48+
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
49+
});
50+
51+
/**
52+
* WebSocket server
53+
*/
54+
var wsServer = new webSocketServer({
55+
// WebSocket server is tied to a HTTP server. WebSocket request is just
56+
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
57+
httpServer: server
58+
});
59+
60+
// This callback function is called every time someone
61+
// tries to connect to the WebSocket server
62+
wsServer.on('request', function(request) {
63+
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
64+
65+
// accept connection - you should check 'request.origin' to make sure that
66+
// client is connecting from your website
67+
// (http://en.wikipedia.org/wiki/Same_origin_policy)
68+
var connection = request.accept(null, request.origin);
69+
// we need to know client index to remove them on 'close' event
70+
var index = clients.push(connection) - 1;
71+
var userName = false;
72+
var userColor = false;
73+
74+
console.log((new Date()) + ' Connection accepted.');
75+
76+
// send back chat history
77+
if (history.length > 0) {
78+
connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
79+
}
80+
81+
// user sent some message
82+
connection.on('message', function(message) {
83+
if (message.type === 'utf8') { // accept only text
84+
if (userName === false) { // first message sent by user is their name
85+
// remember user name
86+
userName = htmlEntities(message.utf8Data);
87+
// get random color and send it back to the user
88+
userColor = colors.shift();
89+
connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
90+
console.log((new Date()) + ' User is known as: ' + userName
91+
+ ' with ' + userColor + ' color.');
92+
93+
} else { // log and broadcast the message
94+
console.log((new Date()) + ' Received Message from '
95+
+ userName + ': ' + message.utf8Data);
96+
97+
// we want to keep history of all sent messages
98+
var obj = {
99+
time: (new Date()).getTime(),
100+
text: htmlEntities(message.utf8Data),
101+
author: userName,
102+
color: userColor
103+
};
104+
history.push(obj);
105+
history = history.slice(-100);
106+
107+
// broadcast message to all connected clients
108+
var json = JSON.stringify({ type:'message', data: obj });
109+
for (var i=0; i < clients.length; i++) {
110+
clients[i].sendUTF(json);
111+
}
112+
}
113+
}
114+
});
115+
116+
// user disconnected
117+
connection.on('close', function(connection) {
118+
if (userName !== false && userColor !== false) {
119+
console.log((new Date()) + " Peer "
120+
+ connection.remoteAddress + " disconnected.");
121+
// remove user from the list of connected clients
122+
clients.splice(index, 1);
123+
// push back user's color to be reused by another user
124+
colors.push(userColor);
125+
}
126+
});
127+
128+
});

frontend.html

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>WebSockets - Simple chat</title>
6+
7+
<style>
8+
* { font-family:tahoma; font-size:12px; padding:0px; margin:0px; }
9+
p { line-height:18px; }
10+
div { width:500px; margin-left:auto; margin-right:auto;}
11+
#content { padding:5px; background:#ddd; border-radius:5px; overflow-y: scroll;
12+
border:1px solid #CCC; margin-top:10px; height: 160px; }
13+
#input { border-radius:2px; border:1px solid #ccc;
14+
margin-top:10px; padding:5px; width:400px; }
15+
#status { width:88px; display:block; float:left; margin-top:15px; }
16+
</style>
17+
</head>
18+
<body>
19+
<div id="content"></div>
20+
<div>
21+
<span id="status">Connecting...</span>
22+
<input type="text" id="input" disabled="disabled" />
23+
</div>
24+
25+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
26+
<script src="./chat-frontend.js"></script>
27+
</body>
28+
</html>

node_modules/.bin/mime

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/debug/.jshintrc

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/debug/.npmignore

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)