tag:blogger.com,1999:blog-42252941035131123212024-03-06T06:08:26.949+02:00<The client side of the moon />JavaScript front-end, Node.js back-end and everything in betweenAnonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-4225294103513112321.post-6363599142269896052012-12-17T10:09:00.002+02:002012-12-27T16:01:36.881+02:00Overriding The Console Functions<span style="font-family: Trebuchet MS, sans-serif;"><span style="font-size: x-large;"><b>T</b></span>he console functions are the most common functions that developers can use when developing and debugging a web application using JavaScript. Actually, the `console.log` function is the one to use when trying to find an annoying bug. Yet, the console object includes other functions that can help us to trace and debug operations in our applications.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br /></span>
<span style="font-family: 'Trebuchet MS', sans-serif;">The `console.warn` function can be used when we want to show a message in the console, just like the `console.log` function but with a warning sign next to it.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br /></span>
<span style="font-family: 'Trebuchet MS', sans-serif;">The `console.error` can be used to console an object with a message (in a red color), along with the message we get the stack trace, showing us where the error came from.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br /></span>
<span style="font-family: 'Trebuchet MS', sans-serif;">Other cool functions are the `console.time` and `console.timeEnd`. These functions allow us to measure a time frame between operations in order to find and make our algorithm more efficient.</span><br />
<span style="font-family: 'Trebuchet MS', sans-serif;"><br /></span>
<span style="font-family: 'Trebuchet MS', sans-serif;">You can explore the `console` object in order to find out about more cool functions inside.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">In this post I will talk about how to override the console functions and preserved the original functionality. You asking why? Well, I'm glad you asked... :)</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span><span style="font-family: Trebuchet MS, sans-serif;">I'm currently working on a system that combines web and mobile clients, I will not go into details, but think about having a web application on a mobile device and you want to trace all the console.log's that comes from it, we all know that we can enable the console messages (known as 'developer mode') on the mobile side, however, it will be a bit annoying to read those messages on the mobile side. So, my goal is to catch every `console.log` call, send it to my Node.js/Socket.io server and transmit it to my desktop web application.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;">The simple way to achieve this task is to simply override the `console.log` function like so:</span><br />
<br />
<pre class="prettyprint lang-js linenums">console.log = function() {
//send data to server...
}</pre>
<br />
<span style="font-family: Trebuchet MS, sans-serif;">When using this process, I can send the data to my server every time `console.log` is called on the mobile web application. But what if I want to preserve the original action of the `console.log` function? I don't want to destroy the real function, just to add some functionality into it.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">This is the correct way to override and preserve the `console.log` functionality:</span><br />
<br />
<pre class="prettyprint lang-js linenums">(function(){
//saving the original console.log function
var preservedConsoleLog = console.log;
//overriding console.log function
console.log = function() {
//we can't just call to `preservedConsoleLog` function,
//that will throw an error (TypeError: Illegal invocation)
//because we need the function to be inside the
//scope of the `console` object so we going to use the
//`apply` function
preservedConsoleLog.apply(console, arguments);
//and lastly, my addition to the `console.log` function
if(application.socket){
application.socket.emit('console.log', arguments);
}
}
})();</pre>
<br />
<span style="font-family: Trebuchet MS, sans-serif;">Now you can take any function of the `console` object and override it in this way, add your functionality and achieve what you want.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>Anonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.com1tag:blogger.com,1999:blog-4225294103513112321.post-6741445125436031762012-11-18T11:58:00.001+02:002012-11-19T11:20:36.685+02:00Extending jQuery functions<span style="font-family: Trebuchet MS, sans-serif;"><span style="font-size: x-large;">W</span>hen developing with jQuery or any other JavaScript framework, sometimes we want to do more with the framework core functions. For example, many times, when I'm adding or removing a class to/from a jQuery element I want to call a function. If I'll call a function every time I'm adding or removing classes right after the addClass or removeClass it will be hard to maintain the code.</span><br />
<div>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="font-family: Trebuchet MS, sans-serif;">In this post I'll show how to expand jQuery core functions, as an example I will demonstrate how to add addClass and removeClass events to each jQuery element.</span></div>
<div>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="font-family: Trebuchet MS, sans-serif;">Let's start by creating a closure to avoid variable conflicts:</span></div>
<pre class="prettyprint lang-js linenums">(function(){
// code goes here...
})();
</pre>
<br />
<div>
<span style="font-family: Trebuchet MS, sans-serif;">
Now, before the core functions override, we need to save them in order to call them later inside the expanded function
</span></div>
<pre class="prettyprint lang-js linenums">(function(){
// saving the core functions
var coreAddClass = jQuery.fn.addClass;
var coreRemoveClass = jQuery.fn.removeClass;
})();
</pre>
<br />
<div>
<span style="font-family: Trebuchet MS, sans-serif;">
Core functions saved, now we can override them
</span></div>
<pre class="prettyprint lang-js linenums">(function(){
// saving the core functions
var coreAddClass = jQuery.fn.addClass;
var coreRemoveClass = jQuery.fn.removeClass;
// overriding the addClass function
jQuery.fn.addClass = function(){
}
// overriding the removeClass function
jQuery.fn.removeClass = function(){
}
})();
</pre>
<br />
<div>
<span style="font-family: Trebuchet MS, sans-serif;">
If you will run the code we wrote so far and try to $().addClass() or $().removeClass() nothing will happen. The next step is to add our new logic to the functions and then to call the core functions so we will keep the original behaviors</span></div>
<pre class="prettyprint lang-js linenums">(function(){
// saving the core functions
var coreAddClass = jQuery.fn.addClass;
var coreRemoveClass = jQuery.fn.removeClass;
// overriding the addClass function
jQuery.fn.addClass = function(){
// every time the addClass function is called
// for this $element, trigger the addClass event
$(this).trigger('addClass', arguments);
// calling the core function
coreAddClass.apply(this, arguments);
}
// overriding the removeClass function
jQuery.fn.removeClass = function(){
// every time the removeClass function is called
// for this $element, trigger the removeClass event
$(this).trigger('removeClass', arguments);
// calling the core function
coreRemoveClass.apply(this, arguments);
}
})();
</pre>
<br />
<div>
<span style="font-family: Trebuchet MS, sans-serif;">
By applying the core function at the end of the function we are able to keep the original behavior of the core.
<br /><br />
Simple client to test the code:
</span></div>
<pre class="prettyprint lang-js linenums"><!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.events.js"></script>
<script type="text/javascript">
$(function(){
// add event listener to the element addClass event
$('.cube').on('addClass', function(e, className){
console.log(e);
console.log(className);
});
// add event listener to the element removeClass event
$('.cube').on('removeClass', function(e, className){
console.log(e);
console.log(className);
});
$('button').on('click', function(){
// toggle a class on the element, the events are fired
$('.cube').toggleClass('color');
});
});
</script>
<style type="text/css">
.cube{width:100px;height:100px;background:yellow;}
.color{background:red !important;}
</style>
</head>
<body>
<div class="cube"></div>
<button>toggle red</button>
</body>
</html>
</pre>
Anonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.com1tag:blogger.com,1999:blog-4225294103513112321.post-1289078317806071612012-11-16T01:16:00.000+02:002012-12-13T17:49:34.577+02:00Chat Evolution — Node.js and Socket.IO<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;"><span style="font-size: x-large;">N</span>ot so long ago, if we wanted to build a simple JavaScript chat we had
to work with <a href="http://en.wikipedia.org/wiki/Ajax_(programming)" target="_blank">Ajax</a>, <a href="http://en.wikipedia.org/wiki/Comet_(programming)" target="_blank">Comet</a> or Flash component in the browser<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.<o:p></o:p></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span dir="RTL" lang="HE"><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">Ajax can be a little bad for performance, (in the case of chatting)
because it’s based on a request and response. We also needed to hold an
interval in the client which asked the server for new messages and this process
was repeated again and again<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span dir="RTL" lang="HE"><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">Comet (also known as reverse Ajax and long polling) is pretty much the
same. While using Ajax, we asked the server every time for messages, with Comet
we needed to open one connection and wait. This connection remained open in the
server for a while until the server received messages for us, then the server
responded with the messages and closed the opened connection. The client, when
receiving these messages opened a new connection and waited again. One
advantage of Comet is the reduction of the request amounts, but still, we
needed to open and close many of them for each session (in case the user is
active)<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span dir="RTL" lang="HE"><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">And lastly, a Flash component - in this case we placed a small flash
component in our page to exchange messages with the JavaScript client, the
flash could open a socket to the server and this gave us push communication<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">However, we are here to talk about <a href="http://nodejs.org/" target="_blank">Node.js</a> and <a href="http://socket.io/" target="_blank">Socket.IO</a>, so, basically,
the flash component is a good solution but in those days not every device or
browser came with flash support<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<br /></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<b><span style="font-family: Trebuchet MS, sans-serif;">Node.js and Socket.IO<o:p></o:p></span></b></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">With node.js and socket.io we can enjoy better performance, a
bidirectional push communication between a server and a JavaScript client and
even the support for all the browsers. Socket.io can work with several
transports in order to support even old browsers like IE6 (RiP<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>(.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span dir="RTL" lang="HE"><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">In this tutorial, I'll show you how to create a multi chat room server
with node.js and socket.io and a basic jquery client<span dir="RTL"></span><span dir="RTL" lang="HE"><span dir="RTL"></span>.</span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span dir="RTL" lang="HE"><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></span></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<b><span style="font-family: Trebuchet MS, sans-serif;">Installing Socket.IO and Express</span></b></div>
<div class="MsoNormal" dir="LTR" style="direction: ltr; unicode-bidi: embed;">
<span style="font-family: Trebuchet MS, sans-serif;">If you don’t have <a href="http://socket.io/" target="_blank">socket.io</a> and/or <a href="http://expressjs.com/" target="_blank">expressjs</a> node modules installed on your machine,
this will be a great time to do it, you can install them with <a href="https://npmjs.org/" target="_blank">npm</a>:</span></div>
<pre class="prettyprint lang-js">$ npm install express
</pre>
<span style="font-family: Trebuchet MS, sans-serif;">and...
</span><br />
<pre class="prettyprint lang-js">$ npm install socket.io
</pre>
<br />
<span style="font-family: Trebuchet MS, sans-serif;">Now we are ready to go, let's start with the server...</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>server.js</b></span>
<br />
<pre class="prettyprint lang-js linenums">// creating global parameters and start
// listening to 'port', we are creating an express
// server and then we are binding it with socket.io
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
port = 8080,
// hash object to save clients data,
// { socketid: { clientid, nickname }, socketid: { ... } }
chatClients = new Object();
// listening to port...
server.listen(port);
// configure express, since this server is
// also a web server, we need to define the
// paths to the static files
app.use("/styles", express.static(__dirname + '/public/styles'));
app.use("/scripts", express.static(__dirname + '/public/scripts'));
app.use("/images", express.static(__dirname + '/public/images'));
// serving the main applicaion file (index.html)
// when a client makes a request to the app root
// (http://localhost:8080/)
app.get('/', function (req, res) {
res.sendfile(__dirname + '/public/index.html');
});
// sets the log level of socket.io, with
// log level 2 we wont see all the heartbits
// of each socket but only the handshakes and
// disconnections
io.set('log level', 2);
// setting the transports by order, if some client
// is not supporting 'websockets' then the server will
// revert to 'xhr-polling' (like Comet/Long polling).
// for more configurations go to:
// <a href="https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO" target="_blank">https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO</a>
io.set('transports', [ 'websocket', 'xhr-polling' ]);
// socket.io events, each connection goes through here
// and each event is emited in the client.
// I created a function to handle each event
io.sockets.on('connection', function(socket){
// after connection, the client sends us the
// nickname through the connect event
socket.on('connect', function(data){
connect(socket, data);
});
// when a client sends a messgae, he emits
// this event, then the server forwards the
// message to other clients in the same room
socket.on('chatmessage', function(data){
chatmessage(socket, data);
});
// client subscribtion to a room
socket.on('subscribe', function(data){
subscribe(socket, data);
});
// client unsubscribtion from a room
socket.on('unsubscribe', function(data){
unsubscribe(socket, data);
});
// when a client calls the 'socket.close()'
// function or closes the browser, this event
// is built in socket.io so we actually dont
// need to fire it manually
socket.on('disconnect', function(){
disconnect(socket);
});
});
// create a client for the socket
function connect(socket, data){
//generate clientId
data.clientId = generateId();
// save the client to the hash object for
// quick access, we can save this data on
// the socket with 'socket.set(key, value)'
// but the only way to pull it back will be
// async
chatClients[socket.id] = data;
// now the client objtec is ready, update
// the client
socket.emit('ready', { clientId: data.clientId });
// auto subscribe the client to the 'lobby'
subscribe(socket, { room: 'lobby' });
// sends a list of all active rooms in the
// server
socket.emit('roomslist', { rooms: getRooms() });
}
// when a client disconnect, unsubscribe him from
// the rooms he subscribed to
function disconnect(socket){
// get a list of rooms for the client
var rooms = io.sockets.manager.roomClients[socket.id];
// unsubscribe from the rooms
for(var room in rooms){
if(room && rooms[room]){
unsubscribe(socket, { room: room.replace('/','') });
}
}
// client was unsubscribed from the rooms,
// now we can delete him from the hash object
delete chatClients[socket.id];
}
// receive chat message from a client and
// send it to the relevant room
function chatmessage(socket, data){
// by using 'socket.broadcast' we can send/emit
// a message/event to all other clients except
// the sender himself
socket.broadcast.to(data.room).emit('chatmessage', { client:
chatClients[socket.id], message: data.message, room: data.room });
}
// subscribe a client to a room
function subscribe(socket, data){
// get a list of all active rooms
var rooms = getRooms();
// check if this room is exist, if not, update all
// other clients about this new room
if(rooms.indexOf('/' + data.room) < 0){
socket.broadcast.emit('addroom', { room: data.room });
}
// subscribe the client to the room
socket.join(data.room);
// update all other clients about the online
// presence
updatePresence(data.room, socket, 'online');
// send to the client a list of all subscribed clients
// in this room
socket.emit('roomclients', { room: data.room, clients:
getClientsInRoom(socket.id, data.room) });
}
// unsubscribe a client from a room, this can be
// occured when a client disconnected from the server
// or he subscribed to another room
function unsubscribe(socket, data){
// update all other clients about the offline
// presence
updatePresence(data.room, socket, 'offline');
// remove the client from socket.io room
socket.leave(data.room);
// if this client was the only one in that room
// we are updating all clients about that the
// room is destroyed
if(!countClientsInRoom(data.room)){
// with 'io.sockets' we can contact all the
// clients that connected to the server
io.sockets.emit('removeroom', { room: data.room });
}
}
// 'io.sockets.manager.rooms' is an object that holds
// the active room names as a key, returning array of
// room names
function getRooms(){
return Object.keys(io.sockets.manager.rooms);
}
// get array of clients in a room
function getClientsInRoom(socketId, room){
// get array of socket ids in this room
var socketIds = io.sockets.manager.rooms['/' + room];
var clients = [];
if(socketIds && socketIds.length > 0){
socketsCount = socketIds.lenght;
// push every client to the result array
for(var i = 0, len = socketIds.length; i < len; i++){
// check if the socket is not the requesting
// socket
if(socketIds[i] != socketId){
clients.push(chatClients[socketIds[i]]);
}
}
}
return clients;
}
// get the amount of clients in aroom
function countClientsInRoom(room){
// 'io.sockets.manager.rooms' is an object that holds
// the active room names as a key and an array of
// all subscribed client socket ids
if(io.sockets.manager.rooms['/' + room]){
return io.sockets.manager.rooms['/' + room].length;
}
return 0;
}
// updating all other clients when a client goes
// online or offline.
function updatePresence(room, socket, state){
// socket.io may add a trailing '/' to the
// room name so we are clearing it
room = room.replace('/','');
// by using 'socket.broadcast' we can send/emit
// a message/event to all other clients except
// the sender himself
socket.broadcast.to(room).emit('presence', { client:
chatClients[socket.id], state: state, room: room });
}
// unique id generator
function generateId(){
var S4 = function () {
return (((1 + Math.random()) * 0x10000) |
0).toString(16).substring(1);
};
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" +
S4() + "-" + S4() + S4() + S4());
}
// show a message in console
console.log('Chat server is running and listening to port %d...', port);
</pre>
<br />
<span style="font-family: Trebuchet MS, sans-serif;">Don't be afraid of this long code, actually it's not so long if you removes the comment lines :), However, I won't explain it because it's well documented so if you missed something feel free to comment to this post and I will explain...<br /><br />Now let's see the client side, I will show here only the JavaScript part but if you want to download the full application, see the <a href="#references">references</a> at the bottom for a github link and a live demo url.<br /><br /><b>chat.io.js</b></span>
<br />
<pre class="prettyprint lang-js linenums">(function($){
// create global app parameters...
var NICK_MAX_LENGTH = 15,
ROOM_MAX_LENGTH = 10,
lockShakeAnimation = false,
socket = null,
clientId = null,
nickname = null,
// holds the current room we are in
currentRoom = null,
// server information
serverAddress = 'localhost',
serverDisplayName = 'Server',
serverDisplayColor = '#1c5380',
// some templates we going to use in the chat,
// like message row, client and room, this
// templates will be rendered with jQuery.tmpl
tmplt = {
room: [
'<li data-roomId="${room}">',
'<span class="icon"></span> ${room}',
'</li>'
].join(""),
client: [
'<li data-clientId="${clientId}" class="cf">',
'<div class="fl clientName">',
'<span class="icon"></span>',
'${nickname}',
'</div>',
'<div class="fr composing"></div>',
'</li>'
].join(""),
message: [
'<li class="cf">',
'<div class="fl sender">${sender}: </div>',
'<div class="fl text">${text}</div>',
'<div class="fr time">${time}</div>',
'</li>'
].join("")
};
// bind DOM elements like button clicks and keydown
function bindDOMEvents(){
$('.chat-input input').on('keydown', function(e){
var key = e.which || e.keyCode;
if(key == 13) { handleMessage(); }
});
$('.chat-submit button').on('click', function(){
handleMessage();
});
$('#nickname-popup .input input').on('keydown', function(e){
var key = e.which || e.keyCode;
if(key == 13) { handleNickname(); }
});
$('#nickname-popup .begin').on('click', function(){
handleNickname();
});
$('#addroom-popup .input input').on('keydown', function(e){
var key = e.which || e.keyCode;
if(key == 13) { createRoom(); }
});
$('#addroom-popup .create').on('click', function(){
createRoom();
});
$('.big-button-green.start').on('click', function(){
$('#nickname-popup .input input').val('');
Avgrund.show('#nickname-popup');
window.setTimeout(function(){
$('#nickname-popup .input input').focus();
},100);
});
$('.chat-rooms .title-button').on('click', function(){
$('#addroom-popup .input input').val('');
Avgrund.show('#addroom-popup');
window.setTimeout(function(){
$('#addroom-popup .input input').focus();
},100);
});
$('.chat-rooms ul').on('scroll', function(){
$('.chat-rooms ul li.selected').css('top',
$(this).scrollTop());
});
$('.chat-messages').on('scroll', function(){
var self = this;
window.setTimeout(function(){
if($(self).scrollTop() + $(self).height() <
$(self).find('ul').height()){
$(self).addClass('scroll');
} else {
$(self).removeClass('scroll');
}
}, 50);
});
$('.chat-rooms ul li').live('click', function(){
var room = $(this).attr('data-roomId');
if(room != currentRoom){
socket.emit('unsubscribe',
{ room: currentRoom });
socket.emit('subscribe', { room: room });
}
});
}
// bind socket.io event handlers
// this events fired in the server
function bindSocketEvents(){
// when the connection is made, the server emiting
// the 'connect' event
socket.on('connect', function(){
// firing back the connect event to the server
// and sending the nickname for the connected client
socket.emit('connect', { nickname: nickname });
});
// after the server created a client for us, the ready event
// is fired in the server with our clientId, now we can start
socket.on('ready', function(data){
// hiding the 'connecting...' message
$('.chat-shadow').animate({ 'opacity': 0 }, 200,
function(){
$(this).hide();
$('.chat input').focus();
});
// saving the clientId localy
clientId = data.clientId;
});
// after the initialize, the server sends a list of
// all the active rooms
socket.on('roomslist', function(data){
for(var i = 0, len = data.rooms.length; i < len; i++){
// in socket.io, their is always one default room
// without a name (empty string), every socket is
// automaticaly joined to this room, however, we
// don't want this room to be displayed in the
// rooms list
if(data.rooms[i] != ''){
addRoom(data.rooms[i], false);
}
}
});
// when someone sends a message, the sever push it to
// our client through this event with a relevant data
socket.on('chatmessage', function(data){
var nickname = data.client.nickname;
var message = data.message;
//display the message in the chat window
insertMessage(nickname, message, true, false, false);
});
// when we subscribes to a room, the server sends a list
// with the clients in this room
socket.on('roomclients', function(data){
// add the room name to the rooms list
addRoom(data.room, false);
// set the current room
setCurrentRoom(data.room);
// announce a welcome message
insertMessage(serverDisplayName,
'Welcome to the room: `' + data.room +
'`... enjoy!', true, false, true);
$('.chat-clients ul').empty();
// add the clients to the clients list
addClient({ nickname: nickname, clientId: clientId },
false, true);
for(var i = 0, len = data.clients.length; i < len; i++){
if(data.clients[i]){
addClient(data.clients[i], false);
}
}
// hide connecting to room message message
$('.chat-shadow').animate({ 'opacity': 0 }, 200,
function(){
$(this).hide();
$('.chat input').focus();
});
});
// if someone creates a room the server updates us
// about it
socket.on('addroom', function(data){
addRoom(data.room, true);
});
// if one of the room is empty from clients, the server,
// destroys it and updates us
socket.on('removeroom', function(data){
removeRoom(data.room, true);
});
// with this event the server tells us when a client
// is connected or disconnected to the current room
socket.on('presence', function(data){
if(data.state == 'online'){
addClient(data.client, true);
} else if(data.state == 'offline'){
removeClient(data.client, true);
}
});
}
// add a room to the rooms list, socket.io may add
// a trailing '/' to the name so we are clearing it
function addRoom(name, announce){
// clear the trailing '/'
name = name.replace('/','');
// check if the room is not already in the list
if($('.chat-rooms ul li[data-roomId="' + name + '"]').length == 0){
$.tmpl(tmplt.room, { room: name })
.appendTo('.chat-rooms ul');
// if announce is true, show a message about this room
if(announce){
insertMessage(serverDisplayName, 'The room `' + name + '`
created...', true, false, true);
}
}
}
// remove a room from the rooms list
function removeRoom(name, announce){
$('.chat-rooms ul li[data-roomId="' + name + '"]').remove();
// if announce is true, show a message about this room
if(announce){
insertMessage(serverDisplayName, 'The room `' + name +
'` destroyed...', true, false, true);
}
}
// add a client to the clients list
function addClient(client, announce, isMe){
var $html = $.tmpl(tmplt.client, client);
// if this is our client, mark him with color
if(isMe){
$html.addClass('me');
}
// if announce is true, show a message about this client
if(announce){
insertMessage(serverDisplayName, client.nickname +
' has joined the room...', true, false, true);
}
$html.appendTo('.chat-clients ul')
}
// remove a client from the clients list
function removeClient(client, announce){
$('.chat-clients ul li[data-clientId="' +
client.clientId + '"]').remove();
// if announce is true, show a message about this room
if(announce){
insertMessage(serverDisplayName, client.nickname +
' has left the room...', true, false, true);
}
}
// every client can create a new room, when creating one, the client
// is unsubscribed from the current room and then subscribed to the
// room he just created, if he trying to create a room with the same
// name like another room, then the server will subscribe the user
// to the existing room
function createRoom(){
var room = $('#addroom-popup .input input').val().trim();
if(room && room.length <= ROOM_MAX_LENGTH &&
room != currentRoom){
// show room creating message
$('.chat-shadow').show().find('.content')
.html('Creating room: ' + room + '...');
$('.chat-shadow').animate({ 'opacity': 1 }, 200);
// unsubscribe from the current room
socket.emit('unsubscribe', { room: currentRoom });
// create and subscribe to the new room
socket.emit('subscribe', { room: room });
Avgrund.hide();
} else {
shake('#addroom-popup', '#addroom-popup .input input',
'shake', 'yellow');
$('#addroom-popup .input input').val('');
}
}
// sets the current room when the client
// makes a subscription
function setCurrentRoom(room){
currentRoom = room;
$('.chat-rooms ul li.selected').removeClass('selected');
$('.chat-rooms ul li[data-roomId="' + room + '"]')
.addClass('selected');
}
// save the client nickname and start the chat by
// calling the 'connect()' function
function handleNickname(){
var nick = $('#nickname-popup .input input').val().trim();
if(nick && nick.length <= NICK_MAX_LENGTH){
nickname = nick;
Avgrund.hide();
connect();
} else {
shake('#nickname-popup',
'#nickname-popup .input input', 'shake', 'yellow');
$('#nickname-popup .input input').val('');
}
}
// handle the client messages
function handleMessage(){
var message = $('.chat-input input').val().trim();
if(message){
// send the message to the server with the room name
socket.emit('chatmessage', { message: message,
room: currentRoom });
// display the message in the chat window
insertMessage(nickname, message, true, true);
$('.chat-input input').val('');
} else {
shake('.chat', '.chat input', 'wobble', 'yellow');
}
}
// insert a message to the chat window, this function can be
// called with some flags
function insertMessage(sender, message, showTime, isMe, isServer){
var $html = $.tmpl(tmplt.message, {
sender: sender,
text: message,
time: showTime ? getTime() : ''
});
// if isMe is true, mark this message so we can
// know that this is our message in the chat window
if(isMe){
$html.addClass('marker');
}
// if isServer is true, mark this message as a server
// message
if(isServer){
$html.find('.sender')
.css('color', serverDisplayColor);
}
$html.appendTo('.chat-messages ul');
$('.chat-messages').animate({
scrollTop: $('.chat-messages ul').height() }, 100);
}
// return a short time format for the messages
function getTime(){
var date = new Date();
return (date.getHours() < 10 ? '0' + date.getHours().toString() :
date.getHours()) + ':' + (date.getMinutes() < 10 ? '0' +
date.getMinutes().toString() : date.getMinutes());
}
// just for animation
function shake(container, input, effect, bgColor){
if(!lockShakeAnimation){
lockShakeAnimation = true;
$(container).addClass(effect);
$(input).addClass(bgColor);
window.setTimeout(function(){
$(container).removeClass(effect);
$(input).removeClass(bgColor);
$(input).focus();
lockShakeAnimation = false;
}, 1500);
}
}
// after selecting a nickname we call this function
// in order to init the connection with the server
function connect(){
// show connecting message
$('.chat-shadow .content').html('Connecting...');
// creating the connection and saving the socket
socket = io.connect(serverAddress);
// now that we have the socket we can bind events to it
bindSocketEvents();
}
// on document ready, bind the DOM elements to events
$(function(){
bindDOMEvents();
});
})(jQuery);
</pre>
<br />
<span style="font-family: Trebuchet MS, sans-serif;"><b>
Summary</b>
<br />
There are some ways to send a message from the server to the client with Socket.io, Let's explore some of them:
<br />
</span><br />
<ul><span style="font-family: Trebuchet MS, sans-serif;">
<li><b>socket.emit</b> - Fire event to the client side which associated to this socket.
<pre>socket.emit('eventName', { data: 'tosend' });
</pre>
</li>
<li><b>socket.broadcast.emit</b> - Like the socket.emit, firing an event to the client but instead of firing the event to the associated socket, the event will be fired in all other connected socket except from the broadcasting one.
<pre>socket.broadcast.emit('eventName', { data: 'tosend'});
</pre>
</li>
<li><b>socket.broadcast.to('roomName').emit</b> - Sometimes we want to broadcast from one socket to all another socket in the same room rather than all the sockets in the server, in this way we can broadcast to all other sockets who joined a specific room
<pre>socket.broadcast.to('roomName').emit('eventName', {data: 'tosend'});
</pre>
</li>
<li><b>io.sockets.emit</b> - In this way we can fire an event to all the clients that connected to the server.
<pre>io.sockets.emit('eventName', { data: 'tosend'});
</pre>
</li>
<li><b>io.of('/namespaceName').emit</b> - Like rooms, in socket.io we can create namespaces, if you want to emit an event to all the clients that connected to a namespace, this is the way.
<pre>io.of('/namespaceName').emit('eventName', { data: 'tosend'});
</pre>
</li>
</span></ul>
<span style="font-family: Trebuchet MS, sans-serif;">
<br />
<a name="references"></a>
<b>References</b>
<br />
</span><br />
<ul><span style="font-family: Trebuchet MS, sans-serif;">
<li><a href="http://uditalias.github.com/chat-nodejs/public/" target="_blank"><b>Demo</b></a> of this chat application</li>
<li><a href="http://uditalias.github.com/chat-nodejs/" target="_blank"><b>Download</b></a> the source code from github</li>
</span></ul>
<span style="font-family: Trebuchet MS, sans-serif;">
</span>Anonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.com1tag:blogger.com,1999:blog-4225294103513112321.post-27385951189948804422012-11-07T01:57:00.000+02:002012-11-08T12:41:43.556+02:00Facebook API with Node.js<span style="font-family: Trebuchet MS, sans-serif;"><span style="font-size: x-large;">F</span>acebook API is very powerful, it reveals a lot of data. Lets say that we want to work with the Facebook API from node.js to get some data from Facebook.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">In this post I will explain how to achieve this task, I assume that you already have a valid <a href="https://developers.facebook.com/docs/concepts/login/access-tokens-and-types/" target="_blank">access token</a> of a Facebook user, If you didn't reach to this point, this <a href="http://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/" target="_blank">link</a> will show you how to start.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>The module</b></span><br />
<span style="font-family: Trebuchet MS, sans-serif;">The first thing is to build a module for Node, I want this module to be able to get a Facebook API open graph path (such as: /me/friends) and retrieve the data from Facebook. In this module I choose to work with the <a href="http://nodejs.org/api/https.html" target="_blank">https</a> module of node.js, with this module I can make secured requests to Facebook via https.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>facebook.js</b></span>
<br />
<pre class="prettyprint lang-js linenums">var https = require('https');
exports.get = function(accessToken, apiPath, callback) {
// creating options object for the https request
var options = {
// the facebook open graph domain
host: 'graph.facebook.com',
// secured port, for https
port: 443,
// apiPath is the open graph api path
path: apiPath + '?access_token=' + accessToken,
// well.. you know...
method: 'GET'
};
// create a buffer to hold the data received
// from facebook
var buffer = '';
// initialize the get request
var request = https.get(options, function(result){
result.setEncoding('utf8');
// each data event of the request receiving
// chunk, this is where i`m collecting the chunks
// and put them together into one buffer...
result.on('data', function(chunk){
buffer += chunk;
});
// all the data received, calling the callback
// function with the data as a parameter
result.on('end', function(){
callback(buffer);
});
});
// just in case of an error, prompting a message
request.on('error', function(e){
console.log('error from facebook.get(): '
+ e.message);
});
request.end();
}</pre>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">Now, In your Node.js server, include this facebook module and call the get() function with the open graph api path that you want.</span>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<br />
<pre class="prettyprint lang-js linenums">var facebook = require('./facebook');
facebook.get('FB_ACCESS_TOKEN', '/me/friends', function(data){
console.log(data);
});
</pre>
<span style="font-family: Trebuchet MS, sans-serif;">
The get() function is a generic function, you can call it with your api request path, pass the access token of the current user and a callback function to be called when the data is ready.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>Anonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.com0tag:blogger.com,1999:blog-4225294103513112321.post-68518336094884704932012-11-05T01:44:00.003+02:002012-11-07T11:52:55.402+02:00Hello World!<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr7tLrRTnesObchlQ-nu25Y2VicpO6fkogY8BLfk_-KuMQGR10L4SPAniswUd55hM-xOboBW8CdmvKt7fy_1bn0IVplITBQoMiYTRkz_xXkPB7U_cVNWaXXKeoDol6Tu8gKPoaCh1fwMU/s1600/moon.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr7tLrRTnesObchlQ-nu25Y2VicpO6fkogY8BLfk_-KuMQGR10L4SPAniswUd55hM-xOboBW8CdmvKt7fy_1bn0IVplITBQoMiYTRkz_xXkPB7U_cVNWaXXKeoDol6Tu8gKPoaCh1fwMU/s1600/moon.png" /></a></div>
<span style="font-family: Trebuchet MS, sans-serif;"><span style="font-size: x-large;">A</span>fter writing thousands lines of code, I thought that it will be great to share the knowledge I've collected through all my years of coding. So I decided to start with.. you know.. a blog, and after playing a bit with the very cool admin panel of Blogspot, finally, I got one.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">This blog is all about my passion, the Web. From front-end development with JavaScript, HTML5 and CSS3, to Node.js server side programming, Real-Time communication technologies right from the browser (The web is not stateless anymore :), MongoDB and even UI/UX and Web design.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">I will try to keep things updated as much as I can, feel free to comment, ask questions and give suggestions.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">Check out the <a href="http://udidu.blogspot.co.il/p/about.html">About</a> page to find out more about me.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">Cheers.</span>Anonymoushttp://www.blogger.com/profile/15636278614539250979noreply@blogger.com1