/*! * socket.io-node * Copyright(c) 2011 LearnBoost * MIT Licensed */ /** * Module dependencies. */ var parser = require( './parser' ) , util = require( './util' ) , EventEmitter = process.EventEmitter /** * Export the constructor. */ exports = module.exports = Socket; /** * Default error event listener to prevent uncaught exceptions. */ var defaultError = function () { }; /** * Socket constructor. * * @param {Manager} manager instance * @param {String} session id * @param {Namespace} namespace the socket belongs to * @param {Boolean} whether the * @api public */ function Socket( manager, id, nsp, readable ) { this.id = id; this.namespace = nsp; this.manager = manager; this.disconnected = false; this.ackPackets = 0; this.acks = {}; this.setFlags(); this.readable = readable; this.store = this.manager.store.client( this.id ); this.on( 'error', defaultError ); } ; /** * Inherits from EventEmitter. */ Socket.prototype.__proto__ = EventEmitter.prototype; /** * Accessor shortcut for the handshake data * * @api private */ Socket.prototype.__defineGetter__( 'handshake', function () { return this.manager.handshaken[this.id]; } ); /** * Accessor shortcut for the transport type * * @api private */ Socket.prototype.__defineGetter__( 'transport', function () { return this.manager.transports[this.id].name; } ); /** * Accessor shortcut for the logger. * * @api private */ Socket.prototype.__defineGetter__( 'log', function () { return this.manager.log; } ); /** * JSON message flag. * * @api public */ Socket.prototype.__defineGetter__( 'json', function () { this.flags.json = true; return this; } ); /** * Volatile message flag. * * @api public */ Socket.prototype.__defineGetter__( 'volatile', function () { this.flags.volatile = true; return this; } ); /** * Broadcast message flag. * * @api public */ Socket.prototype.__defineGetter__( 'broadcast', function () { this.flags.broadcast = true; return this; } ); /** * Overrides the room to broadcast messages to (flag) * * @api public */ Socket.prototype.to = Socket.prototype.in = function ( room ) { this.flags.room = room; return this; }; /** * Resets flags * * @api private */ Socket.prototype.setFlags = function () { this.flags = { endpoint : this.namespace.name, room : '' }; return this; }; /** * Triggered on disconnect * * @api private */ Socket.prototype.onDisconnect = function ( reason ) { if ( !this.disconnected ) { this.$emit( 'disconnect', reason ); this.disconnected = true; } }; /** * Joins a user to a room. * * @api public */ Socket.prototype.join = function ( name, fn ) { var nsp = this.namespace.name , name = (nsp + '/') + name; this.manager.onJoin( this.id, name ); this.manager.store.publish( 'join', this.id, name ); if ( fn ) { this.log.warn( 'Client#join callback is deprecated' ); fn(); } return this; }; /** * Un-joins a user from a room. * * @api public */ Socket.prototype.leave = function ( name, fn ) { var nsp = this.namespace.name , name = (nsp + '/') + name; this.manager.onLeave( this.id, name ); this.manager.store.publish( 'leave', this.id, name ); if ( fn ) { this.log.warn( 'Client#leave callback is deprecated' ); fn(); } return this; }; /** * Transmits a packet. * * @api private */ Socket.prototype.packet = function ( packet ) { if ( this.flags.broadcast ) { this.log.debug( 'broadcasting packet' ); this.namespace.in( this.flags.room ).except( this.id ).packet( packet ); } else { packet.endpoint = this.flags.endpoint; packet = parser.encodePacket( packet ); this.dispatch( packet, this.flags.volatile ); } this.setFlags(); return this; }; /** * Dispatches a packet * * @api private */ Socket.prototype.dispatch = function ( packet, volatile ) { if ( this.manager.transports[this.id] && this.manager.transports[this.id].open ) { this.manager.transports[this.id].onDispatch( packet, volatile ); } else { if ( !volatile ) { this.manager.onClientDispatch( this.id, packet, volatile ); } this.manager.store.publish( 'dispatch:' + this.id, packet, volatile ); } }; /** * Stores data for the client. * * @api public */ Socket.prototype.set = function ( key, value, fn ) { this.store.set( key, value, fn ); return this; }; /** * Retrieves data for the client * * @api public */ Socket.prototype.get = function ( key, fn ) { this.store.get( key, fn ); return this; }; /** * Checks data for the client * * @api public */ Socket.prototype.has = function ( key, fn ) { this.store.has( key, fn ); return this; }; /** * Deletes data for the client * * @api public */ Socket.prototype.del = function ( key, fn ) { this.store.del( key, fn ); return this; }; /** * Kicks client * * @api public */ Socket.prototype.disconnect = function () { if ( !this.disconnected ) { this.log.info( 'booting client' ); if ( this.manager.transports[this.id] && this.manager.transports[this.id].open ) { this.manager.transports[this.id].onForcedDisconnect(); } else { this.manager.onClientDisconnect( this.id ); this.manager.store.publish( 'disconnect:' + this.id ); } } return this; }; /** * Send a message. * * @api public */ Socket.prototype.send = function ( data, fn ) { var packet = { type : this.flags.json ? 'json' : 'message', data : data }; if ( fn ) { packet.id = ++this.ackPackets; packet.ack = true; this.acks[packet.id] = fn; } return this.packet( packet ); }; /** * Original emit function. * * @api private */ Socket.prototype.$emit = EventEmitter.prototype.emit; /** * Emit override for custom events. * * @api public */ Socket.prototype.emit = function ( ev ) { if ( ev == 'newListener' ) { return this.$emit.apply( this, arguments ); } var args = util.toArray( arguments ).slice( 1 ) , lastArg = args[args.length - 1] , packet = { type : 'event', name : ev }; if ( 'function' == typeof lastArg ) { packet.id = ++this.ackPackets; packet.ack = lastArg.length ? 'data' : true; this.acks[packet.id] = lastArg; args = args.slice( 0, args.length - 1 ); } packet.args = args; return this.packet( packet ); };