Permalink
Browse files

Unicorns.

  • Loading branch information...
1 parent e08742e commit 8daa46698620f21182e5692f3f3b503b21e74ec6 @ringmaster committed Mar 19, 2012
Showing with 254 additions and 78 deletions.
  1. +61 −6 barchat.js
  2. +50 −1 htdocs/css/barchat.css
  3. +78 −25 htdocs/js/barchat.obj.js
  4. +39 −37 htdocs/js/barchat.ui.js
  5. +2 −1 package.json
  6. +24 −8 views/index.html
View
67 barchat.js
@@ -4,6 +4,7 @@ var express = require('express')
, Schema = mongoose.Schema
, crypto = require('crypto')
, _ = require("underscore")
+ , moment = require('moment')
, ObjectId = require('mongoose').Types.ObjectId;
var app = module.exports = express.createServer();
@@ -89,8 +90,18 @@ var userFromToken = function(token, success, failure) {
success(doc);
}
});
+}
+var cleanSessions = function() {
+ console.log('Session cleanup');
+ var options = { multi: true, safe: false };
+ var dt = moment().subtract('m', 1);
+ Users.update({}, {$pull: {'sessions': {'ping': {$lt: new Date(dt)}}}}, options, function(err, doc) {
+ console.log(err);
+ });
}
+setInterval(cleanSessions, 60000);
+cleanSessions();
// Routes
@@ -111,12 +122,40 @@ app.post("/api/v1.0/getUserToken", function(req, res) {
doc.sessions.push(session)
doc.save();
res.cookie('session', session._id);
- res.json({'uid': doc._id, 'nickname': doc.nickname, 'token': session._id, 'session': session});
+ if(!doc.nickname) {
+ doc.nickname = doc.username;
+ }
+ cleanSessions();
+ res.json({'uid': doc._id, 'username': doc.username, 'nickname': doc.nickname, 'token': session._id, 'session': session});
})
}
});
});
+app.post("/api/v1.0/registerUser", function(req, res) {
+ Users.findOne({username: req.body.username}, function(err, doc){
+ if(doc != null) {
+ res.json({err: 1, errMsg: 'Username already exists'});
+ }
+ else {
+ user = new Users();
+ user.username = req.body.username;
+ user.nickname = req.body.username;
+ user.password = crypto.createHash('md5').update(req.body.password).digest("hex");
+ session = new Sessions();
+ session.ping = new Date();
+ // @todo DRY!
+ Rooms.find({'properties.default': 1}, function(err, rooms){
+ session.rooms = _.map(rooms, function(room){return room._id;});
+ user.sessions.push(session)
+ user.save();
+ res.cookie('session', session._id);
+ res.json({'uid': user._id, 'username': user.username, 'nickname': user.nickname, 'token': session._id, 'session': session});
+ });
+ }
+ });
+});
+
app.get("/api/v1.0/getSession", function(req, res) {
var session_id = req.cookies.session;
userFromToken(
@@ -184,11 +223,8 @@ app.post("/api/v1.0/sendMessage", function(req, res) {
userFromToken(
session_id,
function(user){
- console.log(user.username, user, session_id, user.sessions.id(session_id).ping, 'sendMessage', req.body.room);
+ console.log(user.username, session_id, user.sessions.id(session_id).ping, 'sendMessage', req.body.room);
message = new Messages();
-
-console.log(user.avatar, typeof(user.avatar));
-
message.user = {'username': user.username, 'avatar': user.avatar, 'user_id': user._id};
message.raw = req.body.raw,
message.msg = req.body.raw;
@@ -206,7 +242,26 @@ console.log(user.avatar, typeof(user.avatar));
});
app.get("/api/v1.0/partRoom", function(req, res) {});
-app.get("/api/v1.0/listUsers", function(req, res) {});
+app.get("/api/v1.0/getPresence", function(req, res) {
+ var session_id = req.cookies.session;
+ userFromToken(
+ session_id,
+ function(user){
+ console.log(user.username, session_id, user.sessions.id(session_id).ping, 'getPresence', req.query.room);
+ Users.find({'sessions.rooms': req.query.room}, function(err, docs){
+ docs = _.map(docs, function(doc){
+ doc.password = null;
+ return doc;
+ })
+ // @todo This data needs to be stored in aggregate in the database, and compared to send part/join messages
+ res.json(docs);
+ })
+ },
+ function(error){
+ res.json(error);
+ }
+ );
+});
app.get("/api/v1.0/registerUser", function(req, res) {});
View
51 htdocs/css/barchat.css
@@ -1,7 +1,7 @@
html, body {
height: 100%;
margin: 0px;
- font-family: arial;
+ font: normal 13px/100% Verdana, Tahoma, sans-serif;
}
header {
background-color: #e0e0e0;
@@ -93,6 +93,55 @@ header h1 {
text-decoration: none;
}
+#loginform {
+ margin: 80px 20px;
+}
+#loginform #server {
+ width: 220px;
+ margin-right: 4px;
+}
+#loginform input,
+#loginform select {
+ padding: 9px;
+ border: solid 1px #E5E5E5;
+ outline: 0;
+ width: 200px;
+ background: #FFFFFF;
+}
+#loginform select,
+#loginform select:hover,
+#loginform input:hover,
+#loginform input:focus {
+ border-color: #C9C9C9;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 8px;
+}
+#loginform label {
+ margin-left: 10px;
+ color: #999999;
+}
+#loginform input,
+#loginform select {
+ box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
+ -moz-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
+ -webkit-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
+ background: -webkit-gradient(linear, left top, left 25, from(#FFFFFF), color-stop(4%, #eeeeee), to(#FFFFFF));
+ background: -moz-linear-gradient(top, #FFFFFF, #eeeeee 1px, #FFFFFF 25px);
+}
+#loginform input[type="submit"] {
+ width: auto;
+ padding: 9px 15px;
+ background: #616161;
+ border: 0;
+ font-size: 14px;
+ color: #FFFFFF;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+}
+#loginform input[type="submit"]:hover,
+#loginform input[type="submit"]:focus {
+ background: #888888;
+}
+
.room .message {
clear: both;
margin: 20px 20px 0px;
View
103 htdocs/js/barchat.obj.js
@@ -1,49 +1,67 @@
Barchat = new Object();
Barchat.servers = {};
+Barchat.presencefn = null;
+Barchat.presenceInterval = null;
Barchat.Server = function(server) {
this.server_url = server;
this.token = false;
this.latest = 0;
}
-Barchat.Server.prototype.connect = function(username, password) {
+Barchat.Server.prototype.connect = function(username, password, register) {
var server = this;
if(server.token) {
$(server).trigger('error', ['Already connected!']);
return;
}
- $.post(
- server.server_url + '/getUserToken',
- {'username': username, 'password': password},
- function(response){
- if(response.err) {
- $(server).trigger('error', [response.errMsg]);
- }
- else {
- server.token = response.token;
- server.username = username;
-
- $.get(server.server_url +'/getMyRooms', {}, function(data){
- server.rooms = data;
- _.each(server.rooms, function(room) {
- $(server).trigger('join', [server.server_url, room]);
- });
- }, 'jsonp');
-
- server.pollHandle = window.setInterval(function(){server.getMessages();}, 2000);
- $(server).trigger('connected', [server.username + ' is now connected to ' + server.server_url, response]);
+ if(register) {
+ url = server.server_url + '/registerUser';
+ }
+ else {
+ url = server.server_url + '/getUserToken';
+ }
+ $.ajax({
+ url: url,
+ data: {'username': username, 'password': password},
+ type: 'POST',
+ dataType: 'jsonp',
+ success: function(response){
+ if(response.err) {
+ $(server).trigger('error', [response.errMsg]);
+ }
+ else {
+ server.doConnect(response);
+ }
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ $(server).trigger('error', [textStatus]);
}
- },
- 'jsonp'
- );
+ });
}
Barchat.Server.prototype.disconnect = function() {
window.clearInterval(server.pollHandle);
$(server).trigger('disconnected', [server.username + ' is now disconnected from ' + server.server]);
}
+Barchat.Server.prototype.doConnect = function(response) {
+ var server = this;
+ server.token = response.token;
+ server.username = response.username;
+ server.nickname = response.nickname;
+
+ $.get(server.server_url +'/getMyRooms', {}, function(data){
+ server.rooms = data;
+ _.each(server.rooms, function(room) {
+ $(server).trigger('join', [server.server_url, room]);
+ });
+ }, 'jsonp');
+
+ server.pollHandle = window.setInterval(function(){server.getMessages();}, 2000);
+ $(server).trigger('connected', [server.username + ' is now connected to ' + server.server_url, response]);
+}
+
Barchat.Server.prototype.getMessages = function() {
var server = this;
$.get(server.server_url + '/getMessages', {timestamp: server.latest}, function(data){
@@ -71,6 +89,16 @@ Barchat.Server.prototype.sendMessage = function(room, message) {
);
}
+Barchat.Server.prototype.getPresence = function(server_url, room) {
+ $.get(
+ server_url + '/getPresence',
+ {room: room},
+ function(data){
+ $(server).trigger('presence', [data]);
+ }
+ );
+}
+
Barchat.create = function(server, callback) {
if(this.servers[server] == undefined) {
this.servers[server] = new Barchat.Server(server);
@@ -86,4 +114,29 @@ Barchat.create = function(server, callback) {
Barchat.sendMessage = function(server, room, message) {
server = this.servers[server];
return server.sendMessage(room, message);
-}
+}
+
+Barchat.serverByToken = function(token) {
+ server = _.find(this.servers, function(server){
+ server.token == token;
+ });
+ return server;
+}
+
+Barchat.presenceQueue = function(server_url, room) {
+ server = this.servers[server_url];
+ if(!server) {
+ console.log('No server defined', server_url, this.servers);
+ return;
+ }
+ Barchat.presencefn = server.getPresence;
+ if(Barchat.presenceInterval) {
+ window.clearInterval(Barchat.presenceInterval);
+ }
+ Barchat.presenceInterval = window.setInterval(function(){
+ Barchat.presencefn(server_url, room);
+ }, 5000);
+ Barchat.presencefn(server_url, room);
+}
+
+
View
76 htdocs/js/barchat.ui.js
@@ -8,26 +8,26 @@ BarchatUI.removeRoom = function(room_id) {
$('.room[data-room="' + room_id + '"]').remove();
}
BarchatUI.msgRoom = function(msgdatas) {
- for(var i in msgdatas) {
- stage = $('.room[data-room="' + msgdatas[i].room + '"]');
- msgdate = new Date(msgdatas[i].timestamp);
- msgdatas[i].msgdate = msgdate.toString('ddd h:mmtt');
+ _.each(msgdatas, function(msgdata){
+ stage = $('.room[data-room="' + msgdata.room + '"]');
+ msgdate = new Date(msgdata.timestamp);
+ msgdata.msgdate = msgdate.toString('ddd h:mmtt');
var ip = BarchatUI.getInsertionPoint(stage, msgdate.getTime());
if(ip) {
- ip.before(ich.message(msgdatas[i]));
+ ip.before(ich.message(msgdata));
}
else {
lastuser = $('.message:last', stage).data('user');
- if(lastuser == msgdatas[i].user.user_id) {
- $('.message:last .message_texts', stage).append(ich.message_text(msgdatas[i]));
+ if(lastuser == msgdata.user.user_id) {
+ $('.message:last .message_texts', stage).append(ich.message_text(msgdata));
}
else {
- stage.append(ich.message(msgdatas[i]));
+ stage.append(ich.message(msgdata));
}
}
- }
+ })
}
// @todo still an issue with messages having the same millisecond timestamp?
BarchatUI.getInsertionPoint = function(stage, d) {
@@ -62,46 +62,48 @@ BarchatUI.getActiveRoom = function() {
room: $('#roomtabs li.active').data('room')
};
}
+BarchatUI.setActiveRoom = function(server_id, room_id) {
+ $('.room,#roomtabs li').removeClass('active');
+ $('.room[data-room="' + room_id + '"]').addClass('active');
+ $('#roomtabs li[data-room="' + room_id + '"]').addClass('active');
+ $('#statusbar').css('opacity', 0.4);
+ Barchat.presenceQueue(server_id, room_id);
+}
-function shuffle(o){ //v1.0, code based on Fisher-Yates
- for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
- return o;
-};
+BarchatUI.doLogin = function(register){
+ // @todo get an actual server label
+ server = Barchat.create($('#server').val(), function(server){
+ $(server).bind({
+ error: function(e, errMsg){humane.error(errMsg)},
+ connected: function(e, connMsg, response){humane.info(connMsg)},
+ message: function(e, messages){BarchatUI.msgRoom(messages);},
+ join: function(e, server, room){BarchatUI.addRoom(server, room._id, room.title);BarchatUI.setActiveRoom(server, room._id);},
+ presence: function(e, users){BarchatUI.showUsers(users);}
+ });
+ });
+ server.connect($('#username').val(), $('#password').val(), register);
+ return false;
+}
-$(function(){
- // Initialize user sample content
- $('#statusbar').append(ich.placard_group({name: 'users_online', title: 'online', placards: [{avatar: 'avatar.png', nickname: 'Bill Jennings'}]}));
- $('#statusbar').append(ich.placard_group({name: 'users_services', title: 'services', placards: [{avatar: 'github.png', nickname: 'GitHub'}]}));
- $('#statusbar').append(ich.placard_group({name: 'users_offline', title: 'offline', placards: [{avatar: 'avatar2.png', nickname: 'Sue Heron'}]}));
+BarchatUI.showUsers = function(users) {
+ $('#statusbar').html(ich.placard_group({name: 'users_inroom', title: 'inroom', placards: users}));
+ $('#statusbar').css('opacity', 1);
+}
+$(function(){
// textinput filtering
$('#textinput').bind('paste', function(e){
window.setTimeout(function(){
$('#textinput').html($('#textinput').text());
}, 0);
})
-
- $('#loginform').submit(function(){
- //var server = new Barchat.Server($('#server').val(), $('#username').val(), $('#password').val());
- // @todo get an actual server label
- server = Barchat.create($('#server').val(), function(server){
- $(server).bind({
- error: function(e, errMsg){humane.error(errMsg)},
- connected: function(e, connMsg, response){humane.info(connMsg)},
- message: function(e, messages){BarchatUI.msgRoom(messages);},
- join: function(e, server, room){BarchatUI.addRoom(server, room._id, room.title);}
- });
- });
- server.connect($('#username').val(), $('#password').val());
- return false;
- });
-
+ $('#login').click(function(){BarchatUI.doLogin();});
+ $('#register').click(function(){BarchatUI.doLogin(true);});
+ $('#loginform').submit(function(){return false;})
$('#roomtabs a').live('click', function(){
- $('.room,#roomtabs li').removeClass('active');
- $('.room[data-room="' + $(this).parents('li').data('room') + '"]').addClass('active');
- $(this).parents('li').addClass('active');
+ BarchatUI.setActiveRoom($(this).parents('li').data('server'), $(this).parents('li').data('room'));
return false;
});
View
3 package.json
@@ -14,7 +14,8 @@
"dependencies": {
"mongoose": "2.5.10",
"express": "2.5.8",
- "underscore": "1.3.1"
+ "underscore": "1.3.1",
+ "moment": "1.4.0"
},
"devDependencies": {}
}
View
32 views/index.html
@@ -19,7 +19,12 @@
{{#placards}}
<li class="user_placard">
<span class="status"></span>
+ {{#avatar}}
<img class="avatar" src="{{avatar}}" title="{{nickname}}">
+ {{/avatar}}
+ {{^avatar}}
+ <img class="avatar" src="http://unicornify.appspot.com/avatar/{{_id}}?s=45" title="{{nickname}}">
+ {{/avatar}}
</li>
{{/placards}}
<ul>
@@ -39,7 +44,12 @@
<div class="message" data-user="{{user.user_id}}">
{{#user.username}}
<div class="who">
+{{#user.avatar}}
<img src="{{user.avatar}}" class="avatar">
+{{/user.avatar}}
+ {{^user.avatar}}
+ <img class="avatar" src="http://unicornify.appspot.com/avatar/{{user.user_id}}?s=64" title="{{nickname}}">
+ {{/user.avatar}}
<span class="username">{{user.username}}</span>
</div>
{{/user.username}}
@@ -60,14 +70,20 @@
<div id="stage">
<div id="rooms">
<div id="connect" class="room active" data-room="connect">
- <form id="loginform" action="/api/v1.0" method="POST">
- <table>
- <tr><td><label for="server">Server:</label></td><td><select name="server" id="server">
- <option value="http://{{$host}}/api/v1.0">Lise</option>
- </select></td></tr>
- <tr><td><label for="username">Username:</label></td><td><input type="text" id="username" name="username"></td></tr>
- <tr><td><label for="password">Password:</label></td><td><input type="password" id="password" name="password"></td></tr>
- <tr><td></td><td><input type="submit" value="Log In"></td></tr>
+ <form id="loginform" action="" method="POST">
+ <p>
+ <select name="server" id="server">
+ <option value="http://{{$host}}/api/v1.0">{{$host}}</option>
+ </select><label for="server">Server</label>
+ <p>
+ <input type="text" id="username" name="username">
+ <label for="username">Username</label>
+ <p>
+ <input type="password" id="password" name="password">
+ <label for="password">Password</label>
+ <p>
+ <input type="submit" id="login" name="login" value="Log In">
+ <input type="submit" id="register" name="register" value="Register">
</table>
</form>
</div>

0 comments on commit 8daa466

Please sign in to comment.