rob@76: rc-web@69: /** rc-web@69: * Module dependencies. rc-web@69: */ rc-web@69: rob@76: var Socket = require('./socket'); rob@76: var Emitter = require('events').EventEmitter; rob@76: var parser = require('socket.io-parser'); rob@76: var debug = require('debug')('socket.io:namespace'); rob@76: var hasBin = require('has-binary-data'); rc-web@69: rc-web@69: /** rob@76: * Module exports. rc-web@69: */ rc-web@69: rob@76: module.exports = exports = Namespace; rc-web@69: rc-web@69: /** rob@76: * Blacklisted events. rc-web@69: */ rc-web@69: rob@76: exports.events = [ rob@76: 'connect', // for symmetry with client rob@76: 'connection', rob@76: 'newListener' rob@76: ]; rc-web@69: rc-web@69: /** rob@76: * Flags. rc-web@69: */ rc-web@69: rob@76: exports.flags = ['json']; rc-web@69: rc-web@69: /** rob@76: * `EventEmitter#emit` reference. rob@76: */ rob@76: rob@76: var emit = Emitter.prototype.emit; rob@76: rob@76: /** rob@76: * Namespace constructor. rob@76: * rob@76: * @param {Server} server instance rob@76: * @param {Socket} name rob@76: * @api private rob@76: */ rob@76: rob@76: function Namespace(server, name){ rob@76: this.name = name; rob@76: this.server = server; rob@76: this.sockets = []; rob@76: this.connected = {}; rob@76: this.fns = []; rob@76: this.ids = 0; rob@76: this.acks = {}; rob@76: this.initAdapter(); rob@76: } rob@76: rob@76: /** rob@76: * Inherits from `EventEmitter`. rob@76: */ rob@76: rob@76: Namespace.prototype.__proto__ = Emitter.prototype; rob@76: rob@76: /** rob@76: * Apply flags from `Socket`. rob@76: */ rob@76: rob@76: exports.flags.forEach(function(flag){ rob@76: Namespace.prototype.__defineGetter__(flag, function(){ rob@76: this.flags = this.flags || {}; rob@76: this.flags[flag] = true; rob@76: return this; rob@76: }); rob@76: }); rob@76: rob@76: /** rob@76: * Initializes the `Adapter` for this nsp. rob@76: * Run upon changing adapter by `Server#adapter` rob@76: * in addition to the constructor. rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rob@76: Namespace.prototype.initAdapter = function(){ rob@76: this.adapter = new (this.server.adapter())(this); rob@76: }; rc-web@69: rc-web@69: /** rob@76: * Sets up namespace middleware. rc-web@69: * rob@76: * @return {Namespace} self rc-web@69: * @api public rc-web@69: */ rc-web@69: rob@76: Namespace.prototype.use = function(fn){ rob@76: this.fns.push(fn); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rob@76: * Executes the middleware for an incoming client. rc-web@69: * rob@76: * @param {Socket} socket that will get added rob@76: * @param {Function} last fn call in the middleware rob@76: * @api private rob@76: */ rob@76: rob@76: Namespace.prototype.run = function(socket, fn){ rob@76: var fns = this.fns.slice(0); rob@76: if (!fns.length) return fn(null); rob@76: rob@76: function run(i){ rob@76: fns[i](socket, function(err){ rob@76: // upon error, short-circuit rob@76: if (err) return fn(err); rob@76: rob@76: // if no middleware left, summon callback rob@76: if (!fns[i + 1]) return fn(null); rob@76: rob@76: // go on to next rob@76: run(i + 1); rob@76: }); rob@76: } rob@76: rob@76: run(0); rob@76: }; rob@76: rob@76: /** rob@76: * Targets a room when emitting. rob@76: * rob@76: * @param {String} name rob@76: * @return {Namespace} self rc-web@69: * @api public rc-web@69: */ rc-web@69: rob@76: Namespace.prototype.to = rob@76: Namespace.prototype['in'] = function(name){ rob@76: this.rooms = this.rooms || []; rob@76: if (!~this.rooms.indexOf(name)) this.rooms.push(name); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rob@76: * Adds a new client. rob@76: * rob@76: * @return {Socket} rob@76: * @api private rob@76: */ rob@76: rob@76: Namespace.prototype.add = function(client, fn){ rob@76: debug('adding socket to nsp %s', this.name); rob@76: var socket = new Socket(this, client); rob@76: var self = this; rob@76: this.run(socket, function(err){ rob@76: process.nextTick(function(){ rob@76: if ('open' == client.conn.readyState) { rob@76: if (err) return socket.error(err.data || err.message); rob@76: rob@76: // track socket rob@76: self.sockets.push(socket); rob@76: rob@76: // it's paramount that the internal `onconnect` logic rob@76: // fires before user-set events to prevent state order rob@76: // violations (such as a disconnection before the connection rob@76: // logic is complete) rob@76: socket.onconnect(); rob@76: if (fn) fn(); rob@76: rob@76: // fire user-set events rob@76: self.emit('connect', socket); rob@76: self.emit('connection', socket); rob@76: } else { rob@76: debug('next called after client was closed - ignoring socket'); rob@76: } rob@76: }); rob@76: }); rob@76: return socket; rob@76: }; rob@76: rob@76: /** rob@76: * Removes a client. Called by each `Socket`. rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rob@76: Namespace.prototype.remove = function(socket){ rob@76: var i = this.sockets.indexOf(socket); rob@76: if (~i) { rob@76: this.sockets.splice(i, 1); rob@76: } else { rob@76: debug('ignoring remove for %s', socket.id); rob@76: } rob@76: }; rob@76: rob@76: /** rob@76: * Emits to all clients. rob@76: * rob@76: * @return {Namespace} self rob@76: * @api public rob@76: */ rob@76: rob@76: Namespace.prototype.emit = function(ev){ rob@76: if (~exports.events.indexOf(ev)) { rob@76: emit.apply(this, arguments); rob@76: } else { rob@76: // set up packet object rob@76: var args = Array.prototype.slice.call(arguments); rob@76: var parserType = parser.EVENT; // default rob@76: if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary rob@76: rob@76: var packet = { type: parserType, data: args }; rob@76: rob@76: if ('function' == typeof args[args.length - 1]) { rob@76: throw new Error('Callbacks are not supported when broadcasting'); rob@76: } rob@76: rob@76: this.adapter.broadcast(packet, { rob@76: rooms: this.rooms, rob@76: flags: this.flags rob@76: }); rob@76: rob@76: delete this.rooms; rob@76: delete this.flags; rob@76: } rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rob@76: * Sends a `message` event to all clients. rc-web@69: * rob@76: * @return {Namespace} self rc-web@69: * @api public rc-web@69: */ rc-web@69: rob@76: Namespace.prototype.send = rob@76: Namespace.prototype.write = function(){ rob@76: var args = Array.prototype.slice.call(arguments); rob@76: args.unshift('message'); rob@76: this.emit.apply(this, args); rc-web@69: return this; rc-web@69: };