Compare commits

..

23 Commits
0.3.1 ... 0.4

Author SHA1 Message Date
Guillermo Rauch
57c8d37d47 Implemented heartbeats 2010-06-26 15:04:55 -07:00
Guillermo Rauch
5e329229b4 Fix for flush in htmlfile 2010-06-25 14:57:57 -07:00
Guillermo Rauch
ea36d24d9e Updated Socket.IO client 2010-06-18 03:02:51 -03:00
Guillermo Rauch
d79de5aa56 Multipart fix for Node 0.1.98 2010-06-18 02:38:08 -03:00
Guillermo Rauch
06769c4ca7 Object fixes 2010-06-01 16:19:29 -03:00
Christiaan
df876e37df New changes passing JSlint as much as possible again 2010-06-01 21:05:20 +02:00
Christiaan
31d119740c Merge branch 'master' of http://github.com/LearnBoost/Socket.IO-node
Conflicts:
	lib/socket.io/client.js
	lib/socket.io/listener.js
	lib/socket.io/transports/websocket.js
	lib/socket.io/transports/xhr-multipart.js
	lib/socket.io/transports/xhr-polling.js

Conflicts Resolved
2010-06-01 20:54:13 +02:00
Guillermo Rauch
f3c28e298a Cross domain support:
XHR-Polling
 XHR-Multipart
WebSocket support for Webkit nightly / Chromium nightly
Added heartbeats to multipart
2010-06-01 08:01:11 -03:00
Brian
71d80181fa Fixed multipart streaming so that it works properly again (especially for cross-domain requests). Tested on Firefox 3.6.4. 2010-05-30 22:52:28 -07:00
Brian
0e483a05a5 Moved the cross-site origin checking to the client class, added cross-domain support to xhr-* transports, added ping functionality for the xhr-multipart transport, and fixed the listener class where the log function that can be overridden was not being referenced everywhere. 2010-05-30 22:52:16 -07:00
Christiaan
c1fca0c520 Almost fully passing JSlint 2010-05-29 15:21:46 +02:00
Visnu Pitiyanuvath
1918b75360 implement the sec-websocket-key handshake for latest chrome and webkit builds
http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
2010-05-28 11:15:55 -07:00
Christiaan
5e255ccdf9 Use exports instead of this as exports is the official way according to the commonjs spec 2010-05-28 19:37:46 +02:00
Christiaan
9a306fddab variables which are no constructor shouldn't start with a uppercase letter 2010-05-28 19:24:38 +02:00
Christiaan
dc593e9972 Use EventEmitter public API instead of the private _events array 2010-05-28 19:22:54 +02:00
Guillermo Rauch
7f5228abc3 Fixed syntax error -.- 2010-05-28 06:12:31 -03:00
Guillermo Rauch
0ad237ddf5 Fix for stream not writable problems 2010-05-28 05:28:25 -03:00
Guillermo Rauch
3134940ed6 Updated io.Socket initialization to work on hosts other than localhost
Updated client
2010-05-27 21:45:01 -03:00
Guillermo Rauch
0f783bc06a Fixed htmlfile
- end > close for detecting closed connections
 - Added hearbeats
 - added flush() in write
2010-05-27 21:33:07 -03:00
Virtuo
dd3d829173 Correct bug due to variable name clash.
IMO: this kind of code should be put in an external lib and 100% tested.
2010-05-21 13:09:14 +02:00
Guillermo Rauch
fbce6379a5 Note about submodules 2010-05-13 22:05:31 -03:00
Guillermo Rauch
0459c95848 Added support for Node 0.1.94
Close timeout for transports with keep-alive / websocket set to 0
Close timeout for xhr-polling reduced from 8 seconds to 5 seconds
Now you can pass options to specific transports from io.listen()
Some cleanup
2010-05-12 12:16:44 -03:00
Guillermo Rauch
67815ec15d http > git in gitmodules 2010-05-11 19:34:25 -03:00
15 changed files with 319 additions and 164 deletions

2
.gitmodules vendored
View File

@@ -3,4 +3,4 @@
url = git://github.com/visionmedia/js-oo.git
[submodule "test/client"]
path = test/client
url = http://github.com/LearnBoost/Socket.IO.git
url = git://github.com/LearnBoost/Socket.IO.git

View File

@@ -11,7 +11,7 @@ The `Socket.IO` server provides seamless supports for a variety of transports in
Requirements
------------
- Node v0.1.93+
- Node v0.1.94+
- [Socket.IO client](http://github.com/LearnBoost/Socket.IO) to connect from the browser
How to use
@@ -36,12 +36,15 @@ By default, the server will intercept requests that contain `socket.io` in the p
On the client side, you should use the [Socket.IO client](https://github.com/LearnBoost/Socket.IO) to connect.
## Checking out
## Notes
After cloning the repository, remember to run
IMPORTANT! When checking out the git repo, make sure to include the submodules. One way to do it is:
git submodule init
git submodule update
git clone [repo] --recursive
Another, once cloned
git submodule update --init --recursive
## Demo
@@ -105,11 +108,9 @@ Options:
A list of the accepted transports.
- *timeout*
8000
Time it has to pass without a reconnection to consider a client disconnected. Applies to all transports.
- *transportOptions*
An object of options to pass to each transport. For example `{ websocket: { closeTimeout: 8000 }}`
- *log*
@@ -171,7 +172,7 @@ One of the design goals is that you should be able to implement whatever protoco
The concept of session also benefits naturally full-duplex WebSocket, in the event of an accidental disconnection and a quick reconnection. Messages that the server intends to deliver to the client are cached temporarily until the reconnection.
The implementation of reconnection logic (potentially with retries) is left for the user.
The implementation of reconnection logic (potentially with retries) is left for the user. By default, transports that are keep-alive or open all the time (like WebSocket) have a timeout of 0 if a disconnection is detected.
* Message batching

View File

@@ -1,22 +1,37 @@
var sys = require('sys');
var options = require('./util/options').options, urlparse = require('url').parse;
this.Client = Class({
exports.Client = Class({
include: [options],
options: {
closeTimeout: 0,
heartbeatInterval: 5000
},
init: function(listener, req, res){
this.listener = listener;
init: function(listener, req, res, options, head){
this.listener = listener;
this.setOptions(options);
this.connections = 0;
this.connected = false;
this.upgradeHead = head;
this._onConnect(req, res);
},
send: function(message){
if (!this.connected || !(this.connection.readyState == 'open' || this.connection.readyState == 'writeOnly')) return this._queue(message);
if (!this.connected || !(this.connection.readyState === 'open' ||
this.connection.readyState === 'writeOnly')) {
return this._queue(message);
}
this._write(JSON.stringify({messages: [message]}));
return this;
},
broadcast: function(message){
if (!('sessionId' in this)) return this;
if (!('sessionId' in this)) {
return this;
}
this.listener.broadcast(message, this.sessionId);
return this;
},
@@ -37,7 +52,9 @@ this.Client = Class({
this.request = req;
this.response = res;
this.connection = this.request.connection;
if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
if (this._disconnectTimeout) {
clearTimeout(this._disconnectTimeout);
}
},
_payload: function(){
@@ -57,16 +74,23 @@ this.Client = Class({
payload = payload.concat(this._writeQueue || []);
this._writeQueue = [];
if (payload.length) this._write(JSON.stringify({messages: payload}));
if (this.connections == 1) this.listener._onClientConnect(this);
if (payload.length) {
this._write(JSON.stringify({messages: payload}));
}
if (this.connections === 1) {
this.listener._onClientConnect(this);
}
},
_onClose: function(){
var self = this;
if (this._heartbeatInterval) {
clearInterval(this._heartbeatInterval);
}
this.connected = false;
this._disconnectTimeout = setTimeout(function(){
self._onDisconnect();
}, this.listener.options.timeout);
}, this.options.closeTimeout);
},
_onDisconnect: function(){
@@ -74,20 +98,34 @@ this.Client = Class({
this._writeQueue = [];
this.connected = false;
this.finalized = true;
if (this.handshaked) this.listener._onClientDisconnect(this);
if (this.handshaked) {
this.listener._onClientDisconnect(this);
}
}
},
_queue: function(message){
if (!('_writeQueue' in this)) this._writeQueue = [];
if (!('_writeQueue' in this)) {
this._writeQueue = [];
}
this._writeQueue.push(message);
return this;
},
_generateSessionId: function(){
if (this.sessionId) return this.listener.options.log('This client already has a session id');
if (this.sessionId) {
return this.listener.options.log('This client already has a session id');
}
this.sessionId = Math.random().toString().substr(2);
return this;
},
_verifyOrigin: function(origin){
var parts = urlparse(origin), origins = this.listener.options.origins;
return origins.indexOf('*:*') !== -1 ||
origins.indexOf(parts.host + ':' + parts.port) !== -1 ||
origins.indexOf(parts.host + ':*') !== -1 ||
origins.indexOf('*:' + parts.port) !== -1;
}
});

View File

@@ -1,48 +1,50 @@
var url = require('url'),
sys = require('sys'),
Options = require('./util/options').Options,
Client = require('./client').Client,
Transports = {},
Listener = this.Listener = Class({
options = require('./util/options').options,
Client = require('./client').Client,
transports = {};
var Listener = exports.Listener = Class({
include: [process.EventEmitter.prototype, Options],
include: [process.EventEmitter.prototype, options],
options: {
origins: '*:*',
resource: 'socket.io',
transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling'],
timeout: 8000,
transportOptions: {},
log: function(message){
sys.log(message);
require('sys').log(message);
}
},
init: function(server, options){
var self = this;
process.EventEmitter.call(this);
this.server = server;
this.setOptions(options);
this.server = server;
this.setOptions(options);
this.clients = [];
this.clientsIndex = {};
var listener = (this.server._events['request'] instanceof Array)
? this.server._events['request'][0]
: this.server._events['request'];
if (listener){
var self = this;
this.server._events['request'] = function(req, res){
if (self.check(req, res)) return;
listener(req, res);
};
} else {
throw new Error('Couldn\'t find the `request` event in the HTTP server.');
}
var listeners = this.server.listeners('request');
this.server.removeAllListeners('request');
this.server.addListener('request', function(req, res){
if (self.check(req, res)) return;
for (var i = 0; i < listeners.length; i++) {
listeners[i].call(this, req, res);
}
});
this.server.addListener('upgrade', function(req, socket, head){
if (!self.check(req, socket, true, head)){
socket.destroy();
}
});
this.options.transports.forEach(function(t){
if (!(t in Transports)){
Transports[t] = require('./transports/' + t)[t];
if (Transports[t].init) Transports[t].init(this);
if (!(t in transports)){
transports[t] = require('./transports/' + t)[t];
if (transports[t].init) transports[t].init(this);
}
}, this);
@@ -53,13 +55,13 @@ Listener = this.Listener = Class({
for (var i = 0, l = this.clients.length; i < l; i++){
if (this.clients[i] && (!except || [].concat(except).indexOf(this.clients[i].sessionId) == -1)){
this.clients[i].send(message);
}
}
}
return this;
},
check: function(req, res){
var path = url.parse(req.url).pathname, parts, cn;
check: function(req, res, httpUpgrade, head){
var path = url.parse(req.url).pathname, parts, cn;
if (path.indexOf('/' + this.options.resource) === 0){
parts = path.substr(1).split('/');
if (parts[2]){
@@ -68,13 +70,13 @@ Listener = this.Listener = Class({
cn._onConnect(req, res);
} else {
req.connection.end();
sys.log('Couldnt find client with session id "' + parts[2] + '"');
this.options.log('Couldnt find client with session id "' + parts[2] + '"');
}
} else {
this._onConnection(parts[1], req, res);
}
this._onConnection(parts[1], req, res, httpUpgrade, head);
}
return true;
}
}
return false;
},
@@ -84,12 +86,12 @@ Listener = this.Listener = Class({
_onClientConnect: function(client){
if (!(client instanceof Client) || !client.sessionId){
return sys.log('Invalid client');
return this.options.log('Invalid client');
}
client.i = this.clients.length;
this.clients.push(client);
this.clientsIndex[client.sessionId] = client;
sys.log('Client '+ client.sessionId +' connected');
this.options.log('Client '+ client.sessionId +' connected');
this.emit('clientConnect', client);
},
@@ -100,18 +102,18 @@ Listener = this.Listener = Class({
_onClientDisconnect: function(client){
this.clientsIndex[client.sessionId] = null;
this.clients[client.i] = null;
sys.log('Client '+ client.sessionId +' disconnected');
this.options.log('Client '+ client.sessionId +' disconnected');
this.emit('clientDisconnect', client);
},
// new connections (no session id)
_onConnection: function(transport, req, res){
if (this.options.transports.indexOf(transport) === -1){
req.connection.end();
return sys.log('Illegal transport "'+ transport +'"');
_onConnection: function(transport, req, res, httpUpgrade, head){
if (this.options.transports.indexOf(transport) === -1 || (httpUpgrade && !transports[transport].httpUpgrade)){
httpUpgrade ? res.destroy() : req.connection.destroy();
return this.options.log('Illegal transport "'+ transport +'"');
}
sys.log('Initializing client with transport "'+ transport +'"');
new Transports[transport](this, req, res);
this.options.log('Initializing client with transport "'+ transport +'"');
new transports[transport](this, req, res, this.options.transportOptions[transport], head);
}
});
});

View File

@@ -2,9 +2,9 @@ var websocket = require('./websocket').websocket,
net = require('net'),
listeners = [];
this.flashsocket = websocket.extend({});
this.flashsocket.init = function(listener){
exports.flashsocket = websocket.extend({});
exports.flashsocket.httpUpgrade = true;
exports.flashsocket.init = function(listener){
listeners.push(listener);
};
@@ -22,4 +22,4 @@ net.createServer(function(socket){
socket.write('</cross-domain-policy>\n');
socket.end();
}).listen(843);
}).listen(843);

View File

@@ -1,19 +1,28 @@
var Client = require('../client').Client,
qs = require('querystring');
this['htmlfile'] = Client.extend({
exports.htmlfile = Client.extend({
_onConnect: function(req, res){
var self = this, body = '';
switch (req.method){
case 'GET':
this.__super__(req, res);
this.request.connection.addListener('end', function(){ self._onClose(); });
this.response.useChunkedEncodingByDefault = false;
this.request.connection.addListener('close', function(){ self._onClose(); });
this.response.useChunkedEncodingByDefault = true;
this.response.shouldKeepAlive = true;
this.response.writeHead(200, { 'Content-type': 'text/html' });
this.response.flush();
this.response.writeHead(200, {
'Content-Type': 'text/html',
'Connection': 'keep-alive',
'Transfer-Encoding': 'chunked'
});
this.response.write('<html><body>' + new Array(244).join(' '));
if ('flush' in this.response) this.response.flush();
this._payload();
this._heartbeatInterval = setInterval(function(){
self.response.write('<!-- heartbeat -->');
if ('flush' in self.response) self.response.flush();
}, this.options.heartbeatInterval);
break;
case 'POST':
@@ -24,7 +33,7 @@ this['htmlfile'] = Client.extend({
try {
var msg = qs.parse(body);
self._onMessage(msg.data);
} catch(e){}
} catch(e){}
res.writeHead(200);
res.write('ok');
res.end();
@@ -34,8 +43,8 @@ this['htmlfile'] = Client.extend({
},
_write: function(message){
// not sure if this is enough escaping. looks lousy
this.response.write("<script>parent.callback('"+ message.replace(/'/, "\'") +"')</script>");
this.response.write('<script>parent.s._('+ message +', document);</script>');
if ('flush' in this.response) this.response.flush();
}
});

View File

@@ -1,42 +1,60 @@
var Client = require('../client').Client,
url = require('url');
url = require('url'),
Buffer = require('buffer').Buffer,
crypto = require('crypto');
this.websocket = Client.extend({
exports.websocket = Client.extend({
_onConnect: function(req, res){
var self = this;
this.__super__(req, res);
_onConnect: function(req, socket){
var self = this, headers = [];
this.request = req;
this.connection = socket;
this.data = '';
if (!this.request.upgrade || this.request.headers['upgrade'] !== 'WebSocket' || !this._verifyOrigin(this.request.headers['origin'])){
if (this.request.headers.upgrade !== 'WebSocket' || !this._verifyOrigin(this.request.headers.origin)){
this.listener.options.log('WebSocket connection invalid');
this.connection.end();
return;
}
this.connection.setTimeout(0);
this.connection.setEncoding('utf8');
this.connection.setNoDelay(true);
this.connection.write([
headers = [
'HTTP/1.1 101 Web Socket Protocol Handshake',
'Upgrade: WebSocket',
'Connection: Upgrade',
'WebSocket-Origin: ' + this.request.headers.origin,
'WebSocket-Location: ws://' + this.request.headers.host + this.request.url,
'', ''
].join('\r\n'));
'WebSocket-Location: ws://' + this.request.headers.host + this.request.url
];
if ('sec-websocket-key1' in this.request.headers){
headers.push(
'Sec-WebSocket-Origin: ' + this.request.headers.origin,
'Sec-WebSocket-Location: ws://' + this.request.headers.host + this.request.url);
}
this.connection.write(headers.concat('', '').join('\r\n'));
this.connection.addListener('end', function(){ self._onClose(); });
this.connection.addListener('data', function(data){ self._handle(data); });
this._payload();
if (this._proveReception()){
this._payload();
}
setInterval(function(){
self._write(JSON.stringify({heartbeat: '1'}));
}, 10000);
},
_handle: function(data){
var chunk, chunks, chunk_count;
this.data += data;
chunks = this.data.split('\ufffd');
chunk_count = chunks.length - 1;
for (var i = 0; i < chunk_count; i++) {
chunk = chunks[i];
if (chunk[0] != '\u0000') {
if (chunk[0] !== '\u0000') {
this.listener.options.log('Data incorrectly framed by UA. Dropping connection');
this.connection.destroy();
return false;
@@ -45,19 +63,49 @@ this.websocket = Client.extend({
}
this.data = chunks[chunks.length - 1];
},
_verifyOrigin: function(origin){
var parts = url.parse(origin);
return this.listener.options.origins.indexOf('*:*') !== -1
|| this.listener.options.origins.indexOf(parts.host + ':' + parts.port) !== -1
|| this.listener.options.origins.indexOf(parts.host + ':*') !== -1
|| this.listener.options.origins.indexOf('*:' + parts.port) !== -1;
// http://www.whatwg.org/specs/web-apps/current-work/complete/network.html#opening-handshake
_proveReception: function(){
var k1 = this.request.headers['sec-websocket-key1'],
k2 = this.request.headers['sec-websocket-key2'];
if (k1 && k2) {
var md5 = crypto.createHash('md5');
[k1, k2].forEach(function(k) {
var n = k.replace(/[^\d]/g, ''),
spaces = k.replace(/[^ ]/g, '').length,
buf = new Buffer(4);
if (spaces === 0) {
this.listener.options.log('Invalid WebSocket key: "' + k + '". Dropping connection');
this.connection.destroy();
return false;
}
n /= spaces;
buf[3] = n & 0xff;
buf[2] = (n >>= 8) & 0xff;
buf[1] = (n >>= 8) & 0xff;
buf[0] = (n >>= 8) & 0xff;
md5.update(buf.toString('binary'));
});
md5.update(this.upgradeHead.toString('binary'));
this.connection.write(md5.digest('binary'), 'binary');
}
return true;
},
_write: function(message){
this.connection.write('\u0000', 'binary');
this.connection.write(message, 'utf8');
this.connection.write('\uffff', 'binary');
try {
this.connection.write('\u0000', 'binary');
this.connection.write(message, 'utf8');
this.connection.write('\uffff', 'binary');
} catch(e){
this._onClose();
}
}
});
});
exports.websocket.httpUpgrade = true;

View File

@@ -1,48 +1,69 @@
var Client = require('../client').Client,
qs = require('querystring');
qs = require('querystring');
this['xhr-multipart'] = Client.extend({
exports['xhr-multipart'] = Client.extend({
options: {
pingInterval: 7000
},
_pingInterval: null,
_onConnect: function(req, res){
var self = this, body = '';
var self = this, body = '', headers = {};
// https://developer.mozilla.org/En/HTTP_Access_Control
if (req.headers.origin && this._verifyOrigin(req.headers.origin)) {
headers['Access-Control-Allow-Origin'] = req.headers.origin;
headers['Access-Control-Allow-Credentials'] = 'true';
}
if (typeof req.headers['access-control-request-method'] !== 'undefined') {
// CORS preflight message
headers['Access-Control-Allow-Methods'] = req.headers['access-control-request-method'];
res.writeHead(200, headers);
res.write('ok');
res.end();
return;
}
switch (req.method){
case 'GET':
var self = this;
this.__super__(req, res);
headers['Content-Type'] = 'multipart/x-mixed-replace;boundary="socketio"';
headers['Connection'] = 'keep-alive';
this.request.connection.addListener('end', function(){ self._onClose(); });
this.response.useChunkedEncodingByDefault = false;
this.response.shouldKeepAlive = true;
this.response.writeHead(200, {
'Content-Type': 'multipart/x-mixed-replace;boundary=socketio',
'Connection': 'keep-alive'
});
this.response.writeHead(200, headers);
this.response.write("--socketio\n");
this.response.flush();
if ('flush' in this.response) this.response.flush();
this._payload();
this._heartbeatInterval = setInterval(function(){
self._write(String.fromCharCode(6));
}, this.options.heartbeatInterval);
break;
case 'POST':
req.addListener('data', function(message){
body += message;
body += message.toString();
});
req.addListener('end', function(){
try {
var msg = qs.parse(body);
self._onMessage(msg.data);
} catch(e){}
res.writeHead(200);
} catch(e){}
res.writeHead(200, headers);
res.write('ok');
res.end();
body = '';
});
break;
}
},
_write: function(message){
this.response.write("Content-Type: text/plain\n\n");
this.response.write("Content-Type: text/plain" + (message.length === 1 && message.charCodeAt(0) === 6 ? "; charset=us-ascii" : "") + "\n\n");
this.response.write(message + "\n");
this.response.write("--socketio\n");
this.response.flush();
if ('flush' in this.response) this.response.flush();
}
});

View File

@@ -1,10 +1,10 @@
var Client = require('../client').Client,
qs = require('querystring'),
sys = require('sys');
qs = require('querystring');
this['xhr-polling'] = Client.extend({
exports['xhr-polling'] = Client.extend({
options: {
closeTimeout: 5000,
duration: 20000
},
@@ -12,10 +12,10 @@ this['xhr-polling'] = Client.extend({
var self = this, body = '';
switch (req.method){
case 'GET':
this.__super__(req, res);
this.__super__(req, res);
this._closeTimeout = setTimeout(function(){
self._write('');
}, this.options.duration);
}, this.options.duration);
this._payload();
break;
@@ -27,7 +27,7 @@ this['xhr-polling'] = Client.extend({
try {
var msg = qs.parse(body);
self._onMessage(msg.data);
} catch(e){}
} catch(e){}
res.writeHead(200);
res.write('ok');
res.end();
@@ -37,8 +37,18 @@ this['xhr-polling'] = Client.extend({
},
_write: function(message){
if (this._closeTimeout) clearTimeout(this._closeTimeout);
this.response.writeHead(200, {'Content-Type': 'text/plain', 'Content-Length': message.length});
if (this._closeTimeout) {
clearTimeout(this._closeTimeout);
}
var headers = {'Content-Type': 'text/plain', 'Content-Length': message.length};
// https://developer.mozilla.org/En/HTTP_Access_Control
if (this.request.headers.origin && this._verifyOrigin(this.request.headers.origin)) {
headers['Access-Control-Allow-Origin'] = this.request.headersorigin;
if (this.request.headers.cookie) {
headers['Access-Control-Allow-Credentials'] = 'true';
}
}
this.response.writeHead(200, headers);
this.response.write(message);
this.response.end();
this._onClose();

View File

@@ -1,16 +1,20 @@
// Based on Mixin.js from MooTools (MIT)
// Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>
this.flatten = function(arr){
exports.flatten = function(arr){
var array = [];
for (var i = 0, l = arr.length; i < l; i++){
var item = arr[i];
if (item != null) array = array.concat(item instanceof Array ? array.flatten(item) : item);
if (item !== null) {
array = array.concat(item instanceof Array ? array.flatten(item) : item);
}
}
return array;
};
this.include = function(arr, item){
if (arr.indexOf(item) == -1) arr.push(item);
exports.include = function(arr, item){
if (arr.indexOf(item) === -1) {
arr.push(item);
}
return arr;
};

View File

@@ -1,38 +1,51 @@
// Based on Mixin.js from MooTools (MIT)
// Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>
var clone = this.clone = function(item){
var clone;
exports.clone = function(item) {
var cloned;
if (item instanceof Array){
clone = [];
for (var i = 0; i < item.length; i++) clone[i] = clone(item[i]);
return clone;
} else if (typeof item == 'object') {
clone = {};
for (var key in object) clone[key] = clone(object[key]);
return clone;
} else {
return item;
cloned = [];
for (var i = 0; i < item.length; i++) {
cloned[i] = exports.clone(item[i]);
}
return cloned;
}
},
if (typeof item === 'object') {
cloned = {};
for (var key in item) {
cloned[key] = exports.clone(item[key]);
}
return cloned;
}
return item;
};
mergeOne = function(source, key, current){
var mergeOne = function(source, key, current){
if (current instanceof Array){
source[key] = clone(current);
} else if (typeof current == 'object'){
if (typeof source[key] == 'object') object.merge(source[key], current);
else source[key] = clone(current);
source[key] = exports.clone(current);
} else if (typeof current === 'object'){
if (typeof source[key] === 'object') {
exports.merge(source[key], current);
} else {
source[key] = exports.clone(current);
}
} else {
source[key] = current;
}
return source;
};
this.merge = function(source, k, v){
if (typeof k == 'string') return mergeOne(source, k, v);
exports.merge = function(source, k, v){
if (typeof k === 'string') {
return mergeOne(source, k, v);
}
for (var i = 1, l = arguments.length; i < l; i++){
var object = arguments[i];
for (var key in object) mergeOne(source, key, object[key]);
for (var key in object) {
mergeOne(source, key, object[key]);
}
}
return source;
};
};

View File

@@ -2,7 +2,7 @@
// Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>
var object = require('./object'), sys = require('sys');
this.Options = {
exports.options = {
options: {},
@@ -12,13 +12,20 @@ this.Options = {
},
setOptions: function(options){
for (var key in options) this.setOption(key, options[key]);
for (var key in options) {
this.setOption(key, options[key]);
}
if (this.addListener){
var first_lower = function(full, first){
return first.toLowerCase();
};
// Automagically register callbacks if the varname starts with on
for (var i in this.options){
if (!(/^on[A-Z]/).test(i) || typeof this.options[i] != 'function') continue;
this.addListener(i.replace(/^on([A-Z])/, function(full, first){
return first.toLowerCase();
}), this.options[i]);
if (!(/^on[A-Z]/).test(i) || typeof this.options[i] !== 'function') {
continue;
}
this.addListener(i.replace(/^on([A-Z])/, first_lower), this.options[i]);
this.options[i] = null;
}
}

View File

@@ -25,7 +25,7 @@
document.getElementById('text').value = '';
}
var socket = new io.Socket('localhost', {rememberTransport: false, port: 8080});
var socket = new io.Socket(null, {rememberTransport: false, port: 8080});
socket.connect();
socket.addEvent('message', function(data){
var obj = JSON.parse(data);

View File

@@ -23,8 +23,8 @@ server = http.createServer(function(req, res){
default:
if (/\.(js|html|swf)$/.test(path)){
try {
var swf = path.substr(-4) == '.swf';
res.writeHead(200, {'Content-Type': swf ? 'application/x-shockwave-flash' : ('text/' + (path.substr(-3) == '.js' ? 'javascript' : 'html'))});
var swf = path.substr(-4) === '.swf';
res.writeHead(200, {'Content-Type': swf ? 'application/x-shockwave-flash' : ('text/' + (path.substr(-3) === '.js' ? 'javascript' : 'html'))});
res.write(fs.readFileSync(__dirname + path, swf ? 'binary' : 'utf8'), swf ? 'binary' : 'utf8');
res.end();
} catch(e){
@@ -58,7 +58,9 @@ io.listen(server, {
onClientMessage: function(message, client){
var msg = { message: [client.sessionId, message] };
buffer.push(msg);
if (buffer.length > 15) buffer.shift();
if (buffer.length > 15) {
buffer.shift();
}
client.broadcast(json(msg));
}