Manage events, callbacks and rooms over a socket connection
An event handler for socket interfaces. It was built for use with SockJS, but can be used with any socket interface, such as node streams.
It has a similar feature set to Socket.io, including rooms and broadcasting.
Jandal has a maximum of three arguments per event. This restriction vastly improves performance in most browsers and in nodejs.
This may sound harsh, but you probably don't need to use more than three args anyway. You can always store extra args in an array or object.
There is also the deprecated multi-args branch
Add it to your project with
npm install --save jandal.
var http Jandal sockjs server conn;http = require'http';Jandal = require'jandal';sockjs = require'sockjs';// standard sockjs stuffserver = httpcreateServer;conn = sockjscreateServer;conninstallHandlersserver prefix: '/socket' ;serverlisten8080;// Listen for new connectionsconnon'connection'var jandal;// wrap the socket in a Jandaljandal = socket 'stream';// listening for the 'log' eventjandalon'log'console.log'log: ' + text;;// listening for an event with a callbackjandalon'echo'callbacktext;;// send an event to the clientjandalemit'weclome'id: socketidtime: Datenow;;
Grab a copy of
/client.js from this repo, or use CommonJS compiler and
var conn socket;// use browserify// or load the libraries as seperate scriptsrequire'sockjs';require'jandal/client';conn = '';socket = conn 'websocket';// Wait for socket to connectsocketon'socket.open'// listen for eventssocketon'welcome'console.loginfo;;// send a message to the serversocketemit'log' 'the time is' + Datenow;// Send a message to the server with a callbacksocketemit'echo' 'hello'assertreply === 'hello';;;
connon'connection'var jandal;// wrap the socketjandal = socket 'stream';// add it to a roomjandaljoin'my_room';// emit to all other sockets in a roomjandalbroadcastto'my_room'emit'a new socket has joined' jandalid;// remove it from a roomjandalleave'my_room';;
Jandal class has a couple of static properties useful for managing
This is a
Room instance that holds all the connected sockets. See the
docs for more info.
// EmittingJandalallemit'hello' 1 23;// BroadcastingJandalallbroadcast'socket-id' 'hello' 1 2 3;
Easily access any sockets in any room. See the
Room docs for more info.
Every Jandal instance extends the NodeJS EventEmitter so you can also use
setMaxListeners. See the
EventEmitter docs for more information.
An array that holds all the rooms the socket is currently joined to.
var jandal conn;jandal = ;conn = configurl;jandalconnectconn 'websocket';
Example with custom handles:
var jandal handle socket;jandal = ;socket = ;handle =socketemit'message' message;onread: socket fnsocketon'message' fn;;jandalconnectsocket handle;
This is very similar to the NodeJS EventEmitter, but you are limited to three arguments.
Arguments can be strings, numbers, booleans, dates, objects, arrays, etc...
Basically anything that
JSON.stringify can handle.
You can also send one function for use as a callback.
var jandal;jandal = ;// lots of different data typesjandalemit'my-event' 'arg 1' 'arg 2' arg: 3// passing functions as callbacksjandalemit'my-callback' 'some data'console.log'running the callback with' response;;
Works very similar to the EventEmitter.
jandalon'my-event'console.log'"my-event" has been emitted with' arguments;;// listening for a namespace + eventjandalon'task.create' listener;// this is the same asjandalnamespace'task'on'create' listener;
Return a new Namespace instance. If the namespace already exists, it will
use that instead of creating a new one. See the
Namespace docs for more info.
var jandal ns;jandal = ;ns = jandalnamespace'app';// sends "app.hello()"nsemit'hello';// listens for "app.goodbye"nson'goodbye'console.log'bye';;
Put the socket in a room.
Remove the socket from a room.
Returns a room. Same as
// add the socket to the roomjandaljoin'my-room';// get the roomvar room = jandalroom'my-room';// emit to all the sockets in the roomroomemit'hello';
Remove the socket from all the rooms it is currently in.
Rooms are just a collection of sockets. You can add or remove sockets from them, and emit events to all sockets in that room, or broadcast events from a socket to all other sockets.
Every socket is added to the 'all' room, which can be acessed through
Returns the number of connected sockets in a room.
Check if a socket is in a room. Returns
var a b;a = ;ajoin'my-room';b = ;Jandalin'my-room'containsa; // trueJandalin'my-room'containsb; // false
Exactly the same as
jandal.emit but will be sent to all connected sockets.
Jandalin'my-room'emit'hello' 1 2 3;
Just like emit, but will not send to the 'sender' socket.
Jandalin'my-room'broadcast'some-id' 'bye' 1 2 3;
Get a namespace for a room.
Destroy all sockets in a room
Handles are used as an interface between Jandal and a socket.
There are two handles bundled by default:
Works with SockJS-Node
stream:return socketid;socketwritemessage;socketon'data' fn;socketon'close' fn;socketon'error' fn;setTimeoutfn 0;socketremoveAllListeners'data';socketremoveAllListeners'close';socketremoveAllListeners'error';
Works with the WebSocket API (and also SockJS-Client).
websocketsId = 0;websocket:if sockethasOwnProperty'id' return socketid;socketid = ++websocketsId;return socketid;socketsendmessage;fnedata; ;socketonclose = fn;socketonerror = fn;socketonopen = fn;delete socketonmessage;delete socketonclose;delete socketonerror;delete socketonopen;
Return something that identifies this socket, like an ID.
var handler =// if your sockets already have an idreturn socketid;// maybe assign an id?// HINT: better to usereturn socketid || socketid = ++someNumber;// if you don't care about anythingreturn socket;;
Write a message to the socket. Will be called whenever a message needs to be sent.
var handler =socketwritemessage;;
Listen for messages. Will be called once per each socket. Expects the
callback to be passed a message whenever one is sent.
var handler =socketon'read' fn;;
Listen for errors on the socket. Will be called only once per each socket.
fn to be called whenever the socket has an error. Accepts one
argument that will be be passed through to the
var handler =socketon'error'fnerr;;;
Listen for the socket connection to be opened. Will be called once per each
socket. Expects the
fn callback to called once when the socket has connected.
If the socket is already open, the you can run the callback immediately. Will
be passed through to the
var handler =socketon'open' fn;;
Listen for the socket to be closed. Will be called once per each socket.
fn callback to be called only once, and only when the socket has
been closed. Arguments will be passed through to the
var handler =socketon'close' fn;;
Disconnect the raw socket from the jandal instance.
var handler =socketoff'data';socketoff'open';socketoff'close';socketoff'error';;
There are four parts to a message:
The namespace and callback are both optional.
// event + single argfetch"info"// event + multiple argsfetch"info""count":40// event + arg + callbackfetch"info"fn10// namespace + event + arguserload"numbers"102030// namespace + event + arg + callbacktaskcreate"name":"this is a new task"fn1
Each message can have a single callback. The callback must be the last arguments, and can only be called once.
Callbacks are just like regular events, so you can also have a callback on a callback.
// send a message with a callbackapplogin'username' 'password'fn32// response running the callback with argssocketfn_23login: success// callback with a callbacksocketfn_24login: failfn25
The same code can be run in the browser by using Browserify.
This also allows you to use the library to communicate between servers, as it acts as the client and the server.
To compile for the browser:
npm run-script build
And then either copy/paste the
client.js file into your project, or
include it via
The MIT License (MIT)
Copyright (c) 2014 George Czabania
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
socket.id !== senderinstead of
socket !== sender. This requires all sockets to have an 'id' attribute.
socketnamespace instead of
Jandalfor handling callbacks.
parseprivate methods of a Jandal instance.
callbacksprivate properties of a Jandal instance.
Jandal.handle(). Instead pass the handler to the
new Jandal(socket, 'stream');.
room.empty(). No longer destroys room, just removes all the connected sockets.
var = a, b, c;to
var a = 1; \n var b = 2; var c = 3;
onclosehandler now accepts two arguments that will be passed through to the
client.jsto the root directory. You should now use
Socket.prototype.serialize, where callbacks could not be the last argument.
Socket.prototype.parsewill only accept strings.
Socket.prototype.parseagainst crashing on invalid messages.
Socket.prototype.roomto access rooms from a jandal instance.