mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4395b86c60 | ||
|
|
43d58186d8 | ||
|
|
2066ddd8fe | ||
|
|
db50f3e8ee | ||
|
|
b79faf8998 | ||
|
|
ce88922bea | ||
|
|
5c4c681a83 | ||
|
|
7ca0606670 | ||
|
|
f6036007fc | ||
|
|
aa027ab571 | ||
|
|
b4f24c6995 | ||
|
|
3a53c63778 | ||
|
|
d8e3ccc637 | ||
|
|
cef5fb3574 | ||
|
|
cfbae2ac15 | ||
|
|
1072add73f | ||
|
|
648b1d0792 | ||
|
|
63624e5996 | ||
|
|
1ee5285136 | ||
|
|
5308452b8a | ||
|
|
2ee09436ce | ||
|
|
d2ecaff462 | ||
|
|
023566e03b | ||
|
|
c306a3c303 | ||
|
|
80f1d9780b | ||
|
|
ff10eeecba | ||
|
|
cd9cbb500b | ||
|
|
de8d573948 | ||
|
|
ea9e5ed2cc | ||
|
|
d648fc5a72 | ||
|
|
109a59ca9a | ||
|
|
d304ce19d1 | ||
|
|
f534a260b3 | ||
|
|
cff4669d57 | ||
|
|
4b0a1f22c8 | ||
|
|
e79bdb00e9 | ||
|
|
d11ca00b49 | ||
|
|
f3ba4173c7 | ||
|
|
569103e19a | ||
|
|
06445a0faa | ||
|
|
2506b06961 | ||
|
|
992eda86b4 | ||
|
|
6fa8b1f051 | ||
|
|
a91c6f26f4 | ||
|
|
aa9f2596cb | ||
|
|
e2a97588ef | ||
|
|
0b904d79c2 | ||
|
|
f99ac54df5 | ||
|
|
b306cc77d7 | ||
|
|
ba70be4e0b | ||
|
|
0b17ec9cb8 | ||
|
|
71e77561bb | ||
|
|
f5b2028577 | ||
|
|
7f08d8fd59 | ||
|
|
ffb0574a76 | ||
|
|
e57e27ee43 |
91
History.md
91
History.md
@@ -1,4 +1,95 @@
|
||||
|
||||
0.6.18 / 2011-05-16
|
||||
===================
|
||||
|
||||
* Updated client with multiple fixes.
|
||||
* Fixed returning 'ws' for location even when the client connects via 'wss'.
|
||||
|
||||
0.6.17 / 2011-03-30
|
||||
==================
|
||||
|
||||
* Fixed the 'possible EventEmitter memory leak detected' bug for the XHR transport
|
||||
* Reconnection support added to chat example. [3rd-Eden]
|
||||
|
||||
0.6.16 / 2011-03-04
|
||||
===================
|
||||
|
||||
* Fixed cross domain xhr-polling in Safari [tifroz]
|
||||
|
||||
0.6.15 / 2011-02-23
|
||||
===================
|
||||
|
||||
* Fixed memory leak in WebSocket transport [belorion]
|
||||
|
||||
0.6.14 / 2011-02-18
|
||||
===================
|
||||
|
||||
* Fixed logging scope issue [shripad]
|
||||
|
||||
0.6.13 / 2011-02-18
|
||||
===================
|
||||
|
||||
* Fixed references to listener when logging
|
||||
|
||||
0.6.12 / 2011-02-18
|
||||
===================
|
||||
|
||||
* Fixed noDelay missing file descriptor problem
|
||||
|
||||
0.6.11 / 2011-02-15
|
||||
===================
|
||||
|
||||
* Fixed; Make sure to not execute any other connection operations after WebSocket
|
||||
write error.
|
||||
* Added more error logging
|
||||
|
||||
0.6.10 / 2011-02-09
|
||||
===================
|
||||
|
||||
* Added SSL chat example (`make example-ssl`)
|
||||
* Fixed; possible write errors when a connection error event fires
|
||||
|
||||
0.6.9 / 2011-02-06
|
||||
==================
|
||||
|
||||
* 0.3 compatibility
|
||||
* Updated socket.io client to 0.6.2
|
||||
* Fixed Flash inline policy serving for Firefox 4
|
||||
* Updated expresso
|
||||
* Added comments and version number to socket.io/index
|
||||
|
||||
0.6.8 / 2011-01-10
|
||||
==================
|
||||
|
||||
* Fixed issue with terminating connection twice
|
||||
|
||||
0.6.7 / 2011-01-09
|
||||
==================
|
||||
|
||||
* Fixed situation where the connection drops but the client can still autoreconnect
|
||||
through a different socket. In this case we still want to clear the FD but not
|
||||
call onDisconnect immediately.
|
||||
|
||||
0.6.6 / 2011-01-09
|
||||
==================
|
||||
|
||||
* Note for Flash socket and inline policy on Firefox
|
||||
* Destroy the fds on disconnect
|
||||
* Restored 20 secs of polling so that node doesn't timeout the connections
|
||||
|
||||
0.6.5 / 2011-01-09
|
||||
==================
|
||||
|
||||
* Make sure not to trigger multiple timeouts when closing
|
||||
* Important fix for polling transports.
|
||||
|
||||
0.6.4 / 2011-01-05
|
||||
==================
|
||||
|
||||
* Don't destroy the connection in _onClose. Destroying it will prevent the buffers from being flushed and will result in corrupted responses for the xhr-polling transport.
|
||||
* Added try/catch block around JSON.parse and return an empty object literal if JSON parsing fails.
|
||||
* Added missing .connect() to example
|
||||
|
||||
0.6.3 / 2010-12-23
|
||||
==================
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -7,4 +7,7 @@ test-cov:
|
||||
example:
|
||||
node ./example/server.js
|
||||
|
||||
.PHONY: example
|
||||
example-ssl:
|
||||
node ./example/server-ssl.js
|
||||
|
||||
.PHONY: example
|
||||
|
||||
@@ -32,10 +32,14 @@ and point your browser to `http://localhost:8080`. In addition to `8080`, if the
|
||||
|
||||
By default, the server will intercept requests that contain `socket.io` in the path / resource part of the URI. You can change this as shown in the available options below.
|
||||
|
||||
On the server:
|
||||
On the server, install socket.io with NPM:
|
||||
|
||||
npm install socket.io
|
||||
|
||||
Then:
|
||||
|
||||
var http = require('http'),
|
||||
io = require('./path/to/socket.io'),
|
||||
io = require('socket.io'),
|
||||
|
||||
server = http.createServer(function(req, res){
|
||||
// your normal server code
|
||||
@@ -59,6 +63,7 @@ On the client:
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
var socket = new io.Socket();
|
||||
socket.connect();
|
||||
socket.on('connect', function(){ … })
|
||||
socket.on('message', function(){ … })
|
||||
socket.on('disconnect', function(){ … })
|
||||
|
||||
21
example/cert.crt
Normal file
21
example/cert.crt
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXTCCAkWgAwIBAgIJAMUSOvlaeyQHMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTAxMTE2MDkzMjQ5WhcNMTMxMTE1MDkzMjQ5WjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEVwfPQQp4X
|
||||
wtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+1FAE0c5o
|
||||
exPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404WthquTqg
|
||||
S7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy25IyBK3QJ
|
||||
c+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWAQsqW+COL
|
||||
0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABo1AwTjAdBgNVHQ4EFgQUDnV4d6mD
|
||||
tOnluLoCjkUHTX/n4agwHwYDVR0jBBgwFoAUDnV4d6mDtOnluLoCjkUHTX/n4agw
|
||||
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAFwV4MQfTo+qMv9JMiyno
|
||||
IEiqfOz4RgtmBqRnXUffcjS2dhc7/z+FPZnM79Kej8eLHoVfxCyWRHFlzm93vEdv
|
||||
wxOCrD13EDOi08OOZfxWyIlCa6Bg8cMAKqQzd2OvQOWqlRWBTThBJIhWflU33izX
|
||||
Qn5GdmYqhfpc+9ZHHGhvXNydtRQkdxVK2dZNzLBvBlLlRmtoClU7xm3A+/5dddeP
|
||||
AQHEPtyFlUw49VYtZ3ru6KqPms7MKvcRhYLsy9rwSfuuniMlx4d0bDR7TOkw0QQS
|
||||
A0N8MGQRQpzl4mw4jLzyM5d5QtuGBh2P6hPGa0YQxtI3RPT/p6ENzzBiAKXiSfzo
|
||||
xw==
|
||||
-----END CERTIFICATE-----
|
||||
61
example/chat-ssl.html
Normal file
61
example/chat-ssl.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>socket.io client test</title>
|
||||
|
||||
<script src="/json.js"></script> <!-- for ie -->
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
function message(obj){
|
||||
var el = document.createElement('p');
|
||||
if ('announcement' in obj) el.innerHTML = '<em>' + esc(obj.announcement) + '</em>';
|
||||
else if ('message' in obj) el.innerHTML = '<b>' + esc(obj.message[0]) + ':</b> ' + esc(obj.message[1]);
|
||||
document.getElementById('chat').appendChild(el);
|
||||
document.getElementById('chat').scrollTop = 1000000;
|
||||
}
|
||||
|
||||
function send(){
|
||||
var val = document.getElementById('text').value;
|
||||
socket.send(val);
|
||||
message({ message: ['you', val] });
|
||||
document.getElementById('text').value = '';
|
||||
}
|
||||
|
||||
function esc(msg){
|
||||
return msg.replace(/</g, '<').replace(/>/g, '>');
|
||||
};
|
||||
|
||||
var socket = new io.Socket(null, {port: 443, secure: true, rememberTransport: false});
|
||||
socket.connect();
|
||||
socket.on('message', function(obj){
|
||||
if ('buffer' in obj){
|
||||
document.getElementById('form').style.display='block';
|
||||
document.getElementById('chat').innerHTML = '';
|
||||
|
||||
for (var i in obj.buffer) message(obj.buffer[i]);
|
||||
} else message(obj);
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1>Sample chat client</h1>
|
||||
<div id="chat"><p>Connecting...</p></div>
|
||||
<form id="form" onsubmit="send(); return false">
|
||||
<input type="text" autocomplete="off" id="text"><input type="submit" value="Send">
|
||||
</form>
|
||||
|
||||
<style>
|
||||
#chat { height: 300px; overflow: auto; width: 800px; border: 1px solid #eee; font: 13px Helvetica, Arial; }
|
||||
#chat p { padding: 8px; margin: 0; }
|
||||
#chat p:nth-child(odd) { background: #F6F6F6; }
|
||||
#form { width: 782px; background: #333; padding: 5px 10px; display: none; }
|
||||
#form input[type=text] { width: 700px; padding: 5px; background: #fff; border: 1px solid #fff; }
|
||||
#form input[type=submit] { cursor: pointer; background: #999; border: none; padding: 6px 8px; -moz-border-radius: 8px; -webkit-border-radius: 8px; margin-left: 5px; text-shadow: 0 1px 0 #fff; }
|
||||
#form input[type=submit]:hover { background: #A2A2A2; }
|
||||
#form input[type=submit]:active { position: relative; top: 2px; }
|
||||
</style>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,6 +13,8 @@
|
||||
var el = document.createElement('p');
|
||||
if ('announcement' in obj) el.innerHTML = '<em>' + esc(obj.announcement) + '</em>';
|
||||
else if ('message' in obj) el.innerHTML = '<b>' + esc(obj.message[0]) + ':</b> ' + esc(obj.message[1]);
|
||||
|
||||
if( obj.message && window.console && console.log ) console.log(obj.message[0], obj.message[1]);
|
||||
document.getElementById('chat').appendChild(el);
|
||||
document.getElementById('chat').scrollTop = 1000000;
|
||||
}
|
||||
@@ -38,11 +40,17 @@
|
||||
for (var i in obj.buffer) message(obj.buffer[i]);
|
||||
} else message(obj);
|
||||
});
|
||||
|
||||
socket.on('connect', function(){ message({ message: ['System', 'Connected']})});
|
||||
socket.on('disconnect', function(){ message({ message: ['System', 'Disconnected']})});
|
||||
socket.on('reconnect', function(){ message({ message: ['System', 'Reconnected to server']})});
|
||||
socket.on('reconnecting', function( nextRetry ){ message({ message: ['System', 'Attempting to re-connect to the server, next attempt in ' + nextRetry + 'ms']})});
|
||||
socket.on('reconnect_failed', function(){ message({ message: ['System', 'Reconnected to server FAILED.']})});
|
||||
</script>
|
||||
|
||||
<h1>Sample chat client</h1>
|
||||
<div id="chat"><p>Connecting...</p></div>
|
||||
<form id="form" onsubmit="send(); return false">
|
||||
<form id="form" onSubmit="send(); return false">
|
||||
<input type="text" autocomplete="off" id="text"><input type="submit" value="Send">
|
||||
</form>
|
||||
|
||||
|
||||
27
example/key.key
Normal file
27
example/key.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEV
|
||||
wfPQQp4XwtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+
|
||||
1FAE0c5oexPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404
|
||||
WthquTqgS7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy2
|
||||
5IyBK3QJc+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWA
|
||||
QsqW+COL0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABAoIBAGe4+9VqZfJN+dsq
|
||||
8Osyuz01uQ8OmC0sAWTIqUlQgENIyf9rCJsUBlYmwR5BT6Z69XP6QhHdpSK+TiAR
|
||||
XUz0EqG9HYzcxHIBaACP7j6iRoQ8R4kbbiWKo0z3WqQGIOqFjvD/mKEuQdE5mEYw
|
||||
eOUCG6BnX1WY2Yr8WKd2AA/tp0/Y4d8z04u9eodMpSTbHTzYMJb5SbBN1vo6FY7q
|
||||
8zSuO0BMzXlAxUsCwHsk1GQHFr8Oh3zIR7bQGtMBouI+6Lhh7sjFYsfxJboqMTBV
|
||||
IKaA216M6ggHG7MU1/jeKcMGDmEfqQLQoyWp29rMK6TklUgipME2L3UD7vTyAVzz
|
||||
xbVOpZkCgYEA8CXW4sZBBrSSrLR5SB+Ubu9qNTggLowOsC/kVKB2WJ4+xooc5HQo
|
||||
mFhq1v/WxPQoWIxdYsfg2odlL+JclK5Qcy6vXmRSdAQ5lK9gBDKxZSYc3NwAw2HA
|
||||
zyHCTK+I0n8PBYQ+yGcrxu0WqTGnlLW+Otk4CejO34WlgHwbH9bbY5UCgYEA3ZvT
|
||||
C4+OoMHXlmICSt29zUrYiL33IWsR3/MaONxTEDuvgkOSXXQOl/8Ebd6Nu+3WbsSN
|
||||
bjiPC/JyL1YCVmijdvFpl4gjtgvfJifs4G+QHvO6YfsYoVANk4u6g6rUuBIOwNK4
|
||||
RwYxwDc0oysp+g7tPxoSgDHReEVKJNzGBe9NGGsCgYEA4O4QP4gCEA3B9BF2J5+s
|
||||
n9uPVxmiyvZUK6Iv8zP4pThTBBMIzNIf09G9AHPQ7djikU2nioY8jXKTzC3xGTHM
|
||||
GJZ5m6fLsu7iH+nDvSreDSeNkTBfZqGAvoGYQ8uGE+L+ZuRfCcXYsxIOT5s6o4c3
|
||||
Dle2rVFpsuKzCY00urW796ECgYBn3go75+xEwrYGQSer6WR1nTgCV29GVYXKPooy
|
||||
zmmMOT1Yw80NSkEw0pFD4cTyqVYREsTrPU0mn1sPfrOXxnGfZSVFpcR/Je9QVfQ7
|
||||
eW7GYxwfom335aqHVj10SxRqteP+UoWWnHujCPz94VRKZMakBddYCIGSan+G6YdS
|
||||
7sdmwwKBgBc2qj0wvGXDF2kCLwSGfWoMf8CS1+5fIiUIdT1e/+7MfDdbmLMIFVjF
|
||||
QKS3zVViXCbrG5SY6wS9hxoc57f6E2A8vcaX6zy2xkZlGHQCpWRtEM5R01OWJQaH
|
||||
HsHMmQZGUQVoDm1oRkDhrTFK4K3ukc3rAxzeTZ96utOQN8/KJsTv
|
||||
-----END RSA PRIVATE KEY-----
|
||||
66
example/server-ssl.js
Normal file
66
example/server-ssl.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Important note: this application is not suitable for benchmarks!
|
||||
*/
|
||||
|
||||
var https = require('https')
|
||||
, url = require('url')
|
||||
, fs = require('fs')
|
||||
, io = require('../')
|
||||
, sys = require(process.binding('natives').util ? 'util' : 'sys')
|
||||
, server;
|
||||
|
||||
server = https.createServer({
|
||||
key: fs.readFileSync(__dirname + '/key.key')
|
||||
, cert: fs.readFileSync(__dirname + '/cert.crt')
|
||||
}, function(req, res){
|
||||
// your normal server code
|
||||
var path = url.parse(req.url).pathname;
|
||||
switch (path){
|
||||
case '/':
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.write('<h1>Welcome. Try the <a href="/chat-ssl.html">SSL Chat</a> example.</h1>');
|
||||
res.end();
|
||||
break;
|
||||
|
||||
case '/json.js':
|
||||
case '/chat-ssl.html':
|
||||
fs.readFile(__dirname + path, function(err, data){
|
||||
if (err) return send404(res);
|
||||
res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'})
|
||||
res.write(data, 'utf8');
|
||||
res.end();
|
||||
});
|
||||
break;
|
||||
|
||||
default: send404(res);
|
||||
}
|
||||
}),
|
||||
|
||||
send404 = function(res){
|
||||
res.writeHead(404);
|
||||
res.write('404');
|
||||
res.end();
|
||||
};
|
||||
|
||||
server.listen(443);
|
||||
|
||||
// socket.io, I choose you
|
||||
// simplest chat application evar
|
||||
var io = io.listen(server)
|
||||
, buffer = [];
|
||||
|
||||
io.on('connection', function(client){
|
||||
client.send({ buffer: buffer });
|
||||
client.broadcast({ announcement: client.sessionId + ' connected' });
|
||||
|
||||
client.on('message', function(message){
|
||||
var msg = { message: [client.sessionId, message] };
|
||||
buffer.push(msg);
|
||||
if (buffer.length > 15) buffer.shift();
|
||||
client.broadcast(msg);
|
||||
});
|
||||
|
||||
client.on('disconnect', function(){
|
||||
client.broadcast({ announcement: client.sessionId + ' disconnected' });
|
||||
});
|
||||
});
|
||||
@@ -48,7 +48,11 @@ Client.prototype._onMessage = function(data){
|
||||
case '~h~':
|
||||
return this._onHeartbeat(messages[i].substr(3));
|
||||
case '~j~':
|
||||
messages[i] = JSON.parse(messages[i].substr(3));
|
||||
try {
|
||||
messages[i] = JSON.parse(messages[i].substr(3));
|
||||
} catch(e) {
|
||||
messages[i] = {};
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.emit('message', messages[i]);
|
||||
@@ -57,31 +61,44 @@ Client.prototype._onMessage = function(data){
|
||||
};
|
||||
|
||||
Client.prototype._onConnect = function(req, res){
|
||||
var self = this;
|
||||
|
||||
var self = this
|
||||
, attachConnection = !this.connection;
|
||||
|
||||
this.request = req;
|
||||
this.response = res;
|
||||
this.connection = req.connection;
|
||||
|
||||
if(!attachConnection) attachConnection = !attachConnection && this.connection.eventsAttached === undefined;
|
||||
this.connection.eventsAttached = true;
|
||||
|
||||
this.connection.addListener('end', function(){
|
||||
self._onClose();
|
||||
});
|
||||
if (attachConnection){
|
||||
function destroyConnection(){
|
||||
self._onClose();
|
||||
self.connection && self.connection.destroy()
|
||||
};
|
||||
this.connection.addListener('end', destroyConnection);
|
||||
this.connection.addListener('timeout', destroyConnection);
|
||||
this.connection.addListener('error', destroyConnection);
|
||||
}
|
||||
|
||||
if (req){
|
||||
req.addListener('error', function(err){
|
||||
req.end && req.end() || req.destroy && req.destroy();
|
||||
});
|
||||
if (res) res.addListener('error', function(err){
|
||||
res.end && res.end() || res.destroy && res.destroy();
|
||||
});
|
||||
req.connection.addListener('error', function(err){
|
||||
req.connection.end && req.connection.end() || req.connection.destroy && req.connection.destroy();
|
||||
});
|
||||
|
||||
function destroyRequest(){
|
||||
req.destroy && req.destroy();
|
||||
};
|
||||
req.addListener('error', destroyRequest);
|
||||
req.addListener('timeout', destroyRequest);
|
||||
if (res){
|
||||
function destroyResponse(){
|
||||
res.destroy && res.destroy();
|
||||
}
|
||||
res.addListener('error', destroyResponse);
|
||||
res.addListener('timeout', destroyResponse);
|
||||
}
|
||||
if (this._disconnectTimeout) clearTimeout(this._disconnectTimeout);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Client.prototype._payload = function(){
|
||||
var payload = [];
|
||||
|
||||
@@ -121,15 +138,11 @@ Client.prototype._onHeartbeat = function(h){
|
||||
};
|
||||
|
||||
Client.prototype._onClose = function(skipDisconnect){
|
||||
if (!this._open) return this;
|
||||
var self = this;
|
||||
if (this._heartbeatInterval) clearTimeout(this._heartbeatInterval);
|
||||
if (this._heartbeatTimeout) clearTimeout(this._heartbeatTimeout);
|
||||
this._open = false;
|
||||
if (this.connection){
|
||||
this.connection.end();
|
||||
this.connection.destroy();
|
||||
this.connection = null;
|
||||
}
|
||||
this.request = null;
|
||||
this.response = null;
|
||||
if (skipDisconnect !== false){
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
exports.Listener = require('./listener');
|
||||
|
||||
/**
|
||||
* Listener creation shorcut
|
||||
*
|
||||
* @param {Server} node HTTP server
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.listen = function(server, options){
|
||||
return new exports.Listener(server, options);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener constructor
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.Listener = require('./listener');
|
||||
|
||||
/**
|
||||
* Version
|
||||
*/
|
||||
|
||||
exports.version = '0.6.18';
|
||||
|
||||
@@ -21,7 +21,9 @@ Flashsocket.init = function(listener){
|
||||
if (listeners.length === 0 && netserver){
|
||||
try {
|
||||
netserver.close();
|
||||
} catch(e){}
|
||||
} catch(e){
|
||||
listener.options.log('flashsocket netserver close error - ' + e.stack)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -42,7 +44,7 @@ Flashsocket.init = function(listener){
|
||||
netserver.listen(843);
|
||||
} catch(e){
|
||||
if (e.errno == 13)
|
||||
listener.options.log('Your node instance does not have root privileges.'
|
||||
listener.options.log('Your node instance does not have root privileges. '
|
||||
+ 'This means that the flash XML policy file will be '
|
||||
+ 'served inline instead of on port 843. This will slow '
|
||||
+ 'down initial connections slightly.');
|
||||
|
||||
@@ -32,7 +32,9 @@ HTMLFile.prototype._onConnect = function(req, res){
|
||||
try {
|
||||
var msg = qs.parse(body);
|
||||
self._onMessage(msg.data);
|
||||
} catch(e){}
|
||||
} catch(e){
|
||||
self.listener.options.log('htmlfile message handler error - ' + e.stack);
|
||||
}
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write('ok');
|
||||
res.end();
|
||||
|
||||
@@ -33,7 +33,7 @@ WebSocket.prototype._onConnect = function(req, socket){
|
||||
}
|
||||
|
||||
var origin = this.request.headers.origin,
|
||||
location = (origin && origin.substr(0, 5) == 'https' ? 'wss' : 'ws')
|
||||
location = (this.request.socket.encrypted ? 'wss' : 'ws')
|
||||
+ '://' + this.request.headers.host + this.request.url;
|
||||
|
||||
this.waitingForNonce = false;
|
||||
@@ -78,14 +78,14 @@ WebSocket.prototype._onConnect = function(req, socket){
|
||||
|
||||
try {
|
||||
this.connection.write(headers.concat('', '').join('\r\n'));
|
||||
this.connection.setTimeout(0);
|
||||
this.connection.setNoDelay(true);
|
||||
this.connection.setEncoding('utf-8');
|
||||
} catch(e){
|
||||
this._onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
this.connection.setTimeout(0);
|
||||
this.connection.setNoDelay(true);
|
||||
this.connection.setEncoding('utf-8');
|
||||
|
||||
if (this.waitingForNonce) {
|
||||
// Since we will be receiving the binary nonce through the normal HTTP
|
||||
// data event, set the connection to 'binary' temporarily
|
||||
@@ -99,18 +99,16 @@ WebSocket.prototype._onConnect = function(req, socket){
|
||||
this.buffer = "";
|
||||
|
||||
this.connection.addListener('data', function(data){
|
||||
self.buffer += data;
|
||||
if (self.waitingForNonce) {
|
||||
self.buffer += data;
|
||||
|
||||
if (self.buffer.length < 8) { return; }
|
||||
// Restore the connection to utf8 encoding after receiving the nonce
|
||||
self.connection.setEncoding('utf8');
|
||||
self.waitingForNonce = false;
|
||||
// Stuff the nonce into the location where it's expected to be
|
||||
self.upgradeHead = self.buffer.substr(0,8);
|
||||
self.buffer = self.buffer.substr(8);
|
||||
if (self.buffer.length > 0) {
|
||||
self.parser.add(self.buffer);
|
||||
}
|
||||
self.buffer = '';
|
||||
if (self._proveReception(self._headers)) { self._payload(); }
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,9 @@ Multipart.prototype._onConnect = function(req, res){
|
||||
try {
|
||||
var msg = qs.parse(body);
|
||||
self._onMessage(msg.data);
|
||||
} catch(e){}
|
||||
} catch(e){
|
||||
self.listener.options.log('xhr-multipart message handler error - ' + e.stack);
|
||||
}
|
||||
res.writeHead(200, headers);
|
||||
res.write('ok');
|
||||
res.end();
|
||||
|
||||
@@ -12,13 +12,12 @@ Polling.prototype.getOptions = function(){
|
||||
return {
|
||||
timeout: null, // no heartbeats
|
||||
closeTimeout: 8000,
|
||||
duration: 50000
|
||||
duration: 20000
|
||||
};
|
||||
};
|
||||
|
||||
Polling.prototype._onConnect = function(req, res){
|
||||
var self = this, body = '';
|
||||
|
||||
switch (req.method){
|
||||
case 'GET':
|
||||
Client.prototype._onConnect.apply(this, [req, res]);
|
||||
@@ -49,7 +48,9 @@ Polling.prototype._onConnect = function(req, res){
|
||||
// optimization: just strip first 5 characters here?
|
||||
var msg = qs.parse(body);
|
||||
self._onMessage(msg.data);
|
||||
} catch(e){}
|
||||
} catch(e){
|
||||
self.listener.options.log('xhr-polling message handler error - ' + e.stack);
|
||||
}
|
||||
res.writeHead(200, headers);
|
||||
res.write('ok');
|
||||
res.end();
|
||||
@@ -68,7 +69,7 @@ Polling.prototype._write = function(message){
|
||||
var headers = {'Content-Type': 'text/plain; charset=UTF-8', 'Content-Length': Buffer.byteLength(message)};
|
||||
// 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.headers.origin;
|
||||
headers['Access-Control-Allow-Origin'] = (this.request.headers.origin === 'null' ? '*' : this.request.headers.origin);
|
||||
if (this.request.headers.cookie) headers['Access-Control-Allow-Credentials'] = 'true';
|
||||
}
|
||||
this.response.writeHead(200, headers);
|
||||
|
||||
34
package.json
34
package.json
@@ -1,17 +1,21 @@
|
||||
{ "name" : "socket.io"
|
||||
, "description" : "The cross-browser WebSocket"
|
||||
, "version" : "0.6.3"
|
||||
, "author" : "LearnBoost"
|
||||
, "licenses" :
|
||||
[ { "type" : "MIT"
|
||||
, "url" : "http://github.com/learnboost/Socket.IO-node/raw/master/README.md"
|
||||
{
|
||||
"name": "socket.io"
|
||||
, "description": "The cross-browser WebSocket"
|
||||
, "version": "0.6.18"
|
||||
, "author": "Guillermo Rauch <guillermo@learnboost.com>"
|
||||
, "contributors": [
|
||||
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
|
||||
, { "name": "Arnout Kazemier", "email": "info@3rd-eden.com" }
|
||||
]
|
||||
, "licenses": [{
|
||||
"type": "MIT"
|
||||
, "url": "http://github.com/learnboost/Socket.IO-node/raw/master/README.md"
|
||||
}]
|
||||
, "repository": {
|
||||
"type": "git"
|
||||
, "url": "http://github.com/learnboost/Socket.IO-node.git"
|
||||
}
|
||||
]
|
||||
, "repository" :
|
||||
{ "type" : "git"
|
||||
, "url" : "http://github.com/learnboost/Socket.IO-node.git"
|
||||
}
|
||||
, "engine" : [ "node >=0.1.102" ]
|
||||
, "main" : "./index"
|
||||
, "scripts" : { "test" : "make test" }
|
||||
, "engine": [ "node >=0.1.102" ]
|
||||
, "main": "./index"
|
||||
, "scripts": { "test" : "make test" }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,35 @@
|
||||
|
||||
0.7.2 / 2010-12-29
|
||||
==================
|
||||
|
||||
* Fixed problem with `listen()` sometimes firing on the same tick [guillermo]
|
||||
|
||||
0.7.1 / 2010-12-28
|
||||
==================
|
||||
|
||||
* Fixed `assert.request()` client logic into an issue() function, fired upon the `listen()` callback if the server doesn't have an assigned fd. [guillermo]
|
||||
* Removed `--watch`
|
||||
|
||||
0.7.0 / 2010-11-19
|
||||
==================
|
||||
|
||||
* Removed `assert` from test function signature
|
||||
Just use `require('assert')` :) this will make integration
|
||||
with libraries like [should](http://github.com/visionmedia/should) cleaner.
|
||||
|
||||
0.6.4 / 2010-11-02
|
||||
==================
|
||||
|
||||
* Added regexp support to `assert.response()` headers
|
||||
* Removed `waitForExit` code, causing issues
|
||||
|
||||
0.6.3 / 2010-11-02
|
||||
==================
|
||||
|
||||
* Added `assert.response()` body RegExp support
|
||||
* Fixed issue with _--serial_ not executing files sequentially. Closes #42
|
||||
* Fixed hang when modules use `setInterval` - monitor running tests & force the process to quit after all have completed + timeout [Steve Mason]
|
||||
|
||||
0.6.2 / 2010-09-17
|
||||
==================
|
||||
|
||||
|
||||
@@ -35,5 +35,27 @@ Install via npm:
|
||||
|
||||
$ npm install expresso
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*!
|
||||
/*
|
||||
* Expresso
|
||||
* Copyright(c) TJ Holowaychuk <tj@vision-media.ca>
|
||||
* (MIT Licensed)
|
||||
@@ -23,7 +23,7 @@ var assert = require('assert'),
|
||||
* Expresso version.
|
||||
*/
|
||||
|
||||
var version = '0.6.2';
|
||||
var version = '0.7.2';
|
||||
|
||||
/**
|
||||
* Failure count.
|
||||
@@ -62,12 +62,6 @@ var growl = false;
|
||||
|
||||
var port = 5555;
|
||||
|
||||
/**
|
||||
* Watch mode.
|
||||
*/
|
||||
|
||||
var watch = false;
|
||||
|
||||
/**
|
||||
* Execute serially.
|
||||
*/
|
||||
@@ -80,6 +74,12 @@ var serial = false;
|
||||
|
||||
var timeout = 2000;
|
||||
|
||||
/**
|
||||
* Quiet output.
|
||||
*/
|
||||
|
||||
var quiet = false;
|
||||
|
||||
/**
|
||||
* Usage documentation.
|
||||
*/
|
||||
@@ -88,9 +88,9 @@ var usage = ''
|
||||
+ '[bold]{Usage}: expresso [options] <file ...>'
|
||||
+ '\n'
|
||||
+ '\n[bold]{Options}:'
|
||||
+ '\n -w, --watch Watch for modifications and re-execute tests'
|
||||
+ '\n -g, --growl Enable growl notifications'
|
||||
+ '\n -c, --coverage Generate and report test coverage'
|
||||
+ '\n -q, --quiet Suppress coverage report if 100%'
|
||||
+ '\n -t, --timeout MS Timeout in milliseconds, defaults to 2000'
|
||||
+ '\n -r, --require PATH Require the given module path'
|
||||
+ '\n -o, --only TESTS Execute only the comma sperated TESTS (can be set several times)'
|
||||
@@ -171,14 +171,14 @@ while (args.length) {
|
||||
run(files);
|
||||
})
|
||||
break;
|
||||
case '-q':
|
||||
case '--quiet':
|
||||
quiet = true;
|
||||
break;
|
||||
case '-b':
|
||||
case '--boring':
|
||||
boring = true;
|
||||
break;
|
||||
case '-w':
|
||||
case '--watch':
|
||||
watch = true;
|
||||
break;
|
||||
case '-g':
|
||||
case '--growl':
|
||||
growl = true;
|
||||
@@ -339,6 +339,27 @@ assert.length = function(val, n, msg) {
|
||||
*/
|
||||
|
||||
assert.response = function(server, req, res, msg){
|
||||
// Check that the server is ready or defer
|
||||
if (!server.fd) {
|
||||
if (!('__deferred' in server)) {
|
||||
server.__deferred = [];
|
||||
}
|
||||
server.__deferred.push(arguments);
|
||||
if (!server.__started) {
|
||||
server.listen(server.__port = port++, '127.0.0.1', function(){
|
||||
if (server.__deferred) {
|
||||
process.nextTick(function(){
|
||||
server.__deferred.forEach(function(args){
|
||||
assert.response.apply(assert, args);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
server.__started = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Callback as third or fourth arg
|
||||
var callback = typeof res === 'function'
|
||||
? res
|
||||
@@ -357,84 +378,92 @@ assert.response = function(server, req, res, msg){
|
||||
|
||||
// Create client
|
||||
if (!server.fd) {
|
||||
server.listen(server.__port = port++, '127.0.0.1');
|
||||
server.client = http.createClient(server.__port);
|
||||
server.listen(server.__port = port++, '127.0.0.1', issue);
|
||||
} else {
|
||||
issue();
|
||||
}
|
||||
|
||||
// Issue request
|
||||
var timer,
|
||||
client = server.client,
|
||||
method = req.method || 'GET',
|
||||
status = res.status || res.statusCode,
|
||||
data = req.data || req.body,
|
||||
timeout = req.timeout || 0;
|
||||
function issue(){
|
||||
if (!server.client)
|
||||
server.client = http.createClient(server.__port);
|
||||
|
||||
var request = client.request(method, req.url, req.headers);
|
||||
// Issue request
|
||||
var timer,
|
||||
client = server.client,
|
||||
method = req.method || 'GET',
|
||||
status = res.status || res.statusCode,
|
||||
data = req.data || req.body,
|
||||
requestTimeout = req.timeout || 0;
|
||||
|
||||
// Timeout
|
||||
if (timeout) {
|
||||
timer = setTimeout(function(){
|
||||
--server.__pending || server.close();
|
||||
delete req.timeout;
|
||||
assert.fail(msg + 'Request timed out after ' + timeout + 'ms.');
|
||||
}, timeout);
|
||||
}
|
||||
var request = client.request(method, req.url, req.headers);
|
||||
|
||||
if (data) request.write(data);
|
||||
request.addListener('response', function(response){
|
||||
response.body = '';
|
||||
response.setEncoding('utf8');
|
||||
response.addListener('data', function(chunk){ response.body += chunk; });
|
||||
response.addListener('end', function(){
|
||||
--server.__pending || server.close();
|
||||
if (timer) clearTimeout(timer);
|
||||
// Timeout
|
||||
if (requestTimeout) {
|
||||
timer = setTimeout(function(){
|
||||
--server.__pending || server.close();
|
||||
delete req.timeout;
|
||||
assert.fail(msg + 'Request timed out after ' + requestTimeout + 'ms.');
|
||||
}, requestTimeout);
|
||||
}
|
||||
|
||||
// Assert response body
|
||||
if (res.body !== undefined) {
|
||||
var eql = res.body instanceof RegExp
|
||||
? res.body.test(response.body)
|
||||
: res.body === response.body;
|
||||
assert.ok(
|
||||
eql,
|
||||
msg + 'Invalid response body.\n'
|
||||
+ ' Expected: ' + sys.inspect(res.body) + '\n'
|
||||
+ ' Got: ' + sys.inspect(response.body)
|
||||
);
|
||||
}
|
||||
if (data) request.write(data);
|
||||
request.on('response', function(response){
|
||||
response.body = '';
|
||||
response.setEncoding('utf8');
|
||||
response.on('data', function(chunk){ response.body += chunk; });
|
||||
response.on('end', function(){
|
||||
--server.__pending || server.close();
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Assert response status
|
||||
if (typeof status === 'number') {
|
||||
assert.equal(
|
||||
response.statusCode,
|
||||
status,
|
||||
msg + colorize('Invalid response status code.\n'
|
||||
+ ' Expected: [green]{' + status + '}\n'
|
||||
+ ' Got: [red]{' + response.statusCode + '}')
|
||||
);
|
||||
}
|
||||
|
||||
// Assert response headers
|
||||
if (res.headers) {
|
||||
var keys = Object.keys(res.headers);
|
||||
for (var i = 0, len = keys.length; i < len; ++i) {
|
||||
var name = keys[i],
|
||||
actual = response.headers[name.toLowerCase()],
|
||||
expected = res.headers[name];
|
||||
assert.equal(
|
||||
actual,
|
||||
expected,
|
||||
msg + colorize('Invalid response header [bold]{' + name + '}.\n'
|
||||
+ ' Expected: [green]{' + expected + '}\n'
|
||||
+ ' Got: [red]{' + actual + '}')
|
||||
// Assert response body
|
||||
if (res.body !== undefined) {
|
||||
var eql = res.body instanceof RegExp
|
||||
? res.body.test(response.body)
|
||||
: res.body === response.body;
|
||||
assert.ok(
|
||||
eql,
|
||||
msg + 'Invalid response body.\n'
|
||||
+ ' Expected: ' + sys.inspect(res.body) + '\n'
|
||||
+ ' Got: ' + sys.inspect(response.body)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback
|
||||
callback(response);
|
||||
// Assert response status
|
||||
if (typeof status === 'number') {
|
||||
assert.equal(
|
||||
response.statusCode,
|
||||
status,
|
||||
msg + colorize('Invalid response status code.\n'
|
||||
+ ' Expected: [green]{' + status + '}\n'
|
||||
+ ' Got: [red]{' + response.statusCode + '}')
|
||||
);
|
||||
}
|
||||
|
||||
// Assert response headers
|
||||
if (res.headers) {
|
||||
var keys = Object.keys(res.headers);
|
||||
for (var i = 0, len = keys.length; i < len; ++i) {
|
||||
var name = keys[i],
|
||||
actual = response.headers[name.toLowerCase()],
|
||||
expected = res.headers[name],
|
||||
eql = expected instanceof RegExp
|
||||
? expected.test(actual)
|
||||
: expected == actual;
|
||||
assert.ok(
|
||||
eql,
|
||||
msg + colorize('Invalid response header [bold]{' + name + '}.\n'
|
||||
+ ' Expected: [green]{' + expected + '}\n'
|
||||
+ ' Got: [red]{' + actual + '}')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback
|
||||
callback(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
request.end();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -476,7 +505,6 @@ function rpad(str, width) {
|
||||
*/
|
||||
|
||||
function reportCoverage(cov) {
|
||||
populateCoverage(cov);
|
||||
// Stats
|
||||
print('\n [bold]{Test Coverage}\n');
|
||||
var sep = ' +------------------------------------------+----------+------+------+--------+',
|
||||
@@ -507,9 +535,11 @@ function reportCoverage(cov) {
|
||||
for (var name in cov) {
|
||||
if (name.match(/\.js$/)) {
|
||||
var file = cov[name];
|
||||
print('\n [bold]{' + name + '}:');
|
||||
print(file.source);
|
||||
sys.print('\n');
|
||||
if ((file.coverage < 100) || !quiet) {
|
||||
print('\n [bold]{' + name + '}:');
|
||||
print(file.source);
|
||||
sys.print('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,6 +604,25 @@ function coverage(data, val) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if all files have 100% coverage
|
||||
*
|
||||
* @param {Object} cov
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
function hasFullCoverage(cov) {
|
||||
for (var name in cov) {
|
||||
var file = cov[name];
|
||||
if (file instanceof Array) {
|
||||
if (file.coverage !== 100) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the given test `files`, or try _test/*_.
|
||||
*
|
||||
@@ -581,6 +630,7 @@ function coverage(data, val) {
|
||||
*/
|
||||
|
||||
function run(files) {
|
||||
cursor(false);
|
||||
if (!files.length) {
|
||||
try {
|
||||
files = fs.readdirSync('test').map(function(file){
|
||||
@@ -592,7 +642,6 @@ function run(files) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
if (watch) watchFiles(files);
|
||||
runFiles(files);
|
||||
}
|
||||
|
||||
@@ -617,16 +666,25 @@ function cursor(show) {
|
||||
*/
|
||||
|
||||
function runFiles(files) {
|
||||
files.forEach(runFile);
|
||||
if (serial) {
|
||||
(function next(){
|
||||
if (files.length) {
|
||||
runFile(files.shift(), next);
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
files.forEach(runFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run tests for the given `file`.
|
||||
* Run tests for the given `file`, callback `fn()` when finished.
|
||||
*
|
||||
* @param {String} file
|
||||
* @param {Function} fn
|
||||
*/
|
||||
|
||||
function runFile(file) {
|
||||
function runFile(file, fn) {
|
||||
if (file.match(/\.js$/)) {
|
||||
var title = path.basename(file),
|
||||
file = path.join(cwd, file),
|
||||
@@ -634,7 +692,7 @@ function runFile(file) {
|
||||
(function check(){
|
||||
var len = Object.keys(mod).length;
|
||||
if (len) {
|
||||
runSuite(title, mod);
|
||||
runSuite(title, mod, fn);
|
||||
} else {
|
||||
setTimeout(check, 20);
|
||||
}
|
||||
@@ -642,49 +700,6 @@ function runFile(file) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the module cache for the given `file`.
|
||||
*
|
||||
* @param {String} file
|
||||
*/
|
||||
|
||||
function clearCache(file) {
|
||||
var keys = Object.keys(module.moduleCache);
|
||||
for (var i = 0, len = keys.length; i < len; ++i) {
|
||||
var key = keys[i];
|
||||
if (key.indexOf(file) === key.length - file.length) {
|
||||
delete module.moduleCache[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch the given `files` for changes.
|
||||
*
|
||||
* @param {Array} files
|
||||
*/
|
||||
|
||||
function watchFiles(files) {
|
||||
var p = 0,
|
||||
c = ['▫ ', '▫▫ ', '▫▫▫ ', ' ▫▫▫',
|
||||
' ▫▫', ' ▫', ' ▫', ' ▫▫',
|
||||
'▫▫▫ ', '▫▫ ', '▫ '],
|
||||
l = c.length;
|
||||
cursor(false);
|
||||
setInterval(function(){
|
||||
sys.print(colorize(' [green]{' + c[p++ % l] + '} watching\r'));
|
||||
}, 100);
|
||||
files.forEach(function(file){
|
||||
fs.watchFile(file, { interval: 100 }, function(curr, prev){
|
||||
if (curr.mtime > prev.mtime) {
|
||||
print(' [yellow]{◦} ' + file);
|
||||
clearCache(file);
|
||||
runFile(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report `err` for the given `test` and `suite`.
|
||||
*
|
||||
@@ -696,23 +711,23 @@ function watchFiles(files) {
|
||||
function error(suite, test, err) {
|
||||
++failures;
|
||||
var name = err.name,
|
||||
stack = err.stack.replace(err.name, ''),
|
||||
stack = err.stack ? err.stack.replace(err.name, '') : '',
|
||||
label = test === 'uncaught'
|
||||
? test
|
||||
: suite + ' ' + test;
|
||||
print('\n [bold]{' + label + '}: [red]{' + name + '}' + stack + '\n');
|
||||
if (watch) notify(label + ' failed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the given tests.
|
||||
* Run the given tests, callback `fn()` when finished.
|
||||
*
|
||||
* @param {String} title
|
||||
* @param {Object} tests
|
||||
* @param {Function} fn
|
||||
*/
|
||||
|
||||
var dots = 0;
|
||||
function runSuite(title, tests) {
|
||||
function runSuite(title, tests, fn) {
|
||||
// Keys
|
||||
var keys = only.length
|
||||
? only.slice(0)
|
||||
@@ -720,7 +735,7 @@ function runSuite(title, tests) {
|
||||
|
||||
// Setup
|
||||
var setup = tests.setup || function(fn){ fn(); };
|
||||
|
||||
|
||||
// Iterate tests
|
||||
(function next(){
|
||||
if (keys.length) {
|
||||
@@ -735,27 +750,25 @@ function runSuite(title, tests) {
|
||||
++testcount;
|
||||
assert.testTitle = key;
|
||||
if (serial) {
|
||||
if (!watch) {
|
||||
sys.print('.');
|
||||
if (++dots % 25 === 0) sys.print('\n');
|
||||
}
|
||||
sys.print('.');
|
||||
if (++dots % 25 === 0) sys.print('\n');
|
||||
setup(function(){
|
||||
if (test.length < 2) {
|
||||
test(assert);
|
||||
if (test.length < 1) {
|
||||
test();
|
||||
next();
|
||||
} else {
|
||||
var id = setTimeout(function(){
|
||||
throw new Error("'" + key + "' timed out");
|
||||
}, timeout);
|
||||
test(assert, function(){
|
||||
test(function(){
|
||||
clearTimeout(id);
|
||||
next();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
test(assert, function(fn){
|
||||
process.addListener('beforeExit', function(){
|
||||
test(function(fn){
|
||||
process.on('beforeExit', function(){
|
||||
try {
|
||||
fn();
|
||||
} catch (err) {
|
||||
@@ -769,6 +782,8 @@ function runSuite(title, tests) {
|
||||
}
|
||||
}
|
||||
if (!serial) next();
|
||||
} else if (serial) {
|
||||
fn();
|
||||
}
|
||||
})();
|
||||
}
|
||||
@@ -778,6 +793,7 @@ function runSuite(title, tests) {
|
||||
*/
|
||||
|
||||
function report() {
|
||||
cursor(true);
|
||||
process.emit('beforeExit');
|
||||
if (failures) {
|
||||
print('\n [bold]{Failures}: [red]{' + failures + '}\n\n');
|
||||
@@ -788,7 +804,10 @@ function report() {
|
||||
notify('100% ok');
|
||||
}
|
||||
if (typeof _$jscoverage === 'object') {
|
||||
reportCoverage(_$jscoverage);
|
||||
populateCoverage(_$jscoverage);
|
||||
if (!hasFullCoverage(_$jscoverage) || !quiet) {
|
||||
reportCoverage(_$jscoverage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,14 +825,14 @@ function notify(msg) {
|
||||
|
||||
// Report uncaught exceptions
|
||||
|
||||
process.addListener('uncaughtException', function(err){
|
||||
process.on('uncaughtException', function(err){
|
||||
error('uncaught', 'uncaught', err);
|
||||
});
|
||||
|
||||
// Show cursor
|
||||
|
||||
['INT', 'TERM', 'QUIT'].forEach(function(sig){
|
||||
process.addListener('SIG' + sig, function(){
|
||||
process.on('SIG' + sig, function(){
|
||||
cursor(true);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -113,7 +113,7 @@ code .this { color: #19469D; }</style>
|
||||
<h1>!/usr/bin/env node</h1>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code>!
|
||||
<pre><code>
|
||||
* <span class="class">Expresso</span>
|
||||
* <span class="class">Copyright</span>(<span class="variable">c</span>) <span class="class">TJ</span> <span class="class">Holowaychuk</span> &<span class="variable">lt</span>;<span class="variable">tj</span>@<span class="variable">vision</span>-<span class="variable">media</span>.<span class="variable">ca</span>&<span class="variable">gt</span>;
|
||||
* (<span class="class">MIT</span> <span class="class">Licensed</span>)
|
||||
@@ -142,7 +142,7 @@ code .this { color: #19469D; }</style>
|
||||
</p>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code><span class="keyword">var</span> <span class="variable">version</span> = <span class="string">'0.6.1'</span>;</code></pre>
|
||||
<pre><code><span class="keyword">var</span> <span class="variable">version</span> = <span class="string">'0.6.4'</span>;</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="code">
|
||||
@@ -219,6 +219,15 @@ code .this { color: #19469D; }</style>
|
||||
</tr>
|
||||
<tr class="code">
|
||||
<td class="docs">
|
||||
<p>Default timeout.
|
||||
</p>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code><span class="keyword">var</span> <span class="variable">timeout</span> = <span class="number integer">2000</span>;</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="code">
|
||||
<td class="docs">
|
||||
<p>Usage documentation.
|
||||
</p>
|
||||
</td>
|
||||
@@ -230,6 +239,7 @@ code .this { color: #19469D; }</style>
|
||||
+ <span class="string">'\n -w, --watch Watch for modifications and re-execute tests'</span>
|
||||
+ <span class="string">'\n -g, --growl Enable growl notifications'</span>
|
||||
+ <span class="string">'\n -c, --coverage Generate and report test coverage'</span>
|
||||
+ <span class="string">'\n -t, --timeout MS Timeout in milliseconds, defaults to 2000'</span>
|
||||
+ <span class="string">'\n -r, --require PATH Require the given module path'</span>
|
||||
+ <span class="string">'\n -o, --only TESTS Execute only the comma sperated TESTS (can be set several times)'</span>
|
||||
+ <span class="string">'\n -I, --include PATH Unshift the given path to require.paths'</span>
|
||||
@@ -291,6 +301,14 @@ code .this { color: #19469D; }</style>
|
||||
<span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--require requires a path'</span>);
|
||||
}
|
||||
<span class="keyword">break</span>;
|
||||
<span class="keyword">case</span> <span class="string">'-t'</span>:
|
||||
<span class="keyword">case</span> <span class="string">'--timeout'</span>:
|
||||
<span class="keyword">if</span> (<span class="variable">arg</span> = <span class="variable">args</span>.<span class="variable">shift</span>()) {
|
||||
<span class="variable">timeout</span> = <span class="variable">parseInt</span>(<span class="variable">arg</span>, <span class="number integer">10</span>);
|
||||
} <span class="keyword">else</span> {
|
||||
<span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(<span class="string">'--timeout requires an argument'</span>);
|
||||
}
|
||||
<span class="keyword">break</span>;
|
||||
<span class="keyword">case</span> <span class="string">'-c'</span>:
|
||||
<span class="keyword">case</span> <span class="string">'--cov'</span>:
|
||||
<span class="keyword">case</span> <span class="string">'--coverage'</span>:
|
||||
@@ -525,17 +543,17 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
<span class="variable">method</span> = <span class="variable">req</span>.<span class="variable">method</span> || <span class="string">'GET'</span>,
|
||||
<span class="variable">status</span> = <span class="variable">res</span>.<span class="variable">status</span> || <span class="variable">res</span>.<span class="variable">statusCode</span>,
|
||||
<span class="variable">data</span> = <span class="variable">req</span>.<span class="variable">data</span> || <span class="variable">req</span>.<span class="variable">body</span>,
|
||||
<span class="variable">timeout</span> = <span class="variable">req</span>.<span class="variable">timeout</span> || <span class="number integer">0</span>;
|
||||
<span class="variable">requestTimeout</span> = <span class="variable">req</span>.<span class="variable">timeout</span> || <span class="number integer">0</span>;
|
||||
|
||||
<span class="keyword">var</span> <span class="variable">request</span> = <span class="variable">client</span>.<span class="variable">request</span>(<span class="variable">method</span>, <span class="variable">req</span>.<span class="variable">url</span>, <span class="variable">req</span>.<span class="variable">headers</span>);
|
||||
|
||||
<span class="comment">// Timeout</span>
|
||||
<span class="keyword">if</span> (<span class="variable">timeout</span>) {
|
||||
<span class="keyword">if</span> (<span class="variable">requestTimeout</span>) {
|
||||
<span class="variable">timer</span> = <span class="variable">setTimeout</span>(<span class="keyword">function</span>(){
|
||||
--<span class="variable">server</span>.<span class="variable">__pending</span> || <span class="variable">server</span>.<span class="variable">close</span>();
|
||||
<span class="keyword">delete</span> <span class="variable">req</span>.<span class="variable">timeout</span>;
|
||||
<span class="variable">assert</span>.<span class="variable">fail</span>(<span class="variable">msg</span> + <span class="string">'Request timed out after '</span> + <span class="variable">timeout</span> + <span class="string">'ms.'</span>);
|
||||
}, <span class="variable">timeout</span>);
|
||||
<span class="variable">assert</span>.<span class="variable">fail</span>(<span class="variable">msg</span> + <span class="string">'Request timed out after '</span> + <span class="variable">requestTimeout</span> + <span class="string">'ms.'</span>);
|
||||
}, <span class="variable">requestTimeout</span>);
|
||||
}
|
||||
|
||||
<span class="keyword">if</span> (<span class="variable">data</span>) <span class="variable">request</span>.<span class="variable">write</span>(<span class="variable">data</span>);
|
||||
@@ -549,9 +567,11 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
|
||||
<span class="comment">// Assert response body</span>
|
||||
<span class="keyword">if</span> (<span class="variable">res</span>.<span class="variable">body</span> !== <span class="variable">undefined</span>) {
|
||||
<span class="variable">assert</span>.<span class="variable">equal</span>(
|
||||
<span class="variable">response</span>.<span class="variable">body</span>,
|
||||
<span class="variable">res</span>.<span class="variable">body</span>,
|
||||
<span class="keyword">var</span> <span class="variable">eql</span> = <span class="variable">res</span>.<span class="variable">body</span> <span class="variable">instanceof</span> <span class="class">RegExp</span>
|
||||
? <span class="variable">res</span>.<span class="variable">body</span>.<span class="variable">test</span>(<span class="variable">response</span>.<span class="variable">body</span>)
|
||||
: <span class="variable">res</span>.<span class="variable">body</span> === <span class="variable">response</span>.<span class="variable">body</span>;
|
||||
<span class="variable">assert</span>.<span class="variable">ok</span>(
|
||||
<span class="variable">eql</span>,
|
||||
<span class="variable">msg</span> + <span class="string">'Invalid response body.\n'</span>
|
||||
+ <span class="string">' Expected: '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">res</span>.<span class="variable">body</span>) + <span class="string">'\n'</span>
|
||||
+ <span class="string">' Got: '</span> + <span class="variable">sys</span>.<span class="variable">inspect</span>(<span class="variable">response</span>.<span class="variable">body</span>)
|
||||
@@ -575,10 +595,12 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">i</span> = <span class="number integer">0</span>, <span class="variable">len</span> = <span class="variable">keys</span>.<span class="variable">length</span>; <span class="variable">i</span> &<span class="variable">lt</span>; <span class="variable">len</span>; ++<span class="variable">i</span>) {
|
||||
<span class="keyword">var</span> <span class="variable">name</span> = <span class="variable">keys</span>[<span class="variable">i</span>],
|
||||
<span class="variable">actual</span> = <span class="variable">response</span>.<span class="variable">headers</span>[<span class="variable">name</span>.<span class="variable">toLowerCase</span>()],
|
||||
<span class="variable">expected</span> = <span class="variable">res</span>.<span class="variable">headers</span>[<span class="variable">name</span>];
|
||||
<span class="variable">assert</span>.<span class="variable">equal</span>(
|
||||
<span class="variable">actual</span>,
|
||||
<span class="variable">expected</span>,
|
||||
<span class="variable">expected</span> = <span class="variable">res</span>.<span class="variable">headers</span>[<span class="variable">name</span>],
|
||||
<span class="variable">eql</span> = <span class="variable">expected</span> <span class="variable">instanceof</span> <span class="class">RegExp</span>
|
||||
? <span class="variable">expected</span>.<span class="variable">test</span>(<span class="variable">actual</span>)
|
||||
: <span class="variable">expected</span> == <span class="variable">actual</span>;
|
||||
<span class="variable">assert</span>.<span class="variable">ok</span>(
|
||||
<span class="variable">eql</span>,
|
||||
<span class="variable">msg</span> + <span class="variable">colorize</span>(<span class="string">'Invalid response header [bold]{'</span> + <span class="variable">name</span> + <span class="string">'}.\n'</span>
|
||||
+ <span class="string">' Expected: [green]{'</span> + <span class="variable">expected</span> + <span class="string">'}\n'</span>
|
||||
+ <span class="string">' Got: [red]{'</span> + <span class="variable">actual</span> + <span class="string">'}'</span>)
|
||||
@@ -800,20 +822,28 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code><span class="keyword">function</span> <span class="variable">runFiles</span>(<span class="variable">files</span>) {
|
||||
<span class="variable">files</span>.<span class="variable">forEach</span>(<span class="variable">runFile</span>);
|
||||
<span class="keyword">if</span> (<span class="variable">serial</span>) {
|
||||
(<span class="keyword">function</span> <span class="variable">next</span>(){
|
||||
<span class="keyword">if</span> (<span class="variable">files</span>.<span class="variable">length</span>) {
|
||||
<span class="variable">runFile</span>(<span class="variable">files</span>.<span class="variable">shift</span>(), <span class="variable">next</span>);
|
||||
}
|
||||
})();
|
||||
} <span class="keyword">else</span> {
|
||||
<span class="variable">files</span>.<span class="variable">forEach</span>(<span class="variable">runFile</span>);
|
||||
}
|
||||
}</code></pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="code">
|
||||
<td class="docs">
|
||||
<p>Run tests for the given <code>file</code>.</p>
|
||||
<p>Run tests for the given <code>file</code>, callback <code>fn()</code> when finished.</p>
|
||||
|
||||
<h2></h2>
|
||||
|
||||
<ul><li><p><strong>param</strong>: <em>String</em> file</p></li></ul>
|
||||
<ul><li><p><strong>param</strong>: <em>String</em> file</p></li><li><p><strong>param</strong>: <em>Function</em> fn</p></li></ul>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code><span class="keyword">function</span> <span class="variable">runFile</span>(<span class="variable">file</span>) {
|
||||
<pre><code><span class="keyword">function</span> <span class="variable">runFile</span>(<span class="variable">file</span>, <span class="variable">fn</span>) {
|
||||
<span class="keyword">if</span> (<span class="variable">file</span>.<span class="variable">match</span>(<span class="regexp">/\.js$/</span>)) {
|
||||
<span class="keyword">var</span> <span class="variable">title</span> = <span class="variable">path</span>.<span class="variable">basename</span>(<span class="variable">file</span>),
|
||||
<span class="variable">file</span> = <span class="variable">path</span>.<span class="variable">join</span>(<span class="variable">cwd</span>, <span class="variable">file</span>),
|
||||
@@ -821,7 +851,7 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
(<span class="keyword">function</span> <span class="variable">check</span>(){
|
||||
<span class="keyword">var</span> <span class="variable">len</span> = <span class="class">Object</span>.<span class="variable">keys</span>(<span class="variable">mod</span>).<span class="variable">length</span>;
|
||||
<span class="keyword">if</span> (<span class="variable">len</span>) {
|
||||
<span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">mod</span>);
|
||||
<span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">mod</span>, <span class="variable">fn</span>);
|
||||
} <span class="keyword">else</span> {
|
||||
<span class="variable">setTimeout</span>(<span class="variable">check</span>, <span class="number integer">20</span>);
|
||||
}
|
||||
@@ -904,15 +934,15 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
</tr>
|
||||
<tr class="code">
|
||||
<td class="docs">
|
||||
<p>Run the given tests.</p>
|
||||
<p>Run the given tests, callback <code>fn()</code> when finished.</p>
|
||||
|
||||
<h2></h2>
|
||||
|
||||
<ul><li><p><strong>param</strong>: <em>String</em> title</p></li><li><p><strong>param</strong>: <em>Object</em> tests</p></li></ul>
|
||||
<ul><li><p><strong>param</strong>: <em>String</em> title</p></li><li><p><strong>param</strong>: <em>Object</em> tests</p></li><li><p><strong>param</strong>: <em>Function</em> fn</p></li></ul>
|
||||
</td>
|
||||
<td class="code">
|
||||
<pre><code><span class="keyword">var</span> <span class="variable">dots</span> = <span class="number integer">0</span>;
|
||||
<span class="keyword">function</span> <span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">tests</span>) {
|
||||
<span class="keyword">function</span> <span class="variable">runSuite</span>(<span class="variable">title</span>, <span class="variable">tests</span>, <span class="variable">fn</span>) {
|
||||
<span class="comment">// Keys</span>
|
||||
<span class="keyword">var</span> <span class="variable">keys</span> = <span class="variable">only</span>.<span class="variable">length</span>
|
||||
? <span class="variable">only</span>.<span class="variable">slice</span>(<span class="number integer">0</span>)
|
||||
@@ -920,7 +950,7 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
|
||||
<span class="comment">// Setup</span>
|
||||
<span class="keyword">var</span> <span class="variable">setup</span> = <span class="variable">tests</span>.<span class="variable">setup</span> || <span class="keyword">function</span>(<span class="variable">fn</span>){ <span class="variable">fn</span>(); };
|
||||
|
||||
|
||||
<span class="comment">// Iterate tests</span>
|
||||
(<span class="keyword">function</span> <span class="variable">next</span>(){
|
||||
<span class="keyword">if</span> (<span class="variable">keys</span>.<span class="variable">length</span>) {
|
||||
@@ -940,21 +970,21 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
<span class="keyword">if</span> (++<span class="variable">dots</span> % <span class="number integer">25</span> === <span class="number integer">0</span>) <span class="variable">sys</span>.<span class="variable">print</span>(<span class="string">'\n'</span>);
|
||||
}
|
||||
<span class="variable">setup</span>(<span class="keyword">function</span>(){
|
||||
<span class="keyword">if</span> (<span class="variable">test</span>.<span class="variable">length</span> &<span class="variable">lt</span>; <span class="number integer">2</span>) {
|
||||
<span class="variable">test</span>(<span class="variable">assert</span>);
|
||||
<span class="keyword">if</span> (<span class="variable">test</span>.<span class="variable">length</span> &<span class="variable">lt</span>; <span class="number integer">1</span>) {
|
||||
<span class="variable">test</span>();
|
||||
<span class="variable">next</span>();
|
||||
} <span class="keyword">else</span> {
|
||||
<span class="keyword">var</span> <span class="variable">id</span> = <span class="variable">setTimeout</span>(<span class="keyword">function</span>(){
|
||||
<span class="keyword">throw</span> <span class="keyword">new</span> <span class="class">Error</span>(&<span class="variable">quot</span>;<span class="string">'" + key + "'</span> <span class="variable">timed</span> <span class="variable">out</span>&<span class="variable">quot</span>;);
|
||||
}, <span class="number integer">2000</span>);
|
||||
<span class="variable">test</span>(<span class="variable">assert</span>, <span class="keyword">function</span>(){
|
||||
}, <span class="variable">timeout</span>);
|
||||
<span class="variable">test</span>(<span class="keyword">function</span>(){
|
||||
<span class="variable">clearTimeout</span>(<span class="variable">id</span>);
|
||||
<span class="variable">next</span>();
|
||||
});
|
||||
}
|
||||
});
|
||||
} <span class="keyword">else</span> {
|
||||
<span class="variable">test</span>(<span class="variable">assert</span>, <span class="keyword">function</span>(<span class="variable">fn</span>){
|
||||
<span class="variable">test</span>(<span class="keyword">function</span>(<span class="variable">fn</span>){
|
||||
<span class="variable">process</span>.<span class="variable">addListener</span>(<span class="string">'beforeExit'</span>, <span class="keyword">function</span>(){
|
||||
<span class="keyword">try</span> {
|
||||
<span class="variable">fn</span>();
|
||||
@@ -969,6 +999,8 @@ the given <code>req</code> object and <code>res</code> assertions object.</p>
|
||||
}
|
||||
}
|
||||
<span class="keyword">if</span> (!<span class="variable">serial</span>) <span class="variable">next</span>();
|
||||
} <span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">serial</span>) {
|
||||
<span class="variable">fn</span>();
|
||||
}
|
||||
})();
|
||||
}</code></pre>
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
<div id="wrapper">
|
||||
<h1>Expresso</h1>
|
||||
<div class='mp'>
|
||||
<h2 id="NAME">NAME</h2>
|
||||
<p class="man-name">
|
||||
<code>index</code>
|
||||
</p>
|
||||
<p><a href="http://github.com/visionmedia/expresso">Expresso</a> is a JavaScript <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> framework written for <a href="http://nodejs.org">nodejs</a>. Expresso is extremely fast, and is packed with features such as additional assertion methods, code coverage reporting, CI support, and more.</p>
|
||||
|
||||
<h2 id="Features">Features</h2>
|
||||
@@ -85,7 +89,7 @@ the command below, which will first compile node-jscoverage:</p>
|
||||
|
||||
<p>To define tests we simply export several functions:</p>
|
||||
|
||||
<pre><code>exports['test String#length'] = function(assert){
|
||||
<pre><code>exports['test String#length'] = function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
};
|
||||
</code></pre>
|
||||
@@ -95,7 +99,7 @@ export your own object containing the tests, however this
|
||||
is essentially the as above:</p>
|
||||
|
||||
<pre><code>module.exports = {
|
||||
'test String#length': function(assert){
|
||||
'test String#length': function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
}
|
||||
};
|
||||
@@ -103,16 +107,16 @@ is essentially the as above:</p>
|
||||
|
||||
<p>If you prefer not to use quoted keys:</p>
|
||||
|
||||
<pre><code>exports.testsStringLength = function(assert){
|
||||
<pre><code>exports.testsStringLength = function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
};
|
||||
</code></pre>
|
||||
|
||||
<p>The second argument passed to each callback is <em>beforeExit</em>,
|
||||
<p>The argument passed to each callback is <em>beforeExit</em>,
|
||||
which is typically used to assert that callbacks have been
|
||||
invoked.</p>
|
||||
|
||||
<pre><code>exports.testAsync = function(assert, beforeExit){
|
||||
<pre><code>exports.testAsync = function(beforeExit){
|
||||
var n = 0;
|
||||
setTimeout(function(){
|
||||
++n;
|
||||
@@ -229,9 +233,9 @@ which is then used to perform several assertions
|
||||
on the response with the following properties:</p>
|
||||
|
||||
<ul>
|
||||
<li><em>body</em> assert response body</li>
|
||||
<li><em>body</em> assert response body (regexp or string)</li>
|
||||
<li><em>status</em> assert response status code</li>
|
||||
<li><em>header</em> assert that all given headers match (unspecified are ignored)</li>
|
||||
<li><em>header</em> assert that all given headers match (unspecified are ignored, use a regexp or string)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -361,7 +365,7 @@ future <code>--cov</code> will most likely accept a path.</p>
|
||||
<p>Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the <em>exports.foo = function(){};</em> syntax is supported for this:</p>
|
||||
|
||||
<pre><code>setTimeout(function(){
|
||||
exports['test async exports'] = function(assert){
|
||||
exports['test async exports'] = function(){
|
||||
assert.ok('wahoo');
|
||||
};
|
||||
}, 100);
|
||||
|
||||
@@ -37,7 +37,7 @@ Install via npm:
|
||||
|
||||
To define tests we simply export several functions:
|
||||
|
||||
exports['test String#length'] = function(assert){
|
||||
exports['test String#length'] = function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
};
|
||||
|
||||
@@ -46,22 +46,22 @@ export your own object containing the tests, however this
|
||||
is essentially the as above:
|
||||
|
||||
module.exports = {
|
||||
'test String#length': function(assert){
|
||||
'test String#length': function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
}
|
||||
};
|
||||
|
||||
If you prefer not to use quoted keys:
|
||||
|
||||
exports.testsStringLength = function(assert){
|
||||
exports.testsStringLength = function(){
|
||||
assert.equal(6, 'foobar'.length);
|
||||
};
|
||||
|
||||
The second argument passed to each callback is _beforeExit_,
|
||||
The argument passed to each callback is _beforeExit_,
|
||||
which is typically used to assert that callbacks have been
|
||||
invoked.
|
||||
|
||||
exports.testAsync = function(assert, beforeExit){
|
||||
exports.testAsync = function(beforeExit){
|
||||
var n = 0;
|
||||
setTimeout(function(){
|
||||
++n;
|
||||
@@ -164,9 +164,9 @@ receives the response for assertions, or an object
|
||||
which is then used to perform several assertions
|
||||
on the response with the following properties:
|
||||
|
||||
- _body_ assert response body
|
||||
- _body_ assert response body (regexp or string)
|
||||
- _status_ assert response status code
|
||||
- _header_ assert that all given headers match (unspecified are ignored)
|
||||
- _header_ assert that all given headers match (unspecified are ignored, use a regexp or string)
|
||||
|
||||
When providing _res_ you may then also pass a callback function
|
||||
as the fourth argument for additional assertions.
|
||||
@@ -284,7 +284,7 @@ future `--cov` will most likely accept a path.
|
||||
Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the _exports.foo = function(){};_ syntax is supported for this:
|
||||
|
||||
setTimeout(function(){
|
||||
exports['test async exports'] = function(assert){
|
||||
exports['test async exports'] = function(){
|
||||
assert.ok('wahoo');
|
||||
};
|
||||
}, 100);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{ "name": "expresso",
|
||||
"version": "0.6.2",
|
||||
"version": "0.7.2",
|
||||
"description": "TDD framework, light-weight, fast, CI-friendly",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"bin": {
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
module.exports = {
|
||||
'assert.eql()': function(assert){
|
||||
'assert.eql()': function(){
|
||||
assert.equal(assert.deepEqual, assert.eql);
|
||||
},
|
||||
|
||||
'assert.type()': function(assert){
|
||||
'assert.type()': function(){
|
||||
assert.type('foobar', 'string');
|
||||
assert.type(2, 'number');
|
||||
assert.throws(function(){
|
||||
@@ -11,7 +18,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.includes()': function(assert){
|
||||
'assert.includes()': function(){
|
||||
assert.includes('some random string', 'dom');
|
||||
assert.throws(function(){
|
||||
assert.include('some random string', 'foobar');
|
||||
@@ -31,7 +38,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.isNull()': function(assert){
|
||||
'assert.isNull()': function(){
|
||||
assert.isNull(null);
|
||||
assert.throws(function(){
|
||||
assert.isNull(undefined);
|
||||
@@ -41,7 +48,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.isUndefined()': function(assert){
|
||||
'assert.isUndefined()': function(){
|
||||
assert.isUndefined(undefined);
|
||||
assert.throws(function(){
|
||||
assert.isUndefined(null);
|
||||
@@ -51,7 +58,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.isNotNull()': function(assert){
|
||||
'assert.isNotNull()': function(){
|
||||
assert.isNotNull(false);
|
||||
assert.isNotNull(undefined);
|
||||
assert.throws(function(){
|
||||
@@ -59,7 +66,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.isDefined()': function(assert){
|
||||
'assert.isDefined()': function(){
|
||||
assert.isDefined(false);
|
||||
assert.isDefined(null);
|
||||
assert.throws(function(){
|
||||
@@ -67,14 +74,14 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
'assert.match()': function(assert){
|
||||
'assert.match()': function(){
|
||||
assert.match('foobar', /foo(bar)?/);
|
||||
assert.throws(function(){
|
||||
assert.match('something', /rawr/);
|
||||
});
|
||||
},
|
||||
|
||||
'assert.length()': function(assert){
|
||||
'assert.length()': function(){
|
||||
assert.length('test', 4);
|
||||
assert.length([1,2,3,4], 4);
|
||||
assert.throws(function(){
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
setTimeout(function(){
|
||||
exports['test async exports'] = function(assert){
|
||||
exports['test async exports'] = function(){
|
||||
assert.ok('wahoo');
|
||||
};
|
||||
}, 100);
|
||||
@@ -3,10 +3,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var bar = require('bar');
|
||||
var assert = require('assert')
|
||||
, bar = require('bar');
|
||||
|
||||
module.exports = {
|
||||
'bar()': function(assert){
|
||||
'bar()': function(){
|
||||
assert.equal('bar', bar.bar());
|
||||
}
|
||||
};
|
||||
@@ -3,10 +3,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var foo = require('foo');
|
||||
var assert = require('assert')
|
||||
, foo = require('foo');
|
||||
|
||||
module.exports = {
|
||||
'foo()': function(assert){
|
||||
'foo()': function(){
|
||||
assert.equal('foo', foo.foo());
|
||||
assert.equal('foo', foo.foo());
|
||||
}
|
||||
|
||||
@@ -3,80 +3,144 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
var assert = require('assert')
|
||||
, http = require('http');
|
||||
|
||||
var server = http.createServer(function(req, res){
|
||||
if (req.method === 'GET') {
|
||||
if (req.url === '/delay') {
|
||||
setTimeout(function(){
|
||||
res.writeHead(200, {});
|
||||
res.end('delayed');
|
||||
}, 200);
|
||||
} else {
|
||||
var body = JSON.stringify({ name: 'tj' });
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json; charset=utf8',
|
||||
'Content-Length': body.length
|
||||
});
|
||||
res.end(body);
|
||||
}
|
||||
if (req.method === 'GET') {
|
||||
if (req.url === '/delay') {
|
||||
setTimeout(function(){
|
||||
res.writeHead(200, {});
|
||||
res.end('delayed');
|
||||
}, 200);
|
||||
} else {
|
||||
var body = '';
|
||||
req.setEncoding('utf8');
|
||||
req.addListener('data', function(chunk){ body += chunk });
|
||||
req.addListener('end', function(){
|
||||
res.writeHead(200, {});
|
||||
res.end(req.url + ' ' + body);
|
||||
});
|
||||
var body = JSON.stringify({ name: 'tj' });
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json; charset=utf8',
|
||||
'Content-Length': body.length
|
||||
});
|
||||
res.end(body);
|
||||
}
|
||||
} else {
|
||||
var body = '';
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', function(chunk){ body += chunk });
|
||||
req.on('end', function(){
|
||||
res.writeHead(200, {});
|
||||
res.end(req.url + ' ' + body);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
'test assert.response()': function(assert, beforeExit){
|
||||
var called = 0;
|
||||
var delayedServer = http.createServer(function(req, res){
|
||||
res.writeHead(200);
|
||||
res.end('it worked');
|
||||
});
|
||||
|
||||
assert.response(server, {
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
},{
|
||||
body: '{"name":"tj"}',
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf8'
|
||||
}
|
||||
});
|
||||
|
||||
assert.response(server, {
|
||||
url: '/foo',
|
||||
method: 'POST',
|
||||
data: 'bar baz'
|
||||
},{
|
||||
body: '/foo bar baz',
|
||||
status: 200
|
||||
}, function(res){
|
||||
++called;
|
||||
assert.ok(res);
|
||||
});
|
||||
|
||||
assert.response(server, {
|
||||
url: '/foo'
|
||||
}, function(res){
|
||||
++called;
|
||||
assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback');
|
||||
});
|
||||
|
||||
assert.response(server,
|
||||
{ url: '/delay', timeout: 300 },
|
||||
{ body: 'delayed' });
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(2, called);
|
||||
});
|
||||
},
|
||||
var oldListen = delayedServer.listen;
|
||||
delayedServer.listen = function(){
|
||||
var args = arguments;
|
||||
setTimeout(function(){
|
||||
oldListen.apply(delayedServer, args);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'test assert.response(req, res, fn)': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(server, {
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
},{
|
||||
body: '{"name":"tj"}',
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf8'
|
||||
}
|
||||
}, function(res){
|
||||
++calls;
|
||||
assert.ok(res);
|
||||
});
|
||||
|
||||
'test assert.response() regexp': function(assert){
|
||||
assert.response(server,
|
||||
{ url: '/foo', method: 'POST', data: 'foobar' },
|
||||
{ body: /^\/foo foo(bar)?/ });
|
||||
}
|
||||
};
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
})
|
||||
},
|
||||
|
||||
'test assert.response(req, fn)': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(server, {
|
||||
url: '/foo'
|
||||
}, function(res){
|
||||
++calls;
|
||||
assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback');
|
||||
});
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
});
|
||||
},
|
||||
|
||||
'test assert.response() delay': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(server,
|
||||
{ url: '/delay', timeout: 1500 },
|
||||
{ body: 'delayed' },
|
||||
function(){
|
||||
++calls;
|
||||
});
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
});
|
||||
},
|
||||
|
||||
'test assert.response() regexp': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(server,
|
||||
{ url: '/foo', method: 'POST', data: 'foobar' },
|
||||
{ body: /^\/foo foo(bar)?/ },
|
||||
function(){
|
||||
++calls;
|
||||
});
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
});
|
||||
},
|
||||
|
||||
'test assert.response() regexp headers': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(server,
|
||||
{ url: '/' },
|
||||
{ body: '{"name":"tj"}', headers: { 'Content-Type': /^application\/json/ } },
|
||||
function(){
|
||||
++calls;
|
||||
});
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
});
|
||||
},
|
||||
|
||||
// [!] if this test doesn't pass, an uncaught ECONNREFUSED will display
|
||||
'test assert.response() with deferred listen()': function(beforeExit){
|
||||
var calls = 0;
|
||||
|
||||
assert.response(delayedServer,
|
||||
{ url: '/' },
|
||||
{ body: 'it worked' },
|
||||
function(){
|
||||
++calls;
|
||||
});
|
||||
|
||||
beforeExit(function(){
|
||||
assert.equal(1, calls);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
var setup = 0,
|
||||
order = [];
|
||||
var assert = require('assert')
|
||||
, setup = 0
|
||||
, order = [];
|
||||
|
||||
module.exports = {
|
||||
setup: function(done){
|
||||
@@ -8,7 +9,7 @@ module.exports = {
|
||||
done();
|
||||
},
|
||||
|
||||
a: function(assert, done){
|
||||
a: function(done){
|
||||
assert.equal(1, setup);
|
||||
order.push('a');
|
||||
setTimeout(function(){
|
||||
@@ -16,7 +17,7 @@ module.exports = {
|
||||
}, 500);
|
||||
},
|
||||
|
||||
b: function(assert, done){
|
||||
b: function(done){
|
||||
assert.equal(2, setup);
|
||||
order.push('b');
|
||||
setTimeout(function(){
|
||||
@@ -24,7 +25,7 @@ module.exports = {
|
||||
}, 200);
|
||||
},
|
||||
|
||||
c: function(assert, done){
|
||||
c: function(done){
|
||||
assert.equal(3, setup);
|
||||
order.push('c');
|
||||
setTimeout(function(){
|
||||
@@ -32,7 +33,7 @@ module.exports = {
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
d: function(assert){
|
||||
d: function(){
|
||||
assert.eql(order, ['a', 'b', 'c']);
|
||||
}
|
||||
};
|
||||
@@ -3,7 +3,8 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
var assert = require('assert')
|
||||
, http = require('http');
|
||||
|
||||
var server = http.createServer(function(req, res){
|
||||
if (req.method === 'GET') {
|
||||
@@ -32,7 +33,7 @@ var server = http.createServer(function(req, res){
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
'test assert.response()': function(assert, done){
|
||||
'test assert.response()': function(done){
|
||||
assert.response(server, {
|
||||
url: '/',
|
||||
method: 'GET'
|
||||
|
||||
3
support/socket.io-client/.gitmodules
vendored
3
support/socket.io-client/.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "lib/vendor/web-socket-js"]
|
||||
path = lib/vendor/web-socket-js
|
||||
url = git://github.com/gimite/web-socket-js.git
|
||||
21
support/socket.io-client/History.md
Normal file
21
support/socket.io-client/History.md
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
0.7.0 / 2011-??-??
|
||||
==================
|
||||
|
||||
* Fixed JSONP interaction with jQuery. [saschagehlich]
|
||||
* Fixed; different port now considered cross-domain.
|
||||
* Added compatibility for inclusion in non-browser environments.
|
||||
* Added package.json.
|
||||
* Added noConflict support. [kreichgauer]
|
||||
* Added reconnection support with exponential backoff. [3rd-Eden]
|
||||
|
||||
0.6.2 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Fixed problem with xhr-multipart buffering
|
||||
* Updated Flash websocket transport
|
||||
* Fixed tryTransportsOnConnectTimeout option
|
||||
* Added 'connect_failed' event after the last available transport fails to connect
|
||||
within the timeout
|
||||
* Add 'connecting' event emit on each connection attempt.
|
||||
|
||||
@@ -46,14 +46,6 @@ The `socket.io` client is basically a simple HTTP Socket interface implementatio
|
||||
- Easy to use! See [socket.io-node](http://github.com/LearnBoost/Socket.IO-node) for the server to connect to.
|
||||
|
||||
### How to use
|
||||
|
||||
The recommended way of including the Socket.IO client is through the Socket.IO CDN:
|
||||
|
||||
In your <head>
|
||||
|
||||
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
|
||||
|
||||
Then, in your code
|
||||
|
||||
socket = new io.Socket('localhost');
|
||||
socket.connect();
|
||||
@@ -72,27 +64,25 @@ If you are serving you .swf from a other domain than socket.io.js you will need
|
||||
|
||||
The insecure version can be found [here](http://github.com/gimite/web-socket-js/blob/master/WebSocketMainInsecure.zip).
|
||||
|
||||
IMPORTANT! When checking out the git repo, make sure to include the submodules. One way to do it is:
|
||||
|
||||
git clone [repo] --recursive
|
||||
|
||||
Another, once cloned
|
||||
|
||||
git submodule update --init --recursive
|
||||
|
||||
### Documentation
|
||||
|
||||
#### io.Socket
|
||||
|
||||
new io.Socket(host, [options]);
|
||||
|
||||
Options:
|
||||
##### Options:
|
||||
|
||||
- *secure*
|
||||
|
||||
false
|
||||
|
||||
Use secure connections
|
||||
|
||||
- *port*
|
||||
|
||||
Current port or 80
|
||||
|
||||
The port `socket.io` server is attached to (defaults to the document.location port)
|
||||
The port `socket.io` server is attached to (defaults to the document.location port).
|
||||
|
||||
- *resource*
|
||||
|
||||
@@ -102,9 +92,9 @@ Options:
|
||||
|
||||
- *transports*
|
||||
|
||||
['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling']
|
||||
['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling']
|
||||
|
||||
A list of the transports to attempt to utilize (in order of preference)
|
||||
A list of the transports to attempt to utilize (in order of preference).
|
||||
|
||||
- *transportOptions*
|
||||
|
||||
@@ -117,11 +107,48 @@ Options:
|
||||
|
||||
An object containing (optional) options to pass to each transport.
|
||||
|
||||
Properties:
|
||||
- *rememberTransport*
|
||||
|
||||
true
|
||||
|
||||
A boolean indicating if the utilized transport should be remembered in a cookie.
|
||||
|
||||
- *connectTimeout*
|
||||
|
||||
5000
|
||||
|
||||
The amount of miliseconds a transport has to create a connection before we consider it timed out.
|
||||
|
||||
- *tryTransportsOnConnectTimeout*
|
||||
|
||||
true
|
||||
|
||||
A boolean indicating if we should try other transports when the connectTimeout occurs.
|
||||
|
||||
- *reconnect*
|
||||
|
||||
true
|
||||
|
||||
A boolean indicating if we should automatically reconnect if a connection is disconnected.
|
||||
|
||||
- *reconnectionDelay*
|
||||
|
||||
500
|
||||
|
||||
The amount of milliseconds before we try to connect to the server again. We are using a exponential back off algorithm for the following reconnections, on each reconnect attempt this value will get multiplied (500 > 1000 > 2000 > 4000 > 8000).
|
||||
|
||||
|
||||
- *maxReconnectionAttempts*
|
||||
|
||||
10
|
||||
|
||||
The amount of attempts should we make using the current transport to connect to the server? After this we will do one final attempt, and re-try with all enabled transport methods before we give up.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- *options*
|
||||
|
||||
The passed in options combined with the defaults
|
||||
The passed in options combined with the defaults.
|
||||
|
||||
- *connected*
|
||||
|
||||
@@ -130,16 +157,20 @@ Properties:
|
||||
- *connecting*
|
||||
|
||||
Whether the socket is connecting or not.
|
||||
|
||||
- *reconnecting*
|
||||
|
||||
Whether we are reconnecting or not.
|
||||
|
||||
- *transport*
|
||||
|
||||
The transport instance.
|
||||
|
||||
Methods:
|
||||
##### Methods:
|
||||
|
||||
- *connect*
|
||||
- *connect(λ)*
|
||||
|
||||
Establishes a connection
|
||||
Establishes a connection. If λ is supplied as argument, it will be called once the connection is established.
|
||||
|
||||
- *send(message)*
|
||||
|
||||
@@ -147,21 +178,36 @@ Methods:
|
||||
|
||||
- *disconnect*
|
||||
|
||||
Closes the connection
|
||||
Closes the connection.
|
||||
|
||||
- *on(event, λ)*
|
||||
|
||||
Adds a listener for the event *event*
|
||||
Adds a listener for the event *event*.
|
||||
|
||||
- *once(event, λ)*
|
||||
|
||||
Adds a one time listener for the event *event*. The listener is removed after the first time the event is fired.
|
||||
|
||||
- *removeEvent(event, λ)*
|
||||
|
||||
Removes the listener λ for the event *event*
|
||||
Removes the listener λ for the event *event*.
|
||||
|
||||
Events:
|
||||
##### Events:
|
||||
|
||||
- *connect*
|
||||
|
||||
Fired when the connection is established and the handshake successful
|
||||
Fired when the connection is established and the handshake successful.
|
||||
|
||||
- *connecting(transport_type)*
|
||||
|
||||
Fired when a connection is attempted, passing the transport name.
|
||||
|
||||
- *connect_failed*
|
||||
|
||||
Fired when the connection timeout occurs after the last connection attempt.
|
||||
This only fires if the `connectTimeout` option is set.
|
||||
If the `tryTransportsOnConnectTimeout` option is set, this only fires once all
|
||||
possible transports have been tried.
|
||||
|
||||
- *message(message)*
|
||||
|
||||
@@ -174,19 +220,25 @@ Events:
|
||||
- *disconnect*
|
||||
|
||||
Fired when the connection is considered disconnected.
|
||||
|
||||
- *reconnect(transport_type,reconnectionAttempts)*
|
||||
|
||||
### Changelog
|
||||
Fired when the connection has been re-established. This only fires if the `reconnect` option is set.
|
||||
|
||||
2010 08 02 - **0.5.4** (9.95KB)
|
||||
- *reconnecting(reconnectionDelay,reconnectionAttempts)*
|
||||
|
||||
* Added io.util.load as a reusable onload handler
|
||||
* Added io.util.ios which reports if the UA is running on iPhone or iPad
|
||||
* No more loading bar on iPhone: XHR-Polling now connects `onload` for the iOS WebKit, and waits 10 ms to launch the initial connection.
|
||||
Fired when a reconnection is attempted, passing the next delay for the next reconnection.
|
||||
|
||||
### Credits
|
||||
- *reconnect_failed*
|
||||
|
||||
Fired when all reconnection attempts have failed and we where unsuccessful in reconnecting to the server.
|
||||
|
||||
### Contributors
|
||||
|
||||
Guillermo Rauch <guillermo@learnboost.com>
|
||||
|
||||
Arnout Kazemier <info@3rd-eden.com>
|
||||
|
||||
### License
|
||||
|
||||
(The MIT License)
|
||||
@@ -210,4 +262,4 @@ 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.
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
sys = require('sys'),
|
||||
socket = require('../lib/io'),
|
||||
jsp = require('../lib/vendor/uglifyjs/lib/parse-js'),
|
||||
pro = require("../lib/vendor/uglifyjs/lib/process"),
|
||||
ast,
|
||||
files = [
|
||||
'io.js',
|
||||
'util.js',
|
||||
@@ -29,22 +31,29 @@ var fs = require('fs'),
|
||||
'transports/jsonp-polling.js',
|
||||
'socket.js',
|
||||
'vendor/web-socket-js/swfobject.js',
|
||||
'vendor/web-socket-js/FABridge.js',
|
||||
'vendor/web-socket-js/web_socket.js'
|
||||
],
|
||||
content = "/** Socket.IO "+ socket.io.version +" - Built with build.js */\n";
|
||||
content = "/** Socket.IO "+ socket.io.version +" - Built with build.js */\n",
|
||||
license = "/* Socket.IO.min "+ socket.io.version +" @author Guillermo Rauch <guillermo@learnboost.com>, @license The MIT license., @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com> */\n";
|
||||
|
||||
sys.log('Reading files…');
|
||||
console.log('Reading files…');
|
||||
|
||||
files.forEach(function(file){
|
||||
var path = __dirname + '/../lib/' + file;
|
||||
sys.log (' + ' + path);
|
||||
console.log(' + ' + path);
|
||||
content += fs.readFileSync(path) + "\n";
|
||||
});
|
||||
|
||||
sys.log('Generating…');
|
||||
console.log('Generating…');
|
||||
|
||||
fs.write(fs.openSync(__dirname + '/../socket.io.js', 'w'), content, 0, 'utf8');
|
||||
sys.log(' + ' + __dirname + '/../socket.io.js');
|
||||
console.log(' + ' + __dirname + '/../socket.io.js');
|
||||
|
||||
sys.log('All done!');
|
||||
console.log('Uglyfying…');
|
||||
ast = jsp.parse(content);
|
||||
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
||||
ast = pro.ast_squeeze(ast);
|
||||
fs.write(fs.openSync(__dirname + '/../socket.io.min.js', 'w'), license + pro.gen_code(ast), 0, 'utf8');
|
||||
console.log(' + ' + __dirname + '/../socket.io.min.js');
|
||||
|
||||
console.log('All done!');
|
||||
@@ -1,23 +1,44 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
this.io = {
|
||||
version: '0.6.1',
|
||||
|
||||
setPath: function(path){
|
||||
if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf');
|
||||
this.path = /\/$/.test(path) ? path : path + '/';
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
var io = this.io = {
|
||||
|
||||
/**
|
||||
* Library version.
|
||||
*/
|
||||
version: '0.6.3',
|
||||
|
||||
/**
|
||||
* Updates the location of the WebSocketMain.swf file that is required for the Flashsocket transport.
|
||||
* This should only be needed if you want to load in the WebSocketMainInsecure.swf or if you want to
|
||||
* host the .swf file on a other server.
|
||||
*
|
||||
* @static
|
||||
* @deprecated Set the variable `WEB_SOCKET_SWF_LOCATION` pointing to WebSocketMain.swf
|
||||
* @param {String} path The path of the .swf file
|
||||
* @api public
|
||||
*/
|
||||
setPath: function(path){
|
||||
if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf');
|
||||
this.path = /\/$/.test(path) ? path : path + '/';
|
||||
WEB_SOCKET_SWF_LOCATION = path + 'lib/vendor/web-socket-js/WebSocketMain.swf';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose Socket.IO in jQuery
|
||||
*/
|
||||
if ('jQuery' in this) jQuery.io = this.io;
|
||||
|
||||
/**
|
||||
* Default path to the .swf file.
|
||||
*/
|
||||
if (typeof window != 'undefined'){
|
||||
// WEB_SOCKET_SWF_LOCATION = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//cdn.socket.io/' + this.io.version + '/WebSocketMain.swf';
|
||||
if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined')
|
||||
|
||||
@@ -1,161 +1,462 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var Socket = io.Socket = function(host, options){
|
||||
this.host = host || document.domain;
|
||||
this.options = {
|
||||
secure: false,
|
||||
document: document,
|
||||
port: document.location.port || 80,
|
||||
resource: 'socket.io',
|
||||
transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling'],
|
||||
transportOptions: {
|
||||
'xhr-polling': {
|
||||
timeout: 25000 // based on polling duration default
|
||||
},
|
||||
'jsonp-polling': {
|
||||
timeout: 25000
|
||||
}
|
||||
},
|
||||
connectTimeout: 5000,
|
||||
tryTransportsOnConnectTimeout: true,
|
||||
rememberTransport: true
|
||||
};
|
||||
io.util.merge(this.options, options);
|
||||
this.connected = false;
|
||||
this.connecting = false;
|
||||
this._events = {};
|
||||
this.transport = this.getTransport();
|
||||
if (!this.transport && 'console' in window) console.error('No transport available');
|
||||
};
|
||||
|
||||
Socket.prototype.getTransport = function(override){
|
||||
var transports = override || this.options.transports, match;
|
||||
if (this.options.rememberTransport && !override){
|
||||
match = this.options.document.cookie.match('(?:^|;)\\s*socketio=([^;]*)');
|
||||
if (match){
|
||||
this._rememberedTransport = true;
|
||||
transports = [decodeURIComponent(match[1])];
|
||||
}
|
||||
}
|
||||
for (var i = 0, transport; transport = transports[i]; i++){
|
||||
if (io.Transport[transport]
|
||||
&& io.Transport[transport].check()
|
||||
&& (!this._isXDomain() || io.Transport[transport].xdomainCheck())){
|
||||
return new io.Transport[transport](this, this.options.transportOptions[transport] || {});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Socket.prototype.connect = function(){
|
||||
if (this.transport && !this.connected){
|
||||
if (this.connecting) this.disconnect();
|
||||
this.connecting = true;
|
||||
this.transport.connect();
|
||||
if (this.options.connectTimeout){
|
||||
var self = this;
|
||||
this.connectTimeoutTimer = setTimeout(function(){
|
||||
if (!self.connected){
|
||||
self.disconnect();
|
||||
if (self.options.tryTransportsOnConnectTimeout && !self._rememberedTransport){
|
||||
var remainingTransports = [], transports = self.options.transports;
|
||||
for (var i = 0, transport; transport = transports[i]; i++){
|
||||
if (transport != self.transport.type) remainingTransports.push(transport);
|
||||
}
|
||||
if (remainingTransports.length){
|
||||
self.transport = self.getTransport(remainingTransports);
|
||||
self.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.options.connectTimeout);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype.send = function(data){
|
||||
if (!this.transport || !this.transport.connected) return this._queue(data);
|
||||
this.transport.send(data);
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype.disconnect = function(){
|
||||
var io = this.io;
|
||||
|
||||
/**
|
||||
* Create a new `Socket.IO client` which can establish a persisted
|
||||
* connection with a Socket.IO enabled server.
|
||||
*
|
||||
* Options:
|
||||
* - `secure` Use secure connections, defaulting to false.
|
||||
* - `document` Reference to the document object to retrieve and set cookies, defaulting to document.
|
||||
* - `port` The port where the Socket.IO server listening on, defaulting to location.port.
|
||||
* - `resource` The path or namespace on the server where the Socket.IO requests are intercepted, defaulting to 'socket.io'.
|
||||
* - `transports` A ordered list with the available transports, defaulting to all transports.
|
||||
* - `transportOption` A {Object} containing the options for each transport. The key of the object should reflect
|
||||
* name of the transport and the value a {Object} with the options.
|
||||
* - `connectTimeout` The duration in milliseconds that a transport has to establish a working connection, defaulting to 5000.
|
||||
* - `tryTransportsOnConnectTimeout` Should we attempt other transport methods when the connectTimeout occurs, defaulting to true.
|
||||
* - `reconnect` Should reconnection happen automatically, defaulting to true.
|
||||
* - `reconnectionDelay` The delay in milliseconds before we attempt to establish a working connection. This value will
|
||||
* increase automatically using a exponential back off algorithm. Defaulting to 500.
|
||||
* - `maxReconnectionAttempts` Number of attempts we should make before seizing the reconnect operation, defaulting to 10.
|
||||
* - `rememberTransport` Should the successfully connected transport be remembered in a cookie, defaulting to true.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Create client with the default settings.
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.connect();
|
||||
* socket.on('message', function(msg){
|
||||
* console.log('Received message: ' + msg );
|
||||
* });
|
||||
* socket.on('connect', function(){
|
||||
* socket.send('Hello from client');
|
||||
* });
|
||||
*
|
||||
* Create a connection with server on a different port and host.
|
||||
*
|
||||
* var socket = new io.Socket('http://example.com',{port:1337});
|
||||
*
|
||||
* @constructor
|
||||
* @exports Socket as io.Socket
|
||||
* @param {String} [host] The host where the Socket.IO server is located, it defaults to the host that runs the page.
|
||||
* @param {Objects} [options] The options that will configure the Socket.IO client.
|
||||
* @property {String} host The supplied host arguments or the host that page runs.
|
||||
* @property {Object} options The passed options combined with the defaults.
|
||||
* @property {Boolean} connected Whether the socket is connected or not.
|
||||
* @property {Boolean} connecting Whether the socket is connecting or not.
|
||||
* @property {Boolean} reconnecting Whether the socket is reconnecting or not.
|
||||
* @property {Object} transport The selected transport instance.
|
||||
* @api public
|
||||
*/
|
||||
var Socket = io.Socket = function(host, options){
|
||||
this.host = host || document.domain;
|
||||
this.options = {
|
||||
secure: false,
|
||||
document: document,
|
||||
port: document.location.port || 80,
|
||||
resource: 'socket.io',
|
||||
transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling'],
|
||||
transportOptions: {
|
||||
'xhr-polling': {
|
||||
timeout: 25000 // based on polling duration default
|
||||
},
|
||||
'jsonp-polling': {
|
||||
timeout: 25000
|
||||
}
|
||||
},
|
||||
connectTimeout: 5000,
|
||||
tryTransportsOnConnectTimeout: true,
|
||||
reconnect: true,
|
||||
reconnectionDelay: 500,
|
||||
maxReconnectionAttempts: 10,
|
||||
rememberTransport: true
|
||||
};
|
||||
io.util.merge(this.options, options);
|
||||
this.connected = false;
|
||||
this.connecting = false;
|
||||
this.reconnecting = false;
|
||||
this.events = {};
|
||||
this.transport = this.getTransport();
|
||||
if (!this.transport && 'console' in window) console.error('No transport available');
|
||||
};
|
||||
|
||||
/**
|
||||
* Find an available transport based on the options supplied in the constructor. For example if the
|
||||
* `rememberTransport` option was set we will only connect with the previous successfully connected transport.
|
||||
* The supplied transports can be overruled if the `override` argument is supplied.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Override the existing transports.
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.getTransport(['jsonp-polling','websocket']);
|
||||
* // returns the json-polling transport because it's availabe in all browsers.
|
||||
*
|
||||
* @param {Array} [override] A ordered list with transports that should be used instead of the options.transports.
|
||||
* @returns {Null|Transport} The available transport.
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.getTransport = function(override){
|
||||
var transports = override || this.options.transports, match;
|
||||
if (this.options.rememberTransport && !override){
|
||||
match = this.options.document.cookie.match('(?:^|;)\\s*socketio=([^;]*)');
|
||||
if (match){
|
||||
this.rememberedTransport = true;
|
||||
transports = [decodeURIComponent(match[1])];
|
||||
}
|
||||
}
|
||||
for (var i = 0, transport; transport = transports[i]; i++){
|
||||
if (io.Transport[transport]
|
||||
&& io.Transport[transport].check()
|
||||
&& (!this.isXDomain() || io.Transport[transport].xdomainCheck())){
|
||||
return new io.Transport[transport](this, this.options.transportOptions[transport] || {});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Establish a new connection with the Socket.IO server. This is done using the selected transport by the
|
||||
* getTransport method. If the `connectTimeout` and the `tryTransportsOnConnectTimeout` options are set
|
||||
* the client will keep trying to connect to the server using a different transports when the timeout occurs.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* Create a Socket.IO client with a connect callback (We assume we have the WebSocket transport avaliable).
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.connect(function(transport){
|
||||
* console.log("Connected to server using the " + socket.transport.type + " transport.");
|
||||
* });
|
||||
* // => "Connected to server using the WebSocket transport."
|
||||
*
|
||||
* @param {Function} [fn] Callback.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.connect = function(fn){
|
||||
if (this.transport && !this.connected){
|
||||
if (this.connecting) this.disconnect(true);
|
||||
this.connecting = true;
|
||||
this.emit('connecting', [this.transport.type]);
|
||||
this.transport.connect();
|
||||
if (this.options.connectTimeout){
|
||||
var self = this;
|
||||
this.connectTimeoutTimer = setTimeout(function(){
|
||||
if (!self.connected){
|
||||
self.disconnect(true);
|
||||
if (self.options.tryTransportsOnConnectTimeout && !self.rememberedTransport){
|
||||
if(!self.remainingTransports) self.remainingTransports = self.options.transports.slice(0);
|
||||
var transports = self.remainingTransports;
|
||||
while(transports.length > 0 && transports.splice(0,1)[0] != self.transport.type){}
|
||||
if(transports.length){
|
||||
self.transport = self.getTransport(transports);
|
||||
self.connect();
|
||||
}
|
||||
}
|
||||
if(!self.remainingTransports || self.remainingTransports.length == 0) self.emit('connect_failed');
|
||||
}
|
||||
if(self.remainingTransports && self.remainingTransports.length == 0) delete self.remainingTransports;
|
||||
}, this.options.connectTimeout);
|
||||
}
|
||||
}
|
||||
if (fn && typeof fn == 'function') this.once('connect',fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends the data to the Socket.IO server. If there isn't a connection to the server
|
||||
* the data will be forwarded to the queue.
|
||||
*
|
||||
* @param {Mixed} data The data that needs to be send to the Socket.IO server.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.send = function(data){
|
||||
if (!this.transport || !this.transport.connected) return this.queue(data);
|
||||
this.transport.send(data);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect the established connect.
|
||||
*
|
||||
* @param {Boolean} [soft] A soft disconnect will keep the reconnect settings enabled.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.disconnect = function(soft){
|
||||
if (this.connectTimeoutTimer) clearTimeout(this.connectTimeoutTimer);
|
||||
this.transport.disconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype.on = function(name, fn){
|
||||
if (!(name in this._events)) this._events[name] = [];
|
||||
this._events[name].push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
if (!soft) this.options.reconnect = false;
|
||||
this.transport.disconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a new eventListener for the given event.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.on("connect", function(transport){
|
||||
* console.log("Connected to server using the " + socket.transport.type + " transport.");
|
||||
* });
|
||||
* // => "Connected to server using the WebSocket transport."
|
||||
*
|
||||
* @param {String} name The name of the event.
|
||||
* @param {Function} fn The function that is called once the event is emitted.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.on = function(name, fn){
|
||||
if (!(name in this.events)) this.events[name] = [];
|
||||
this.events[name].push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a one time listener, the listener will be removed after the event is emitted.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.once("custom:event", function(){
|
||||
* console.log("I should only log once.");
|
||||
* });
|
||||
* socket.emit("custom:event");
|
||||
* socket.emit("custom:event");
|
||||
* // => "I should only log once."
|
||||
*
|
||||
* @param {String} name The name of the event.
|
||||
* @param {Function} fn The function that is called once the event is emitted.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.once = function(name, fn){
|
||||
var self = this
|
||||
, once = function(){
|
||||
self.removeEvent(name, once);
|
||||
fn.apply(self, arguments);
|
||||
};
|
||||
once.ref = fn;
|
||||
self.on(name, once);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit a event to all listeners.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var socket = new io.Socket();
|
||||
* socket.on("custom:event", function(){
|
||||
* console.log("Emitted a custom:event");
|
||||
* });
|
||||
* socket.emit("custom:event");
|
||||
* // => "Emitted a custom:event"
|
||||
*
|
||||
* @param {String} name The name of the event.
|
||||
* @param {Array} args Arguments for the event.
|
||||
* @returns {io.Socket}
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.emit = function(name, args){
|
||||
if (name in this._events){
|
||||
var events = this._events[name].concat();
|
||||
if (name in this.events){
|
||||
var events = this.events[name].concat();
|
||||
for (var i = 0, ii = events.length; i < ii; i++)
|
||||
events[i].apply(this, args === undefined ? [] : args);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype.removeEvent = function(name, fn){
|
||||
if (name in this._events){
|
||||
for (var a = 0, l = this._events[name].length; a < l; a++)
|
||||
if (this._events[name][a] == fn) this._events[name].splice(a, 1);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype._queue = function(message){
|
||||
if (!('_queueStack' in this)) this._queueStack = [];
|
||||
this._queueStack.push(message);
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype._doQueue = function(){
|
||||
if (!('_queueStack' in this) || !this._queueStack.length) return this;
|
||||
this.transport.send(this._queueStack);
|
||||
this._queueStack = [];
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype._isXDomain = function(){
|
||||
return this.host !== document.domain;
|
||||
};
|
||||
|
||||
Socket.prototype._onConnect = function(){
|
||||
this.connected = true;
|
||||
this.connecting = false;
|
||||
this._doQueue();
|
||||
if (this.options.rememberTransport) this.options.document.cookie = 'socketio=' + encodeURIComponent(this.transport.type);
|
||||
this.emit('connect');
|
||||
};
|
||||
|
||||
Socket.prototype._onMessage = function(data){
|
||||
this.emit('message', [data]);
|
||||
};
|
||||
|
||||
Socket.prototype._onDisconnect = function(){
|
||||
var wasConnected = this.connected;
|
||||
this.connected = false;
|
||||
this.connecting = false;
|
||||
this._queueStack = [];
|
||||
if (wasConnected) this.emit('disconnect');
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a event listener from the listener array for the specified event.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var socket = new io.Socket()
|
||||
* , event = function(){};
|
||||
* socket.on("connect", event);
|
||||
* socket.removeEvent("connect", event);
|
||||
*
|
||||
* @param {String} name The name of the event.
|
||||
* @param {Function} fn The function that is called once the event is emitted.
|
||||
* @returns {io.Socket}
|
||||
* @api public
|
||||
*/
|
||||
Socket.prototype.removeEvent = function(name, fn){
|
||||
if (name in this.events){
|
||||
for (var a = 0, l = this.events[name].length; a < l; a++)
|
||||
if (this.events[name][a] == fn || this.events[name][a].ref && this.events[name][a].ref == fn) this.events[name].splice(a, 1);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queues messages when there isn't a active connection available. Once a connection has been
|
||||
* established you should call the `doQueue` method to send the queued messages to the server.
|
||||
*
|
||||
* @param {Mixed} message The message that was originally send to the `send` method.
|
||||
* @returns {io.Socket}
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.queue = function(message){
|
||||
if (!('queueStack' in this)) this.queueStack = [];
|
||||
this.queueStack.push(message);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* If there are queued messages we send all messages to the Socket.IO server and empty
|
||||
* the queue.
|
||||
*
|
||||
* @returns {io.Socket}
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.doQueue = function(){
|
||||
if (!('queueStack' in this) || !this.queueStack.length) return this;
|
||||
this.transport.send(this.queueStack);
|
||||
this.queueStack = [];
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we need to use cross domain enabled transports. Cross domain would
|
||||
* be a different port or different domain name.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.isXDomain = function(){
|
||||
var locPort = window.location.port || 80;
|
||||
return this.host !== document.domain || this.options.port != locPort;
|
||||
};
|
||||
|
||||
/**
|
||||
* When the transport established an working connection the Socket.IO server it notifies us
|
||||
* by calling this method so we can set the `connected` and `connecting` properties and emit
|
||||
* the connection event.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.onConnect = function(){
|
||||
this.connected = true;
|
||||
this.connecting = false;
|
||||
this.doQueue();
|
||||
if (this.options.rememberTransport) this.options.document.cookie = 'socketio=' + encodeURIComponent(this.transport.type);
|
||||
this.emit('connect');
|
||||
};
|
||||
|
||||
/**
|
||||
* When the transport receives new messages from the Socket.IO server it notifies us by calling
|
||||
* this method with the decoded `data` it received.
|
||||
*
|
||||
* @param data The message from the Socket.IO server.
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.onMessage = function(data){
|
||||
this.emit('message', [data]);
|
||||
};
|
||||
|
||||
/**
|
||||
* When the transport is disconnected from the Socket.IO server it notifies us by calling
|
||||
* this method. If we where connected and the `reconnect` is set we will attempt to reconnect.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.onDisconnect = function(){
|
||||
var wasConnected = this.connected;
|
||||
this.connected = false;
|
||||
this.connecting = false;
|
||||
this.queueStack = [];
|
||||
if (wasConnected){
|
||||
this.emit('disconnect');
|
||||
if (this.options.reconnect && !this.reconnecting) this.onReconnect();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The reconnection is done using an exponential back off algorithm to prevent
|
||||
* the server from being flooded with connection requests. When the transport
|
||||
* is disconnected we wait until the `reconnectionDelay` finishes. We multiply
|
||||
* the `reconnectionDelay` (if the previous `reconnectionDelay` was 500 it will
|
||||
* be updated to 1000 and than 2000>4000>8000>16000 etc.) and tell the current
|
||||
* transport to connect again. When we run out of `reconnectionAttempts` we will
|
||||
* do one final attempt and loop over all enabled transport methods to see if
|
||||
* other transports might work. If everything fails we emit the `reconnect_failed`
|
||||
* event.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Socket.prototype.onReconnect = function(){
|
||||
this.reconnecting = true;
|
||||
this.reconnectionAttempts = 0;
|
||||
this.reconnectionDelay = this.options.reconnectionDelay;
|
||||
|
||||
var self = this
|
||||
, tryTransportsOnConnectTimeout = this.options.tryTransportsOnConnectTimeout
|
||||
, rememberTransport = this.options.rememberTransport;
|
||||
|
||||
function reset(){
|
||||
if(self.connected) self.emit('reconnect',[self.transport.type,self.reconnectionAttempts]);
|
||||
self.removeEvent('connect_failed', maybeReconnect).removeEvent('connect', maybeReconnect);
|
||||
self.reconnecting = false;
|
||||
delete self.reconnectionAttempts;
|
||||
delete self.reconnectionDelay;
|
||||
delete self.reconnectionTimer;
|
||||
delete self.redoTransports;
|
||||
self.options.tryTransportsOnConnectTimeout = tryTransportsOnConnectTimeout;
|
||||
self.options.rememberTransport = rememberTransport;
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
function maybeReconnect(){
|
||||
if (!self.reconnecting) return;
|
||||
if (!self.connected){
|
||||
if (self.connecting && self.reconnecting) return self.reconnectionTimer = setTimeout(maybeReconnect, 1000);
|
||||
|
||||
if (self.reconnectionAttempts++ >= self.options.maxReconnectionAttempts){
|
||||
if (!self.redoTransports){
|
||||
self.on('connect_failed', maybeReconnect);
|
||||
self.options.tryTransportsOnConnectTimeout = true;
|
||||
self.transport = self.getTransport(self.options.transports); // override with all enabled transports
|
||||
self.redoTransports = true;
|
||||
self.connect();
|
||||
} else {
|
||||
self.emit('reconnect_failed');
|
||||
reset();
|
||||
}
|
||||
} else {
|
||||
self.reconnectionDelay *= 2; // exponential back off
|
||||
self.connect();
|
||||
self.emit('reconnecting', [self.reconnectionDelay,self.reconnectionAttempts]);
|
||||
self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay);
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
this.options.tryTransportsOnConnectTimeout = false;
|
||||
this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay);
|
||||
|
||||
this.on('connect', maybeReconnect);
|
||||
};
|
||||
|
||||
/**
|
||||
* API compatiblity
|
||||
*/
|
||||
Socket.prototype.fire = Socket.prototype.emit;
|
||||
|
||||
Socket.prototype.addListener = Socket.prototype.addEvent = Socket.prototype.addEventListener = Socket.prototype.on;
|
||||
|
||||
})();
|
||||
Socket.prototype.addListener = Socket.prototype.addEvent = Socket.prototype.addEventListener = Socket.prototype.on;
|
||||
Socket.prototype.removeListener = Socket.prototype.removeEventListener = Socket.prototype.removeEvent;
|
||||
|
||||
})();
|
||||
@@ -1,141 +1,295 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
// abstract
|
||||
|
||||
(function(){
|
||||
|
||||
var frame = '~m~',
|
||||
|
||||
stringify = function(message){
|
||||
if (Object.prototype.toString.call(message) == '[object Object]'){
|
||||
if (!('JSON' in window)){
|
||||
if ('console' in window && console.error) console.error('Trying to encode as JSON, but JSON.stringify is missing.');
|
||||
return '{ "$error": "Invalid message" }';
|
||||
}
|
||||
return '~j~' + JSON.stringify(message);
|
||||
} else {
|
||||
return String(message);
|
||||
}
|
||||
};
|
||||
|
||||
Transport = io.Transport = function(base, options){
|
||||
this.base = base;
|
||||
this.options = {
|
||||
timeout: 15000 // based on heartbeat interval default
|
||||
};
|
||||
io.util.merge(this.options, options);
|
||||
};
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* Message frame for encoding and decoding responses from the Socket.IO server.
|
||||
*
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
frame = '~m~',
|
||||
|
||||
/**
|
||||
* Transforms the message to a string. If the message is an {Object} we will convert it to
|
||||
* a string and prefix it with the `~j~` flag to indicate that message is JSON encoded.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* stringify({foo:"bar"});
|
||||
* // => "~j~{"foo":"bar"}"
|
||||
*
|
||||
* @param {String|Array|Object} message The messages that needs to be transformed to a string.
|
||||
* @throws {Error} When the JSON.stringify implementation is missing in the browser.
|
||||
* @returns {String} Message.
|
||||
* @api private
|
||||
*/
|
||||
stringify = function(message){
|
||||
if (Object.prototype.toString.call(message) == '[object Object]'){
|
||||
if (!('JSON' in window)){
|
||||
var error = 'Socket.IO Error: Trying to encode as JSON, but JSON.stringify is missing.';
|
||||
if ('console' in window && console.error){
|
||||
console.error(error);
|
||||
} else {
|
||||
throw new Error(error);
|
||||
}
|
||||
return '{ "$error": "'+ error +'" }';
|
||||
}
|
||||
return '~j~' + JSON.stringify(message);
|
||||
} else {
|
||||
return String(message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the transport template for all supported transport methods. It provides the
|
||||
* basic functionality to create a working transport for Socket.IO.
|
||||
*
|
||||
* Options:
|
||||
* - `timeout` Transport shutdown timeout in milliseconds, based on the heartbeat interval.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var transport = io.Transport.mytransport = function(){
|
||||
* io.Transport.apply(this, arguments);
|
||||
* };
|
||||
* io.util.inherit(transport, io.Transport);
|
||||
*
|
||||
* ... // more code here
|
||||
*
|
||||
* // connect with your new transport
|
||||
* var socket = new io.Socket(null,{transports:['mytransport']});
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} base The reference to io.Socket.
|
||||
* @param {Object} options The transport options.
|
||||
* @property {io.Socket|Object} base The reference to io.Socket.
|
||||
* @property {Object} options The transport options, these are used to overwrite the default options
|
||||
* @property {String} sessionid The sessionid of the established connection, this is only available a connection is established
|
||||
* @property {Boolean} connected The connection has been established.
|
||||
* @property {Boolean} connecting We are still connecting to the server.
|
||||
* @api public
|
||||
*/
|
||||
Transport = io.Transport = function(base, options){
|
||||
this.base = base;
|
||||
this.options = {
|
||||
timeout: 15000 // based on heartbeat interval default
|
||||
};
|
||||
io.util.merge(this.options, options);
|
||||
};
|
||||
|
||||
Transport.prototype.send = function(){
|
||||
throw new Error('Missing send() implementation');
|
||||
};
|
||||
/**
|
||||
* Send the message to the connected Socket.IO server.
|
||||
*
|
||||
* @throws {Error} When the io.Transport is inherited, it should override this method.
|
||||
* @api public
|
||||
*/
|
||||
Transport.prototype.send = function(){
|
||||
throw new Error('Missing send() implementation');
|
||||
};
|
||||
|
||||
/**
|
||||
* Establish a connection with the Socket.IO server..
|
||||
*
|
||||
* @throws {Error} When the io.Transport is inherited, it should override this method.
|
||||
* @api public
|
||||
*/
|
||||
Transport.prototype.connect = function(){
|
||||
throw new Error('Missing connect() implementation');
|
||||
};
|
||||
|
||||
Transport.prototype.connect = function(){
|
||||
throw new Error('Missing connect() implementation');
|
||||
};
|
||||
|
||||
Transport.prototype.disconnect = function(){
|
||||
throw new Error('Missing disconnect() implementation');
|
||||
};
|
||||
|
||||
Transport.prototype._encode = function(messages){
|
||||
var ret = '', message,
|
||||
messages = io.util.isArray(messages) ? messages : [messages];
|
||||
for (var i = 0, l = messages.length; i < l; i++){
|
||||
message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]);
|
||||
ret += frame + message.length + frame + message;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
Transport.prototype._decode = function(data){
|
||||
var messages = [], number, n;
|
||||
do {
|
||||
if (data.substr(0, 3) !== frame) return messages;
|
||||
data = data.substr(3);
|
||||
number = '', n = '';
|
||||
for (var i = 0, l = data.length; i < l; i++){
|
||||
n = Number(data.substr(i, 1));
|
||||
if (data.substr(i, 1) == n){
|
||||
number += n;
|
||||
} else {
|
||||
data = data.substr(number.length + frame.length);
|
||||
number = Number(number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
messages.push(data.substr(0, number)); // here
|
||||
data = data.substr(number);
|
||||
} while(data !== '');
|
||||
return messages;
|
||||
};
|
||||
|
||||
Transport.prototype._onData = function(data){
|
||||
this._setTimeout();
|
||||
var msgs = this._decode(data);
|
||||
if (msgs && msgs.length){
|
||||
for (var i = 0, l = msgs.length; i < l; i++){
|
||||
this._onMessage(msgs[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Transport.prototype._setTimeout = function(){
|
||||
var self = this;
|
||||
if (this._timeout) clearTimeout(this._timeout);
|
||||
this._timeout = setTimeout(function(){
|
||||
self._onTimeout();
|
||||
}, this.options.timeout);
|
||||
};
|
||||
|
||||
Transport.prototype._onTimeout = function(){
|
||||
this._onDisconnect();
|
||||
};
|
||||
|
||||
Transport.prototype._onMessage = function(message){
|
||||
if (!this.sessionid){
|
||||
this.sessionid = message;
|
||||
this._onConnect();
|
||||
} else if (message.substr(0, 3) == '~h~'){
|
||||
this._onHeartbeat(message.substr(3));
|
||||
} else if (message.substr(0, 3) == '~j~'){
|
||||
this.base._onMessage(JSON.parse(message.substr(3)));
|
||||
} else {
|
||||
this.base._onMessage(message);
|
||||
}
|
||||
},
|
||||
|
||||
Transport.prototype._onHeartbeat = function(heartbeat){
|
||||
this.send('~h~' + heartbeat); // echo
|
||||
};
|
||||
|
||||
Transport.prototype._onConnect = function(){
|
||||
this.connected = true;
|
||||
this.connecting = false;
|
||||
this.base._onConnect();
|
||||
this._setTimeout();
|
||||
};
|
||||
|
||||
Transport.prototype._onDisconnect = function(){
|
||||
this.connecting = false;
|
||||
this.connected = false;
|
||||
this.sessionid = null;
|
||||
this.base._onDisconnect();
|
||||
};
|
||||
|
||||
Transport.prototype._prepareUrl = function(){
|
||||
return (this.base.options.secure ? 'https' : 'http')
|
||||
+ '://' + this.base.host
|
||||
+ ':' + this.base.options.port
|
||||
+ '/' + this.base.options.resource
|
||||
+ '/' + this.type
|
||||
+ (this.sessionid ? ('/' + this.sessionid) : '/');
|
||||
};
|
||||
/**
|
||||
* Disconnect the established connection.
|
||||
*
|
||||
* @throws {Error} When the io.Transport is inherited, it should override this method.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.disconnect = function(){
|
||||
throw new Error('Missing disconnect() implementation');
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode the message by adding the `frame` to each message. This allows
|
||||
* the client so send multiple messages with only one request.
|
||||
*
|
||||
* @param {String|Array} messages Messages that need to be encoded.
|
||||
* @returns {String} Encoded message.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.encode = function(messages){
|
||||
var ret = '', message;
|
||||
messages = io.util.isArray(messages) ? messages : [messages];
|
||||
for (var i = 0, l = messages.length; i < l; i++){
|
||||
message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]);
|
||||
ret += frame + message.length + frame + message;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decoded the response from the Socket.IO server, as the server could send multiple
|
||||
* messages in one response.
|
||||
*
|
||||
* @param (String} data The response from the server that requires decoding
|
||||
* @returns {Array} Decoded messages.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.decode = function(data){
|
||||
var messages = [], number, n;
|
||||
do {
|
||||
if (data.substr(0, 3) !== frame) return messages;
|
||||
data = data.substr(3);
|
||||
number = '', n = '';
|
||||
for (var i = 0, l = data.length; i < l; i++){
|
||||
n = Number(data.substr(i, 1));
|
||||
if (data.substr(i, 1) == n){
|
||||
number += n;
|
||||
} else {
|
||||
data = data.substr(number.length + frame.length);
|
||||
number = Number(number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
messages.push(data.substr(0, number)); // here
|
||||
data = data.substr(number);
|
||||
} while(data !== '');
|
||||
return messages;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the response from the server. When a new response is received
|
||||
* it will automatically update the timeout, decode the message and
|
||||
* forwards the response to the onMessage function for further processing.
|
||||
*
|
||||
* @param {String} data Response from the server.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onData = function(data){
|
||||
this.setTimeout();
|
||||
var msgs = this.decode(data);
|
||||
if (msgs && msgs.length){
|
||||
for (var i = 0, l = msgs.length; i < l; i++){
|
||||
this.onMessage(msgs[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* All the transports have a dedicated timeout to detect if
|
||||
* the connection is still alive. We clear the existing timer
|
||||
* and set new one each time this function is called. When the
|
||||
* timeout does occur it will call the `onTimeout` method.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.setTimeout = function(){
|
||||
var self = this;
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(function(){
|
||||
self.onTimeout();
|
||||
}, this.options.timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect from the Socket.IO server when a timeout occurs.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onTimeout = function(){
|
||||
this.onDisconnect();
|
||||
};
|
||||
|
||||
/**
|
||||
* After the response from the server has been parsed to individual
|
||||
* messages we need to decode them using the the Socket.IO message
|
||||
* protocol: <https://github.com/learnboost/socket.io-node/>.
|
||||
*
|
||||
* When a message is received we check if a session id has been set,
|
||||
* if the session id is missing we can assume that the received message
|
||||
* contains the sessionid of the connection.
|
||||
|
||||
* When a message is prefixed with `~h~` we dispatch it our heartbeat
|
||||
* processing method `onHeartbeat` with the content of the heartbeat.
|
||||
*
|
||||
* When the message is prefixed with `~j~` we can assume that the contents
|
||||
* of the message is JSON encoded, so we parse the message and notify
|
||||
* the base of the new message.
|
||||
*
|
||||
* If none of the above, we consider it just a plain text message and
|
||||
* notify the base of the new message.
|
||||
*
|
||||
* @param {String} message A decoded message from the server.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onMessage = function(message){
|
||||
if (!this.sessionid){
|
||||
this.sessionid = message;
|
||||
this.onConnect();
|
||||
} else if (message.substr(0, 3) == '~h~'){
|
||||
this.onHeartbeat(message.substr(3));
|
||||
} else if (message.substr(0, 3) == '~j~'){
|
||||
this.base.onMessage(JSON.parse(message.substr(3)));
|
||||
} else {
|
||||
this.base.onMessage(message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the received heartbeat message back to server. So the server
|
||||
* knows we are still connected.
|
||||
*
|
||||
* @param {String} heartbeat Heartbeat response from the server.
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onHeartbeat = function(heartbeat){
|
||||
this.send('~h~' + heartbeat); // echo
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the base when a connection to the Socket.IO server has
|
||||
* been established. And it starts the connection `timeout` timer.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onConnect = function(){
|
||||
this.connected = true;
|
||||
this.connecting = false;
|
||||
this.base.onConnect();
|
||||
this.setTimeout();
|
||||
};
|
||||
|
||||
/**
|
||||
* Notifies the base when the connection with the Socket.IO server
|
||||
* has been disconnected.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.onDisconnect = function(){
|
||||
this.connecting = false;
|
||||
this.connected = false;
|
||||
this.sessionid = null;
|
||||
this.base.onDisconnect();
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a connection url based on the Socket.IO URL Protocol.
|
||||
* See <https://github.com/learnboost/socket.io-node/> for more details.
|
||||
*
|
||||
* @returns {String} Connection url
|
||||
* @api private
|
||||
*/
|
||||
Transport.prototype.prepareUrl = function(){
|
||||
return (this.base.options.secure ? 'https' : 'http')
|
||||
+ '://' + this.base.host
|
||||
+ ':' + this.base.options.port
|
||||
+ '/' + this.base.options.resource
|
||||
+ '/' + this.type
|
||||
+ (this.sessionid ? ('/' + this.sessionid) : '/');
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -1,53 +1,88 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var Flashsocket = io.Transport.flashsocket = function(){
|
||||
io.Transport.websocket.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(Flashsocket, io.Transport.websocket);
|
||||
|
||||
Flashsocket.prototype.type = 'flashsocket';
|
||||
|
||||
Flashsocket.prototype.connect = function(){
|
||||
var self = this, args = arguments;
|
||||
WebSocket.__addTask(function(){
|
||||
io.Transport.websocket.prototype.connect.apply(self, args);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
Flashsocket.prototype.send = function(){
|
||||
var self = this, args = arguments;
|
||||
WebSocket.__addTask(function(){
|
||||
io.Transport.websocket.prototype.send.apply(self, args);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
Flashsocket.check = function(){
|
||||
if (typeof WebSocket == 'undefined' || !('__addTask' in WebSocket)) return false;
|
||||
if (io.util.opera) return false; // opera is buggy with this transport
|
||||
if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']){
|
||||
return !!navigator.plugins['Shockwave Flash'].description;
|
||||
}
|
||||
if ('ActiveXObject' in window) {
|
||||
try {
|
||||
return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||
} catch (e) {}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Flashsocket.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* The Flashsocket transport. This is a API wrapper for the HTML5 WebSocket specification.
|
||||
* It uses a .swf file to communicate with the server. If you want to serve the .swf file
|
||||
* from a other server than where the Socket.IO script is coming from you need to use the
|
||||
* insecure version of the .swf. More information about this can be found on the github page.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport.websocket}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket = io.Transport.flashsocket = function(){
|
||||
io.Transport.websocket.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(Flashsocket, io.Transport.websocket);
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {String}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket.prototype.type = 'flashsocket';
|
||||
|
||||
/**
|
||||
* Disconnect the established `Flashsocket` connection. This is done by adding a new
|
||||
* task to the Flashsocket. The rest will be handled off by the `WebSocket` transport.
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket.prototype.connect = function(){
|
||||
var self = this, args = arguments;
|
||||
WebSocket.__addTask(function(){
|
||||
io.Transport.websocket.prototype.connect.apply(self, args);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a message to the Socket.IO server. This is done by adding a new
|
||||
* task to the Flashsocket. The rest will be handled off by the `WebSocket` transport.
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket.prototype.send = function(){
|
||||
var self = this, args = arguments;
|
||||
WebSocket.__addTask(function(){
|
||||
io.Transport.websocket.prototype.send.apply(self, args);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the Flashsocket transport is supported as it requires that the Adobe Flash Player
|
||||
* plugin version `10.0.0` or greater is installed. And also check if the polyfill is correctly
|
||||
* loaded.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket.check = function(){
|
||||
if (typeof WebSocket == 'undefined' || !('__addTask' in WebSocket) || !swfobject) return false;
|
||||
return swfobject.hasFlashPlayerVersion("10.0.0");
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the Flashsocket transport can be used as cross domain / cross origin transport.
|
||||
* Because we can't see which type (secure or insecure) of .swf is used we will just return true.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
Flashsocket.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -1,73 +1,138 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var HTMLFile = io.Transport.htmlfile = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(HTMLFile, io.Transport.XHR);
|
||||
|
||||
HTMLFile.prototype.type = 'htmlfile';
|
||||
|
||||
HTMLFile.prototype._get = function(){
|
||||
var self = this;
|
||||
this._open();
|
||||
window.attachEvent('onunload', function(){ self._destroy(); });
|
||||
};
|
||||
|
||||
HTMLFile.prototype._open = function(){
|
||||
this._doc = new ActiveXObject('htmlfile');
|
||||
this._doc.open();
|
||||
this._doc.write('<html></html>');
|
||||
this._doc.parentWindow.s = this;
|
||||
this._doc.close();
|
||||
|
||||
var _iframeC = this._doc.createElement('div');
|
||||
this._doc.body.appendChild(_iframeC);
|
||||
this._iframe = this._doc.createElement('iframe');
|
||||
_iframeC.appendChild(this._iframe);
|
||||
this._iframe.src = this._prepareUrl() + '/' + (+ new Date);
|
||||
};
|
||||
|
||||
HTMLFile.prototype._ = function(data, doc){
|
||||
this._onData(data);
|
||||
var script = doc.getElementsByTagName('script')[0];
|
||||
script.parentNode.removeChild(script);
|
||||
};
|
||||
|
||||
HTMLFile.prototype._destroy = function(){
|
||||
if (this._iframe){
|
||||
this._iframe.src = 'about:blank';
|
||||
this._doc = null;
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* The HTMLFile transport creates a `forever iframe` based transport
|
||||
* for Internet Explorer. Regular forever iframe implementations will
|
||||
* continuously trigger the browsers buzy indicators. If the forever iframe
|
||||
* is created inside a `htmlfile` these indicators will not be trigged.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport.XHR}
|
||||
* @api public
|
||||
*/
|
||||
HTMLFile = io.Transport.htmlfile = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(HTMLFile, io.Transport.XHR);
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {String}
|
||||
* @api public
|
||||
*/
|
||||
HTMLFile.prototype.type = 'htmlfile';
|
||||
|
||||
/**
|
||||
* Starts the HTMLFile data stream for incoming messages. And registers a
|
||||
* onunload event listener so the HTMLFile will be destroyed.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
HTMLFile.prototype.get = function(){
|
||||
var self = this;
|
||||
this.open();
|
||||
window.attachEvent('onunload', function(){ self.destroy(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new ActiveX `htmlfile` with a forever loading iframe
|
||||
* that can be used to listen to messages. Inside the generated
|
||||
* `htmlfile` a reference will be made to the HTMLFile transport.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
HTMLFile.prototype.open = function(){
|
||||
this.doc = new ActiveXObject('htmlfile');
|
||||
this.doc.open();
|
||||
this.doc.write('<html></html>');
|
||||
this.doc.parentWindow.s = this;
|
||||
this.doc.close();
|
||||
|
||||
var iframeC = this.doc.createElement('div');
|
||||
this.doc.body.appendChild(iframeC);
|
||||
this.iframe = this.doc.createElement('iframe');
|
||||
iframeC.appendChild(this.iframe);
|
||||
this.iframe.src = this.prepareUrl() + '/' + (+ new Date);
|
||||
};
|
||||
|
||||
/**
|
||||
* The Socket.IO server will write script tags inside the forever
|
||||
* iframe, this function will be used as callback for the incoming
|
||||
* information.
|
||||
*
|
||||
* @param {String} data The message
|
||||
* @param {document} doc Reference to the context
|
||||
* @api private
|
||||
*/
|
||||
HTMLFile.prototype._ = function(data, doc){
|
||||
this.onData(data);
|
||||
var script = doc.getElementsByTagName('script')[0];
|
||||
script.parentNode.removeChild(script);
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the established connection, iframe and `htmlfile`.
|
||||
* And calls the `CollectGarbage` function of Internet Explorer
|
||||
* to release the memory.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
HTMLFile.prototype.destroy = function(){
|
||||
if (this.iframe){
|
||||
try {
|
||||
this.iframe.src = 'about:blank';
|
||||
} catch(e){}
|
||||
this.doc = null;
|
||||
CollectGarbage();
|
||||
}
|
||||
};
|
||||
|
||||
HTMLFile.prototype.disconnect = function(){
|
||||
this._destroy();
|
||||
return io.Transport.XHR.prototype.disconnect.call(this);
|
||||
};
|
||||
|
||||
HTMLFile.check = function(){
|
||||
if ('ActiveXObject' in window){
|
||||
try {
|
||||
var a = new ActiveXObject('htmlfile');
|
||||
return a && io.Transport.XHR.check();
|
||||
} catch(e){}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
HTMLFile.xdomainCheck = function(){
|
||||
// we can probably do handling for sub-domains, we should test that it's cross domain but a subdomain here
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disconnects the established connection.
|
||||
*
|
||||
* @returns {Transport} Chaining.
|
||||
* @api public
|
||||
*/
|
||||
HTMLFile.prototype.disconnect = function(){
|
||||
this.destroy();
|
||||
return io.Transport.XHR.prototype.disconnect.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser supports this transport. The browser
|
||||
* must have an `ActiveXObject` implementation.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
HTMLFile.check = function(){
|
||||
if ('ActiveXObject' in window){
|
||||
try {
|
||||
var a = new ActiveXObject('htmlfile');
|
||||
return a && io.Transport.XHR.check();
|
||||
} catch(e){}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if cross domain requests are supported.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
HTMLFile.xdomainCheck = function(){
|
||||
// we can probably do handling for sub-domains, we should test that it's cross domain but a subdomain here
|
||||
return false;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -1,116 +1,175 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
io.JSONP = [];
|
||||
|
||||
JSONPPolling = io.Transport['jsonp-polling'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
this._insertAt = document.getElementsByTagName('script')[0];
|
||||
this._index = io.JSONP.length;
|
||||
io.JSONP.push(this);
|
||||
};
|
||||
|
||||
io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
|
||||
|
||||
JSONPPolling.prototype.type = 'jsonp-polling';
|
||||
|
||||
JSONPPolling.prototype._send = function(data){
|
||||
var self = this;
|
||||
if (!('_form' in this)){
|
||||
var form = document.createElement('FORM'),
|
||||
area = document.createElement('TEXTAREA'),
|
||||
id = this._iframeId = 'socket_io_iframe_' + this._index,
|
||||
iframe;
|
||||
|
||||
form.style.position = 'absolute';
|
||||
form.style.top = '-1000px';
|
||||
form.style.left = '-1000px';
|
||||
form.target = id;
|
||||
form.method = 'POST';
|
||||
form.action = this._prepareUrl() + '/' + (+new Date) + '/' + this._index;
|
||||
area.name = 'data';
|
||||
form.appendChild(area);
|
||||
this._insertAt.parentNode.insertBefore(form, this._insertAt);
|
||||
document.body.appendChild(form);
|
||||
|
||||
this._form = form;
|
||||
this._area = area;
|
||||
}
|
||||
|
||||
function complete(){
|
||||
initIframe();
|
||||
self._posting = false;
|
||||
self._checkSend();
|
||||
};
|
||||
|
||||
function initIframe(){
|
||||
if (self._iframe){
|
||||
self._form.removeChild(self._iframe);
|
||||
}
|
||||
|
||||
try {
|
||||
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
|
||||
iframe = document.createElement('<iframe name="'+ self._iframeId +'">');
|
||||
} catch(e){
|
||||
iframe = document.createElement('iframe');
|
||||
iframe.name = self._iframeId;
|
||||
}
|
||||
|
||||
iframe.id = self._iframeId;
|
||||
|
||||
self._form.appendChild(iframe);
|
||||
self._iframe = iframe;
|
||||
};
|
||||
|
||||
initIframe();
|
||||
|
||||
this._posting = true;
|
||||
this._area.value = data;
|
||||
|
||||
try {
|
||||
this._form.submit();
|
||||
} catch(e){}
|
||||
|
||||
if (this._iframe.attachEvent){
|
||||
iframe.onreadystatechange = function(){
|
||||
if (self._iframe.readyState == 'complete') complete();
|
||||
};
|
||||
} else {
|
||||
this._iframe.onload = complete;
|
||||
}
|
||||
};
|
||||
|
||||
JSONPPolling.prototype._get = function(){
|
||||
var self = this,
|
||||
script = document.createElement('SCRIPT');
|
||||
if (this._script){
|
||||
this._script.parentNode.removeChild(this._script);
|
||||
this._script = null;
|
||||
}
|
||||
script.async = true;
|
||||
script.src = this._prepareUrl() + '/' + (+new Date) + '/' + this._index;
|
||||
script.onerror = function(){
|
||||
self._onDisconnect();
|
||||
};
|
||||
this._insertAt.parentNode.insertBefore(script, this._insertAt);
|
||||
this._script = script;
|
||||
};
|
||||
|
||||
JSONPPolling.prototype._ = function(){
|
||||
this._onData.apply(this, arguments);
|
||||
this._get();
|
||||
return this;
|
||||
};
|
||||
|
||||
JSONPPolling.check = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
JSONPPolling.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
(function(){
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* The JSONP transport creates an persistent connection by dynamically
|
||||
* inserting a script tag in the page. This script tag will receive the
|
||||
* information of the Socket.IO server. When new information is received
|
||||
* it creates a new script tag for the new data stream.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport.xhr-polling}
|
||||
* @api public
|
||||
*/
|
||||
JSONPPolling = io.Transport['jsonp-polling'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
this.insertAt = document.getElementsByTagName('script')[0];
|
||||
this.index = io.JSONP.length;
|
||||
io.JSONP.push(this);
|
||||
};
|
||||
|
||||
io.util.inherit(JSONPPolling, io.Transport['xhr-polling']);
|
||||
|
||||
/**
|
||||
* A list of all JSONPolling transports, this is used for by
|
||||
* the Socket.IO server to distribute the callbacks.
|
||||
*
|
||||
* @type {Array}
|
||||
* @api private
|
||||
*/
|
||||
io.JSONP = [];
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {String}
|
||||
* @api public
|
||||
*/
|
||||
JSONPPolling.prototype.type = 'jsonp-polling';
|
||||
|
||||
/**
|
||||
* Posts a encoded message to the Socket.IO server using an iframe.
|
||||
* The iframe is used because script tags can create POST based requests.
|
||||
* The iframe is positioned outside of the view so the user does not
|
||||
* notice it's existence.
|
||||
*
|
||||
* @param {String} data A encoded message.
|
||||
* @api private
|
||||
*/
|
||||
JSONPPolling.prototype.sendIORequest = function(data){
|
||||
var self = this;
|
||||
if (!('form' in this)){
|
||||
var form = document.createElement('FORM'),
|
||||
area = document.createElement('TEXTAREA'),
|
||||
id = this.iframeId = 'socket_io_iframe_' + this.index,
|
||||
iframe;
|
||||
|
||||
form.style.position = 'absolute';
|
||||
form.style.top = '-1000px';
|
||||
form.style.left = '-1000px';
|
||||
form.target = id;
|
||||
form.method = 'POST';
|
||||
form.action = this.prepareUrl() + '/' + (+new Date) + '/' + this.index;
|
||||
area.name = 'data';
|
||||
form.appendChild(area);
|
||||
this.insertAt.parentNode.insertBefore(form, this.insertAt);
|
||||
document.body.appendChild(form);
|
||||
|
||||
this.form = form;
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
function complete(){
|
||||
initIframe();
|
||||
self.posting = false;
|
||||
self.checkSend();
|
||||
};
|
||||
|
||||
function initIframe(){
|
||||
if (self.iframe){
|
||||
self.form.removeChild(self.iframe);
|
||||
}
|
||||
|
||||
try {
|
||||
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
|
||||
iframe = document.createElement('<iframe name="'+ self.iframeId +'">');
|
||||
} catch(e){
|
||||
iframe = document.createElement('iframe');
|
||||
iframe.name = self.iframeId;
|
||||
}
|
||||
|
||||
iframe.id = self.iframeId;
|
||||
|
||||
self.form.appendChild(iframe);
|
||||
self.iframe = iframe;
|
||||
};
|
||||
|
||||
initIframe();
|
||||
|
||||
this.posting = true;
|
||||
this.area.value = data;
|
||||
|
||||
try {
|
||||
this.form.submit();
|
||||
} catch(e){}
|
||||
|
||||
if (this.iframe.attachEvent){
|
||||
iframe.onreadystatechange = function(){
|
||||
if (self.iframe.readyState == 'complete') complete();
|
||||
};
|
||||
} else {
|
||||
this.iframe.onload = complete;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new JSONP poll that can be used to listen
|
||||
* for messages from the Socket.IO server.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
JSONPPolling.prototype.get = function(){
|
||||
var self = this,
|
||||
script = document.createElement('SCRIPT');
|
||||
if (this.script){
|
||||
this.script.parentNode.removeChild(this.script);
|
||||
this.script = null;
|
||||
}
|
||||
script.async = true;
|
||||
script.src = this.prepareUrl() + '/' + (+new Date) + '/' + this.index;
|
||||
script.onerror = function(){
|
||||
self.onDisconnect();
|
||||
};
|
||||
this.insertAt.parentNode.insertBefore(script, this.insertAt);
|
||||
this.script = script;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function for the incoming message stream from the Socket.IO server.
|
||||
*
|
||||
* @param {String} data The message
|
||||
* @param {document} doc Reference to the context
|
||||
* @api private
|
||||
*/
|
||||
JSONPPolling.prototype._ = function(){
|
||||
this.onData.apply(this, arguments);
|
||||
this.get();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if browser supports this transport.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
JSONPPolling.check = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if cross domain requests are supported
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
JSONPPolling.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
})();
|
||||
@@ -1,65 +1,121 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var WS = io.Transport.websocket = function(){
|
||||
io.Transport.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(WS, io.Transport);
|
||||
|
||||
WS.prototype.type = 'websocket';
|
||||
|
||||
WS.prototype.connect = function(){
|
||||
var self = this;
|
||||
this.socket = new WebSocket(this._prepareUrl());
|
||||
this.socket.onmessage = function(ev){ self._onData(ev.data); };
|
||||
this.socket.onclose = function(ev){ self._onClose(); };
|
||||
this.socket.onerror = function(e){ self._onError(e); };
|
||||
return this;
|
||||
};
|
||||
|
||||
WS.prototype.send = function(data){
|
||||
if (this.socket) this.socket.send(this._encode(data));
|
||||
return this;
|
||||
};
|
||||
|
||||
WS.prototype.disconnect = function(){
|
||||
if (this.socket) this.socket.close();
|
||||
return this;
|
||||
};
|
||||
|
||||
WS.prototype._onClose = function(){
|
||||
this._onDisconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
WS.prototype._onError = function(e){
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* The WebSocket transport uses the HTML5 WebSocket API to establish an persistent
|
||||
* connection with the Socket.IO server. This transport will also be inherited by the
|
||||
* FlashSocket fallback as it provides a API compatible polyfill for the WebSockets.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport}
|
||||
* @api public
|
||||
*/
|
||||
WS = io.Transport.websocket = function(){
|
||||
io.Transport.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(WS, io.Transport);
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {String}
|
||||
* @api public
|
||||
*/
|
||||
WS.prototype.type = 'websocket';
|
||||
|
||||
/**
|
||||
* Initializes a new `WebSocket` connection with the Socket.IO server. We attach
|
||||
* all the appropriate listeners to handle the responses from the server.
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
WS.prototype.connect = function(){
|
||||
var self = this;
|
||||
this.socket = new WebSocket(this.prepareUrl());
|
||||
this.socket.onmessage = function(ev){ self.onData(ev.data); };
|
||||
this.socket.onclose = function(ev){ self.onDisconnect(); };
|
||||
this.socket.onerror = function(e){ self.onError(e); };
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a message to the Socket.IO server. The message will automatically be encoded
|
||||
* in the correct message format.
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
WS.prototype.send = function(data){
|
||||
if (this.socket) this.socket.send(this.encode(data));
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect the established `WebSocket` connection.
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
WS.prototype.disconnect = function(){
|
||||
if (this.socket) this.socket.close();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the errors that `WebSocket` might be giving when we
|
||||
* are attempting to connect or send messages.
|
||||
*
|
||||
* @param {Error} e The error.
|
||||
* @api private
|
||||
*/
|
||||
WS.prototype.onError = function(e){
|
||||
this.base.emit('error', [e]);
|
||||
};
|
||||
|
||||
WS.prototype._prepareUrl = function(){
|
||||
return (this.base.options.secure ? 'wss' : 'ws')
|
||||
+ '://' + this.base.host
|
||||
+ ':' + this.base.options.port
|
||||
+ '/' + this.base.options.resource
|
||||
+ '/' + this.type
|
||||
+ (this.sessionid ? ('/' + this.sessionid) : '');
|
||||
};
|
||||
|
||||
WS.check = function(){
|
||||
// we make sure WebSocket is not confounded with a previously loaded flash WebSocket
|
||||
return 'WebSocket' in window && WebSocket.prototype && ( WebSocket.prototype.send && !!WebSocket.prototype.send.toString().match(/native/i)) && typeof WebSocket !== "undefined";
|
||||
};
|
||||
|
||||
WS.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generate a `WebSocket` compatible URL based on the options
|
||||
* the user supplied in our Socket.IO base.
|
||||
*
|
||||
* @returns {String} Connection url
|
||||
* @api private
|
||||
*/
|
||||
WS.prototype.prepareUrl = function(){
|
||||
return (this.base.options.secure ? 'wss' : 'ws')
|
||||
+ '://' + this.base.host
|
||||
+ ':' + this.base.options.port
|
||||
+ '/' + this.base.options.resource
|
||||
+ '/' + this.type
|
||||
+ (this.sessionid ? ('/' + this.sessionid) : '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the browser has support for native `WebSockets` and that
|
||||
* it's not the polyfill created for the FlashSocket transport.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
WS.check = function(){
|
||||
// we make sure WebSocket is not confounded with a previously loaded flash WebSocket
|
||||
return 'WebSocket' in window && WebSocket.prototype && ( WebSocket.prototype.send && !!WebSocket.prototype.send.toString().match(/native/i)) && typeof WebSocket !== "undefined";
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the `WebSocket` transport support cross domain communications.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
WS.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,36 +1,66 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var XHRMultipart = io.Transport['xhr-multipart'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(XHRMultipart, io.Transport.XHR);
|
||||
|
||||
XHRMultipart.prototype.type = 'xhr-multipart';
|
||||
|
||||
XHRMultipart.prototype._get = function(){
|
||||
var self = this;
|
||||
this._xhr = this._request('', 'GET', true);
|
||||
this._xhr.onreadystatechange = function(){
|
||||
if (self._xhr.readyState == 3) self._onData(self._xhr.responseText);
|
||||
};
|
||||
this._xhr.send(null);
|
||||
};
|
||||
|
||||
XHRMultipart.check = function(){
|
||||
return 'XMLHttpRequest' in window && 'prototype' in XMLHttpRequest && 'multipart' in XMLHttpRequest.prototype;
|
||||
};
|
||||
|
||||
XHRMultipart.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
})();
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* The XHR-Multipart transport uses the a multipart XHR connection to
|
||||
* stream in the data from the Socket.IO server
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport.XHR}
|
||||
* @api public
|
||||
*/
|
||||
XHRMultipart = io.Transport['xhr-multipart'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(XHRMultipart, io.Transport.XHR);
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {String}
|
||||
* @api public
|
||||
*/
|
||||
XHRMultipart.prototype.type = 'xhr-multipart';
|
||||
|
||||
/**
|
||||
* Starts the multipart stream for incomming messages.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
XHRMultipart.prototype.get = function(){
|
||||
var self = this;
|
||||
this.xhr = this.request('', 'GET', true);
|
||||
this.xhr.onreadystatechange = function(){
|
||||
if (self.xhr.readyState == 4) self.onData(self.xhr.responseText);
|
||||
};
|
||||
this.xhr.send(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if browser supports this transport.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHRMultipart.check = function(){
|
||||
return 'XMLHttpRequest' in window && 'prototype' in XMLHttpRequest && 'multipart' in XMLHttpRequest.prototype;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if cross domain requests are supported.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHRMultipart.xdomainCheck = function(){
|
||||
return true;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -1,61 +1,97 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var empty = new Function(),
|
||||
|
||||
XHRPolling = io.Transport['xhr-polling'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(XHRPolling, io.Transport.XHR);
|
||||
|
||||
XHRPolling.prototype.type = 'xhr-polling';
|
||||
|
||||
XHRPolling.prototype.connect = function(){
|
||||
if (io.util.ios || io.util.android){
|
||||
var self = this;
|
||||
io.util.load(function(){
|
||||
setTimeout(function(){
|
||||
io.Transport.XHR.prototype.connect.call(self);
|
||||
}, 10);
|
||||
});
|
||||
} else {
|
||||
io.Transport.XHR.prototype.connect.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
XHRPolling.prototype._get = function(){
|
||||
var self = this;
|
||||
this._xhr = this._request(+ new Date, 'GET');
|
||||
this._xhr.onreadystatechange = function(){
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* A small stub function that will be used to reduce memory leaks.
|
||||
*
|
||||
* @type {Function}
|
||||
* @api private
|
||||
*/
|
||||
empty = new Function(),
|
||||
|
||||
/**
|
||||
* The XHR-polling transport uses long polling XHR requests to create a
|
||||
* "persistent" connection with the server.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport.XHR}
|
||||
* @api public
|
||||
*/
|
||||
XHRPolling = io.Transport['xhr-polling'] = function(){
|
||||
io.Transport.XHR.apply(this, arguments);
|
||||
};
|
||||
|
||||
io.util.inherit(XHRPolling, io.Transport.XHR);
|
||||
|
||||
/**
|
||||
* The transport type, you use this to identify which transport was chosen.
|
||||
*
|
||||
* @type {string}
|
||||
* @api public
|
||||
*/
|
||||
XHRPolling.prototype.type = 'xhr-polling';
|
||||
|
||||
/**
|
||||
* Establish a connection, for iPhone and Android this will be done once the page
|
||||
* is loaded.
|
||||
*
|
||||
* @returns {Transport} Chaining.
|
||||
* @api public
|
||||
*/
|
||||
XHRPolling.prototype.connect = function(){
|
||||
var self = this;
|
||||
io.util.defer(function(){ io.Transport.XHR.prototype.connect.call(self) });
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts a XHR request to wait for incoming messages.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
XHRPolling.prototype.get = function(){
|
||||
var self = this;
|
||||
this.xhr = this.request(+ new Date, 'GET');
|
||||
this.xhr.onreadystatechange = function(){
|
||||
var status;
|
||||
if (self._xhr.readyState == 4){
|
||||
self._xhr.onreadystatechange = empty;
|
||||
try { status = self._xhr.status; } catch(e){}
|
||||
if (self.xhr.readyState == 4){
|
||||
self.xhr.onreadystatechange = empty;
|
||||
try { status = self.xhr.status; } catch(e){}
|
||||
if (status == 200){
|
||||
self._onData(self._xhr.responseText);
|
||||
self._get();
|
||||
self.onData(self.xhr.responseText);
|
||||
self.get();
|
||||
} else {
|
||||
self._onDisconnect();
|
||||
self.onDisconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
this._xhr.send(null);
|
||||
};
|
||||
|
||||
XHRPolling.check = function(){
|
||||
return io.Transport.XHR.check();
|
||||
};
|
||||
|
||||
XHRPolling.xdomainCheck = function(){
|
||||
return io.Transport.XHR.xdomainCheck();
|
||||
};
|
||||
this.xhr.send(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if browser supports this transport.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHRPolling.check = function(){
|
||||
return io.Transport.XHR.check();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if cross domain requests are supported
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHRPolling.xdomainCheck = function(){
|
||||
return io.Transport.XHR.xdomainCheck();
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,135 +1,221 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var empty = new Function,
|
||||
|
||||
XMLHttpRequestCORS = (function(){
|
||||
if (!('XMLHttpRequest' in window)) return false;
|
||||
// CORS feature detection
|
||||
var a = new XMLHttpRequest();
|
||||
return a.withCredentials != undefined;
|
||||
})(),
|
||||
|
||||
request = function(xdomain){
|
||||
if ('XDomainRequest' in window && xdomain) return new XDomainRequest();
|
||||
if ('XMLHttpRequest' in window && (!xdomain || XMLHttpRequestCORS)) return new XMLHttpRequest();
|
||||
if (!xdomain){
|
||||
try {
|
||||
var a = new ActiveXObject('MSXML2.XMLHTTP');
|
||||
return a;
|
||||
} catch(e){}
|
||||
|
||||
try {
|
||||
var b = new ActiveXObject('Microsoft.XMLHTTP');
|
||||
return b;
|
||||
} catch(e){}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
XHR = io.Transport.XHR = function(){
|
||||
io.Transport.apply(this, arguments);
|
||||
this._sendBuffer = [];
|
||||
};
|
||||
|
||||
io.util.inherit(XHR, io.Transport);
|
||||
|
||||
XHR.prototype.connect = function(){
|
||||
this._get();
|
||||
return this;
|
||||
};
|
||||
|
||||
XHR.prototype._checkSend = function(){
|
||||
if (!this._posting && this._sendBuffer.length){
|
||||
var encoded = this._encode(this._sendBuffer);
|
||||
this._sendBuffer = [];
|
||||
this._send(encoded);
|
||||
}
|
||||
};
|
||||
|
||||
XHR.prototype.send = function(data){
|
||||
if (io.util.isArray(data)){
|
||||
this._sendBuffer.push.apply(this._sendBuffer, data);
|
||||
} else {
|
||||
this._sendBuffer.push(data);
|
||||
}
|
||||
this._checkSend();
|
||||
return this;
|
||||
};
|
||||
|
||||
XHR.prototype._send = function(data){
|
||||
var self = this;
|
||||
this._posting = true;
|
||||
this._sendXhr = this._request('send', 'POST');
|
||||
this._sendXhr.onreadystatechange = function(){
|
||||
var status;
|
||||
if (self._sendXhr.readyState == 4){
|
||||
self._sendXhr.onreadystatechange = empty;
|
||||
try { status = self._sendXhr.status; } catch(e){}
|
||||
self._posting = false;
|
||||
if (status == 200){
|
||||
self._checkSend();
|
||||
} else {
|
||||
self._onDisconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
this._sendXhr.send('data=' + encodeURIComponent(data));
|
||||
};
|
||||
|
||||
XHR.prototype.disconnect = function(){
|
||||
// send disconnection signal
|
||||
this._onDisconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
XHR.prototype._onDisconnect = function(){
|
||||
if (this._xhr){
|
||||
this._xhr.onreadystatechange = empty;
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* A small stub function that will be used to reduce memory leaks.
|
||||
*
|
||||
* @type {Function}
|
||||
* @api private
|
||||
*/
|
||||
empty = new Function,
|
||||
|
||||
/**
|
||||
* We preform a small feature detection to see if `Cross Origin Resource Sharing`
|
||||
* is supported in the `XMLHttpRequest` object, so we can use it for cross domain requests.
|
||||
*
|
||||
* @type {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
XMLHttpRequestCORS = (function(){
|
||||
if (!('XMLHttpRequest' in window)) return false;
|
||||
// CORS feature detection
|
||||
var a = new XMLHttpRequest();
|
||||
return a.withCredentials != undefined;
|
||||
})(),
|
||||
|
||||
/**
|
||||
* Generates the correct `XMLHttpRequest` for regular and cross domain requests.
|
||||
*
|
||||
* @param {Boolean} [xdomain] Create a request that can be used cross domain.
|
||||
* @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest we will return that.
|
||||
* @api private
|
||||
*/
|
||||
request = function(xdomain){
|
||||
if ('XDomainRequest' in window && xdomain) return new XDomainRequest();
|
||||
if ('XMLHttpRequest' in window && (!xdomain || XMLHttpRequestCORS)) return new XMLHttpRequest();
|
||||
if (!xdomain){
|
||||
try {
|
||||
this._xhr.abort();
|
||||
var a = new ActiveXObject('MSXML2.XMLHTTP');
|
||||
return a;
|
||||
} catch(e){}
|
||||
this._xhr = null;
|
||||
}
|
||||
if (this._sendXhr){
|
||||
this._sendXhr.onreadystatechange = empty;
|
||||
|
||||
try {
|
||||
this._sendXhr.abort();
|
||||
var b = new ActiveXObject('Microsoft.XMLHTTP');
|
||||
return b;
|
||||
} catch(e){}
|
||||
this._sendXhr = null;
|
||||
}
|
||||
this._sendBuffer = [];
|
||||
io.Transport.prototype._onDisconnect.call(this);
|
||||
};
|
||||
|
||||
XHR.prototype._request = function(url, method, multipart){
|
||||
var req = request(this.base._isXDomain());
|
||||
if (multipart) req.multipart = true;
|
||||
req.open(method || 'GET', this._prepareUrl() + (url ? '/' + url : ''));
|
||||
if (method == 'POST' && 'setRequestHeader' in req){
|
||||
req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
|
||||
}
|
||||
return req;
|
||||
};
|
||||
|
||||
XHR.check = function(xdomain){
|
||||
try {
|
||||
if (request(xdomain)) return true;
|
||||
} catch(e){}
|
||||
return false;
|
||||
};
|
||||
|
||||
XHR.xdomainCheck = function(){
|
||||
return XHR.check(true);
|
||||
};
|
||||
|
||||
XHR.request = request;
|
||||
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* This is the base for XHR based transports, the `XHR-Polling` and the `XHR-multipart`
|
||||
* transports will extend this class.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {io.Transport}
|
||||
* @property {Array} sendBuffer Used to queue up messages so they can be send as one request.
|
||||
* @api public
|
||||
*/
|
||||
XHR = io.Transport.XHR = function(){
|
||||
io.Transport.apply(this, arguments);
|
||||
this.sendBuffer = [];
|
||||
};
|
||||
|
||||
io.util.inherit(XHR, io.Transport);
|
||||
|
||||
/**
|
||||
* Establish a connection
|
||||
*
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
XHR.prototype.connect = function(){
|
||||
this.get();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we need to send data to the Socket.IO server, if we have data in our buffer
|
||||
* we encode it and forward it to the sendIORequest method.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
XHR.prototype.checkSend = function(){
|
||||
if (!this.posting && this.sendBuffer.length){
|
||||
var encoded = this.encode(this.sendBuffer);
|
||||
this.sendBuffer = [];
|
||||
this.sendIORequest(encoded);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to the Socket.IO server.
|
||||
*
|
||||
* @param data The message
|
||||
* @returns {Transport}
|
||||
* @api public
|
||||
*/
|
||||
XHR.prototype.send = function(data){
|
||||
if (io.util.isArray(data)){
|
||||
this.sendBuffer.push.apply(this.sendBuffer, data);
|
||||
} else {
|
||||
this.sendBuffer.push(data);
|
||||
}
|
||||
this.checkSend();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Posts a encoded message to the Socket.IO server.
|
||||
*
|
||||
* @param {String} data A encoded message.
|
||||
* @api private
|
||||
*/
|
||||
XHR.prototype.sendIORequest = function(data){
|
||||
var self = this;
|
||||
this.posting = true;
|
||||
this.sendXHR = this.request('send', 'POST');
|
||||
this.sendXHR.onreadystatechange = function(){
|
||||
var status;
|
||||
if (self.sendXHR.readyState == 4){
|
||||
self.sendXHR.onreadystatechange = empty;
|
||||
try { status = self.sendXHR.status; } catch(e){}
|
||||
self.posting = false;
|
||||
if (status == 200){
|
||||
self.checkSend();
|
||||
} else {
|
||||
self.onDisconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
this.sendXHR.send('data=' + encodeURIComponent(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect the established connection.
|
||||
*
|
||||
* @returns {Transport}.
|
||||
* @api public
|
||||
*/
|
||||
XHR.prototype.disconnect = function(){
|
||||
// send disconnection signal
|
||||
this.onDisconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the disconnect request.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
XHR.prototype.onDisconnect = function(){
|
||||
if (this.xhr){
|
||||
this.xhr.onreadystatechange = empty;
|
||||
try {
|
||||
this.xhr.abort();
|
||||
} catch(e){}
|
||||
this.xhr = null;
|
||||
}
|
||||
if (this.sendXHR){
|
||||
this.sendXHR.onreadystatechange = empty;
|
||||
try {
|
||||
this.sendXHR.abort();
|
||||
} catch(e){}
|
||||
this.sendXHR = null;
|
||||
}
|
||||
this.sendBuffer = [];
|
||||
io.Transport.prototype.onDisconnect.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a configured XHR request
|
||||
*
|
||||
* @param {String} url The url that needs to be requested.
|
||||
* @param {String} method The method the request should use.
|
||||
* @param {Boolean} multipart Do a multipart XHR request
|
||||
* @returns {XMLHttpRequest}
|
||||
* @api private
|
||||
*/
|
||||
XHR.prototype.request = function(url, method, multipart){
|
||||
var req = request(this.base.isXDomain());
|
||||
if (multipart) req.multipart = true;
|
||||
req.open(method || 'GET', this.prepareUrl() + (url ? '/' + url : ''));
|
||||
if (method == 'POST' && 'setRequestHeader' in req){
|
||||
req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
|
||||
}
|
||||
return req;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the XHR transports are supported
|
||||
*
|
||||
* @param {Boolean} xdomain Check if we support cross domain requests.
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHR.check = function(xdomain){
|
||||
try {
|
||||
if (request(xdomain)) return true;
|
||||
} catch(e){}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the XHR transport supports corss domain requests.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
XHR.xdomainCheck = function(){
|
||||
return XHR.check(true);
|
||||
};
|
||||
|
||||
XHR.request = request;
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,60 +1,161 @@
|
||||
/**
|
||||
* Socket.IO client
|
||||
*
|
||||
* @author Guillermo Rauch <guillermo@learnboost.com>
|
||||
* @license The MIT license.
|
||||
* @copyright Copyright (c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* socket.io-node-client
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
var _pageLoaded = false;
|
||||
|
||||
io.util = {
|
||||
|
||||
ios: false,
|
||||
|
||||
load: function(fn){
|
||||
if (/loaded|complete/.test(document.readyState) || _pageLoaded) return fn();
|
||||
if ('attachEvent' in window){
|
||||
window.attachEvent('onload', fn);
|
||||
} else {
|
||||
window.addEventListener('load', fn, false);
|
||||
}
|
||||
},
|
||||
|
||||
inherit: function(ctor, superCtor){
|
||||
// no support for `instanceof` for now
|
||||
for (var i in superCtor.prototype){
|
||||
ctor.prototype[i] = superCtor.prototype[i];
|
||||
}
|
||||
},
|
||||
|
||||
indexOf: function(arr, item, from){
|
||||
for (var l = arr.length, i = (from < 0) ? Math.max(0, l + from) : from || 0; i < l; i++){
|
||||
if (arr[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
isArray: function(obj){
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
},
|
||||
|
||||
var io = this.io,
|
||||
|
||||
/**
|
||||
* Set when the `onload` event is executed on the page. This variable is used by
|
||||
* `io.util.load` to detect if we need to execute the function immediately or add
|
||||
* it to a onload listener.
|
||||
*
|
||||
* @type {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
pageLoaded = false;
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
io.util = {
|
||||
/**
|
||||
* Executes the given function when the page is loaded.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* io.util.load(function(){ console.log('page loaded') });
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
load: function(fn){
|
||||
if (/loaded|complete/.test(document.readyState) || pageLoaded) return fn();
|
||||
if ('attachEvent' in window){
|
||||
window.attachEvent('onload', fn);
|
||||
} else {
|
||||
window.addEventListener('load', fn, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Defers the function untill it's the function can be executed without
|
||||
* blocking the load process. This is especially needed for WebKit based
|
||||
* browsers. If a long running connection is made before the onload event
|
||||
* a loading indicator spinner will be present at all times untill a
|
||||
* reconnect has been made.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
defer: function(fn){
|
||||
if (!io.util.webkit) return fn();
|
||||
io.util.load(function(){
|
||||
setTimeout(fn,100);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Inherit the prototype methods from one constructor into another.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* function foo(){};
|
||||
* foo.prototype.hello = function(){ console.log( this.words )};
|
||||
*
|
||||
* function bar(){
|
||||
* this.words = "Hello world";
|
||||
* };
|
||||
*
|
||||
* io.util.inherit(bar,foo);
|
||||
* var person = new bar();
|
||||
* person.hello();
|
||||
* // => "Hello World"
|
||||
*
|
||||
* @param {Constructor} ctor The constructor that needs to inherit the methods.
|
||||
* @param {Constructor} superCtor The constructor to inherit from.
|
||||
* @api public
|
||||
*/
|
||||
inherit: function(ctor, superCtor){
|
||||
// no support for `instanceof` for now
|
||||
for (var i in superCtor.prototype){
|
||||
ctor.prototype[i] = superCtor.prototype[i];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the index of item in a given Array.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var data = ['socket',2,3,4,'socket',5,6,7,'io'];
|
||||
* io.util.indexOf(data,'socket',1);
|
||||
* // => 4
|
||||
*
|
||||
* @param {Array} arr The array
|
||||
* @param item The item that we need to find
|
||||
* @param {Integer} from Starting point
|
||||
* @api public
|
||||
*/
|
||||
indexOf: function(arr, item, from){
|
||||
for (var l = arr.length, i = (from < 0) ? Math.max(0, l + from) : from || 0; i < l; i++){
|
||||
if (arr[i] === item) return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the given object is an Array.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* io.util.isArray([]);
|
||||
* // => true
|
||||
* io.util.isArray({});
|
||||
* // => false
|
||||
*
|
||||
* @param obj
|
||||
* @api public
|
||||
*/
|
||||
isArray: function(obj){
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges the properties of two objects.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var a = {foo:'bar'}
|
||||
* , b = {bar:'baz'};
|
||||
*
|
||||
* io.util.merge(a,b);
|
||||
* // => {foo:'bar',bar:'baz'}
|
||||
*
|
||||
* @param {Object} target The object that receives the keys
|
||||
* @param {Object} additional The object that supplies the keys
|
||||
* @api public
|
||||
*/
|
||||
merge: function(target, additional){
|
||||
for (var i in additional)
|
||||
if (additional.hasOwnProperty(i))
|
||||
target[i] = additional[i];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detect the Webkit platform based on the userAgent string.
|
||||
* This includes Mobile Webkit.
|
||||
*
|
||||
* @type {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
io.util.webkit = /webkit/i.test(navigator.userAgent);
|
||||
|
||||
io.util.load(function(){
|
||||
pageLoaded = true;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
io.util.ios = /iphone|ipad/i.test(navigator.userAgent);
|
||||
io.util.android = /android/i.test(navigator.userAgent);
|
||||
io.util.opera = /opera/i.test(navigator.userAgent);
|
||||
|
||||
io.util.load(function(){
|
||||
_pageLoaded = true;
|
||||
});
|
||||
|
||||
})();
|
||||
})();
|
||||
4
support/socket.io-client/lib/vendor/uglifyjs/.gitignore
vendored
Executable file
4
support/socket.io-client/lib/vendor/uglifyjs/.gitignore
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
.DS_Store
|
||||
.tmp*~
|
||||
*.local.*
|
||||
.pinf-*
|
||||
782
support/socket.io-client/lib/vendor/uglifyjs/README.html
vendored
Executable file
782
support/socket.io-client/lib/vendor/uglifyjs/README.html
vendored
Executable file
@@ -0,0 +1,782 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
lang="en" xml:lang="en">
|
||||
<head>
|
||||
<title>UglifyJS -- a JavaScript parser/compressor/beautifier</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
<meta name="generator" content="Org-mode"/>
|
||||
<meta name="generated" content="2011-02-28 22:35:00 EET"/>
|
||||
<meta name="author" content="Mihai Bazon"/>
|
||||
<meta name="description" content="a JavaScript parser/compressor/beautifier in JavaScript"/>
|
||||
<meta name="keywords" content="javascript, js, parser, compiler, compressor, mangle, minify, minifier"/>
|
||||
<style type="text/css">
|
||||
<!--/*--><![CDATA[/*><!--*/
|
||||
html { font-family: Times, serif; font-size: 12pt; }
|
||||
.title { text-align: center; }
|
||||
.todo { color: red; }
|
||||
.done { color: green; }
|
||||
.tag { background-color: #add8e6; font-weight:normal }
|
||||
.target { }
|
||||
.timestamp { color: #bebebe; }
|
||||
.timestamp-kwd { color: #5f9ea0; }
|
||||
p.verse { margin-left: 3% }
|
||||
pre {
|
||||
border: 1pt solid #AEBDCC;
|
||||
background-color: #F3F5F7;
|
||||
padding: 5pt;
|
||||
font-family: courier, monospace;
|
||||
font-size: 90%;
|
||||
overflow:auto;
|
||||
}
|
||||
table { border-collapse: collapse; }
|
||||
td, th { vertical-align: top; }
|
||||
dt { font-weight: bold; }
|
||||
div.figure { padding: 0.5em; }
|
||||
div.figure p { text-align: center; }
|
||||
textarea { overflow-x: auto; }
|
||||
.linenr { font-size:smaller }
|
||||
.code-highlighted {background-color:#ffff00;}
|
||||
.org-info-js_info-navigation { border-style:none; }
|
||||
#org-info-js_console-label { font-size:10px; font-weight:bold;
|
||||
white-space:nowrap; }
|
||||
.org-info-js_search-highlight {background-color:#ffff00; color:#000000;
|
||||
font-weight:bold; }
|
||||
/*]]>*/-->
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="docstyle.css" />
|
||||
<script type="text/javascript">
|
||||
<!--/*--><![CDATA[/*><!--*/
|
||||
function CodeHighlightOn(elem, id)
|
||||
{
|
||||
var target = document.getElementById(id);
|
||||
if(null != target) {
|
||||
elem.cacheClassElem = elem.className;
|
||||
elem.cacheClassTarget = target.className;
|
||||
target.className = "code-highlighted";
|
||||
elem.className = "code-highlighted";
|
||||
}
|
||||
}
|
||||
function CodeHighlightOff(elem, id)
|
||||
{
|
||||
var target = document.getElementById(id);
|
||||
if(elem.cacheClassElem)
|
||||
elem.className = elem.cacheClassElem;
|
||||
if(elem.cacheClassTarget)
|
||||
target.className = elem.cacheClassTarget;
|
||||
}
|
||||
/*]]>*///-->
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
|
||||
<h1 class="title">UglifyJS – a JavaScript parser/compressor/beautifier</h1>
|
||||
|
||||
|
||||
<div id="table-of-contents">
|
||||
<h2>Table of Contents</h2>
|
||||
<div id="text-table-of-contents">
|
||||
<ul>
|
||||
<li><a href="#sec-1">1 UglifyJS — a JavaScript parser/compressor/beautifier </a>
|
||||
<ul>
|
||||
<li><a href="#sec-1_1">1.1 Unsafe transformations </a>
|
||||
<ul>
|
||||
<li><a href="#sec-1_1_1">1.1.1 Calls involving the global Array constructor </a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#sec-1_2">1.2 Usage </a>
|
||||
<ul>
|
||||
<li><a href="#sec-1_2_1">1.2.1 API </a></li>
|
||||
<li><a href="#sec-1_2_2">1.2.2 Beautifier shortcoming – no more comments </a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#sec-1_3">1.3 Compression – how good is it? </a></li>
|
||||
<li><a href="#sec-1_4">1.4 Bugs? </a></li>
|
||||
<li><a href="#sec-1_5">1.5 Links </a></li>
|
||||
<li><a href="#sec-1_6">1.6 License </a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1" class="outline-2">
|
||||
<h2 id="sec-1"><span class="section-number-2">1</span> UglifyJS — a JavaScript parser/compressor/beautifier </h2>
|
||||
<div class="outline-text-2" id="text-1">
|
||||
|
||||
|
||||
<p>
|
||||
<b>Update</b>: please read the section on <a href="#sec-1_1">unsafe transformations</a>.
|
||||
</p>
|
||||
<p>
|
||||
This package implements a general-purpose JavaScript
|
||||
parser/compressor/beautifier toolkit. It is developed on <a href="http://nodejs.org/">NodeJS</a>, but it
|
||||
should work on any JavaScript platform supporting the CommonJS module system
|
||||
(and if your platform of choice doesn't support CommonJS, you can easily
|
||||
implement it, or discard the <code>exports.*</code> lines from UglifyJS sources).
|
||||
</p>
|
||||
<p>
|
||||
The tokenizer/parser generates an abstract syntax tree from JS code. You
|
||||
can then traverse the AST to learn more about the code, or do various
|
||||
manipulations on it. This part is implemented in <a href="../lib/parse-js.js">parse-js.js</a> and it's a
|
||||
port to JavaScript of the excellent <a href="http://marijn.haverbeke.nl/parse-js/">parse-js</a> Common Lisp library from <a href="http://marijn.haverbeke.nl/">Marijn Haverbeke</a>.
|
||||
</p>
|
||||
<p>
|
||||
( See <a href="http://github.com/mishoo/cl-uglify-js">cl-uglify-js</a> if you're looking for the Common Lisp version of
|
||||
UglifyJS. )
|
||||
</p>
|
||||
<p>
|
||||
The second part of this package, implemented in <a href="../lib/process.js">process.js</a>, inspects and
|
||||
manipulates the AST generated by the parser to provide the following:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
ability to re-generate JavaScript code from the AST. Optionally
|
||||
indented—you can use this if you want to “beautify” a program that has
|
||||
been compressed, so that you can inspect the source. But you can also run
|
||||
our code generator to print out an AST without any whitespace, so you
|
||||
achieve compression as well.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
shorten variable names (usually to single characters). Our mangler will
|
||||
analyze the code and generate proper variable names, depending on scope
|
||||
and usage, and is smart enough to deal with globals defined elsewhere, or
|
||||
with <code>eval()</code> calls or <code>with{}</code> statements. In short, if <code>eval()</code> or
|
||||
<code>with{}</code> are used in some scope, then all variables in that scope and any
|
||||
variables in the parent scopes will remain unmangled, and any references
|
||||
to such variables remain unmangled as well.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
various small optimizations that may lead to faster code but certainly
|
||||
lead to smaller code. Where possible, we do the following:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
foo["bar"] ==> foo.bar
|
||||
|
||||
</li>
|
||||
<li>
|
||||
remove block brackets <code>{}</code>
|
||||
|
||||
</li>
|
||||
<li>
|
||||
join consecutive var declarations:
|
||||
var a = 10; var b = 20; ==> var a=10,b=20;
|
||||
|
||||
</li>
|
||||
<li>
|
||||
resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the
|
||||
replacement if the result occupies less bytes; for example 1/3 would
|
||||
translate to 0.333333333333, so in this case we don't replace it.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
consecutive statements in blocks are merged into a sequence; in many
|
||||
cases, this leaves blocks with a single statement, so then we can remove
|
||||
the block brackets.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
various optimizations for IF statements:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
if (foo) bar(); else baz(); ==> foo?bar():baz();
|
||||
</li>
|
||||
<li>
|
||||
if (!foo) bar(); else baz(); ==> foo?baz():bar();
|
||||
</li>
|
||||
<li>
|
||||
if (foo) bar(); ==> foo&&bar();
|
||||
</li>
|
||||
<li>
|
||||
if (!foo) bar(); ==> foo||bar();
|
||||
</li>
|
||||
<li>
|
||||
if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
|
||||
</li>
|
||||
<li>
|
||||
if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
remove some unreachable code and warn about it (code that follows a
|
||||
<code>return</code>, <code>throw</code>, <code>break</code> or <code>continue</code> statement, except
|
||||
function/variable declarations).
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_1" class="outline-3">
|
||||
<h3 id="sec-1_1"><span class="section-number-3">1.1</span> <span class="target">Unsafe transformations</span> </h3>
|
||||
<div class="outline-text-3" id="text-1_1">
|
||||
|
||||
|
||||
<p>
|
||||
UglifyJS tries its best to achieve great compression while leaving the
|
||||
semantics of the code intact. In general, if your code logic is broken by
|
||||
UglifyJS then it's a bug in UglifyJS and you should report it and I should
|
||||
fix it. :-)
|
||||
</p>
|
||||
<p>
|
||||
However, I opted to include the following potentially unsafe transformations
|
||||
as default behavior. Discussion is welcome, if you have ideas of how to
|
||||
handle this better, or any objections to these optimizations, please let me
|
||||
know.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_1_1" class="outline-4">
|
||||
<h4 id="sec-1_1_1"><span class="section-number-4">1.1.1</span> Calls involving the global Array constructor </h4>
|
||||
<div class="outline-text-4" id="text-1_1_1">
|
||||
|
||||
|
||||
<p>
|
||||
The following transformations occur:
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<pre class="src src-espresso"><span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3, 4) => [1,2,3,4]
|
||||
Array(a, b, c) => [a,b,c]
|
||||
<span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(5) => Array(5)
|
||||
<span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(a) => Array(a)
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
These are all safe if the Array name isn't redefined. JavaScript does allow
|
||||
one to globally redefine Array (and pretty much everything, in fact) but I
|
||||
personally don't see why would anyone do that.
|
||||
</p>
|
||||
<p>
|
||||
UglifyJS does handle the case where Array is redefined locally, or even
|
||||
globally but with a <code>function</code> or <code>var</code> declaration. Therefore, in the
|
||||
following cases UglifyJS <b>doesn't touch</b> calls or instantiations of Array:
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<pre class="src src-espresso"><span style="color: #add8e6;">// </span><span style="color: #add8e6;">case 1. globally declared variable
|
||||
</span> <span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">Array</span>;
|
||||
<span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3);
|
||||
Array(a, b);
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">or (can be declared later)
|
||||
</span> <span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3);
|
||||
<span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">Array</span>;
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">or (can be a function)
|
||||
</span> <span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3);
|
||||
<span style="color: #afeeee; font-weight: bold;">function</span> <span style="color: #7fffd4; font-weight: bold;">Array</span>() { ... }
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">case 2. declared in a function
|
||||
</span> (<span style="color: #afeeee; font-weight: bold;">function</span>(){
|
||||
a = <span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3);
|
||||
b = Array(5, 6);
|
||||
<span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">Array</span>;
|
||||
})();
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">or
|
||||
</span> (<span style="color: #afeeee; font-weight: bold;">function</span>(<span style="color: #40e0d0; font-weight: bold;">Array</span>){
|
||||
<span style="color: #afeeee; font-weight: bold;">return</span> Array(5, 6, 7);
|
||||
})();
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">or
|
||||
</span> (<span style="color: #afeeee; font-weight: bold;">function</span>(){
|
||||
<span style="color: #afeeee; font-weight: bold;">return</span> <span style="color: #afeeee; font-weight: bold;">new</span> <span style="color: #87ceeb; font-weight: bold;">Array</span>(1, 2, 3, 4);
|
||||
<span style="color: #afeeee; font-weight: bold;">function</span> <span style="color: #7fffd4; font-weight: bold;">Array</span>() { ... }
|
||||
})();
|
||||
|
||||
<span style="color: #add8e6;">// </span><span style="color: #add8e6;">etc.
|
||||
</span></pre>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_2" class="outline-3">
|
||||
<h3 id="sec-1_2"><span class="section-number-3">1.2</span> Usage </h3>
|
||||
<div class="outline-text-3" id="text-1_2">
|
||||
|
||||
|
||||
<p>
|
||||
There is a helper script now — <code>bin/uglifyjs</code> — that uses the library to
|
||||
compress a script using the maximum compression settings. Synopsis:
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<pre class="src src-sh">uglifyjs [ options... ] [ filename ]
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<code>filename</code> should be the last argument and should name the file from which
|
||||
to read the JavaScript code. If you don't specify it, it will read code
|
||||
from STDIN.
|
||||
</p>
|
||||
<p>
|
||||
Supported options:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>-b</code> or <code>--beautify</code> — output indented code; when passed, additional
|
||||
options control the beautifier:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>-i N</code> or <code>--indent N</code> — indentation level (number of spaces)
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-q</code> or <code>--quote-keys</code> — quote keys in literal objects (by default,
|
||||
only keys that cannot be identifier names will be quotes).
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>--ascii</code> — pass this argument to encode non-ASCII characters as
|
||||
<code>\uXXXX</code> sequences. By default UglifyJS won't bother to do it and will
|
||||
output Unicode characters instead. (the output is always encoded in UTF8,
|
||||
but if you pass this option you'll only get ASCII).
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-nm</code> or <code>--no-mangle</code> — don't mangle variable names
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-ns</code> or <code>--no-squeeze</code> — don't call <code>ast_squeeze()</code> (which does various
|
||||
optimizations that result in smaller, less readable code).
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-mt</code> or <code>--mangle-toplevel</code> — mangle names in the toplevel scope too
|
||||
(by default we don't do this).
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--no-seqs</code> — when <code>ast_squeeze()</code> is called (thus, unless you pass
|
||||
<code>--no-squeeze</code>) it will reduce consecutive statements in blocks into a
|
||||
sequence. For example, "a = 10; b = 20; foo();" will be written as
|
||||
"a=10,b=20,foo();". In various occasions, this allows us to discard the
|
||||
block brackets (since the block becomes a single statement). This is ON
|
||||
by default because it seems safe and saves a few hundred bytes on some
|
||||
libs that I tested it on, but pass <code>--no-seqs</code> to disable it.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--no-dead-code</code> — by default, UglifyJS will remove code that is
|
||||
obviously unreachable (code that follows a <code>return</code>, <code>throw</code>, <code>break</code> or
|
||||
<code>continue</code> statement and is not a function/variable declaration). Pass
|
||||
this option to disable this optimization.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-nc</code> or <code>--no-copyright</code> — by default, <code>uglifyjs</code> will keep the initial
|
||||
comment tokens in the generated code (assumed to be copyright information
|
||||
etc.). If you pass this it will discard it.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-o filename</code> or <code>--output filename</code> — put the result in <code>filename</code>. If
|
||||
this isn't given, the result goes to standard output (or see next one).
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--overwrite</code> — if the code is read from a file (not from STDIN) and you
|
||||
pass <code>--overwrite</code> then the output will be written in the same file.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--ast</code> — pass this if you want to get the Abstract Syntax Tree instead
|
||||
of JavaScript as output. Useful for debugging or learning more about the
|
||||
internals.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>-v</code> or <code>--verbose</code> — output some notes on STDERR (for now just how long
|
||||
each operation takes).
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--extra</code> — enable additional optimizations that have not yet been
|
||||
extensively tested. These might, or might not, break your code. If you
|
||||
find a bug using this option, please report a test case.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--unsafe</code> — enable other additional optimizations that are known to be
|
||||
unsafe in some contrived situations, but could still be generally useful.
|
||||
For now only this:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
foo.toString() ==> foo+""
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>--max-line-len</code> (default 32K characters) — add a newline after around
|
||||
32K characters. I've seen both FF and Chrome croak when all the code was
|
||||
on a single line of around 670K. Pass –max-line-len 0 to disable this
|
||||
safety feature.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>--reserved-names</code> — some libraries rely on certain names to be used, as
|
||||
pointed out in issue #92 and #81, so this option allow you to exclude such
|
||||
names from the mangler. For example, to keep names <code>require</code> and <code>$super</code>
|
||||
intact you'd specify –reserved-names "require,$super".
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_2_1" class="outline-4">
|
||||
<h4 id="sec-1_2_1"><span class="section-number-4">1.2.1</span> API </h4>
|
||||
<div class="outline-text-4" id="text-1_2_1">
|
||||
|
||||
|
||||
<p>
|
||||
Symlink the <b>lib</b> directory as <b>~/.node_libraries/uglifyjs</b>, so that the
|
||||
<b>require</b> calls in the following sample will work:
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<pre class="src src-espresso"><span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">jsp</span> = require(<span style="color: #87cefa;">"uglifyjs/parse-js"</span>);
|
||||
<span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">pro</span> = require(<span style="color: #87cefa;">"uglifyjs/process"</span>);
|
||||
|
||||
<span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">orig_code</span> = <span style="color: #87cefa;">"... JS code here"</span>;
|
||||
<span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">ast</span> = jsp.parse(orig_code); <span style="color: #add8e6;">// </span><span style="color: #add8e6;">parse code and get the initial AST
|
||||
</span>ast = pro.ast_mangle(ast); <span style="color: #add8e6;">// </span><span style="color: #add8e6;">get a new AST with mangled names
|
||||
</span>ast = pro.ast_squeeze(ast); <span style="color: #add8e6;">// </span><span style="color: #add8e6;">get an AST with compression optimizations
|
||||
</span><span style="color: #afeeee; font-weight: bold;">var</span> <span style="color: #40e0d0; font-weight: bold;">final_code</span> = pro.gen_code(ast); <span style="color: #add8e6;">// </span><span style="color: #add8e6;">compressed code here
|
||||
</span></pre>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
The above performs the full compression that is possible right now. As you
|
||||
can see, there are a sequence of steps which you can apply. For example if
|
||||
you want compressed output but for some reason you don't want to mangle
|
||||
variable names, you would simply skip the line that calls
|
||||
<code>pro.ast_mangle(ast)</code>.
|
||||
</p>
|
||||
<p>
|
||||
Some of these functions take optional arguments. Here's a description:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>jsp.parse(code, strict_semicolons)</code> – parses JS code and returns an AST.
|
||||
<code>strict_semicolons</code> is optional and defaults to <code>false</code>. If you pass
|
||||
<code>true</code> then the parser will throw an error when it expects a semicolon and
|
||||
it doesn't find it. For most JS code you don't want that, but it's useful
|
||||
if you want to strictly sanitize your code.
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>pro.ast_mangle(ast, options)</code> – generates a new AST containing mangled
|
||||
(compressed) variable and function names. It supports the following
|
||||
options:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>toplevel</code> – mangle toplevel names (by default we don't touch them).
|
||||
</li>
|
||||
<li>
|
||||
<code>except</code> – an array of names to exclude from compression.
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>pro.ast_squeeze(ast, options)</code> – employs further optimizations designed
|
||||
to reduce the size of the code that <code>gen_code</code> would generate from the
|
||||
AST. Returns a new AST. <code>options</code> can be a hash; the supported options
|
||||
are:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>make_seqs</code> (default true) which will cause consecutive statements in a
|
||||
block to be merged using the "sequence" (comma) operator
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<code>dead_code</code> (default true) which will remove unreachable code.
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<code>pro.gen_code(ast, options)</code> – generates JS code from the AST. By
|
||||
default it's minified, but using the <code>options</code> argument you can get nicely
|
||||
formatted output. <code>options</code> is, well, optional :-) and if you pass it it
|
||||
must be an object and supports the following properties (below you can see
|
||||
the default values):
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<code>beautify: false</code> – pass <code>true</code> if you want indented output
|
||||
</li>
|
||||
<li>
|
||||
<code>indent_start: 0</code> (only applies when <code>beautify</code> is <code>true</code>) – initial
|
||||
indentation in spaces
|
||||
</li>
|
||||
<li>
|
||||
<code>indent_level: 4</code> (only applies when <code>beautify</code> is <code>true</code>) --
|
||||
indentation level, in spaces (pass an even number)
|
||||
</li>
|
||||
<li>
|
||||
<code>quote_keys: false</code> – if you pass <code>true</code> it will quote all keys in
|
||||
literal objects
|
||||
</li>
|
||||
<li>
|
||||
<code>space_colon: false</code> (only applies when <code>beautify</code> is <code>true</code>) – wether
|
||||
to put a space before the colon in object literals
|
||||
</li>
|
||||
<li>
|
||||
<code>ascii_only: false</code> – pass <code>true</code> if you want to encode non-ASCII
|
||||
characters as <code>\uXXXX</code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_2_2" class="outline-4">
|
||||
<h4 id="sec-1_2_2"><span class="section-number-4">1.2.2</span> Beautifier shortcoming – no more comments </h4>
|
||||
<div class="outline-text-4" id="text-1_2_2">
|
||||
|
||||
|
||||
<p>
|
||||
The beautifier can be used as a general purpose indentation tool. It's
|
||||
useful when you want to make a minified file readable. One limitation,
|
||||
though, is that it discards all comments, so you don't really want to use it
|
||||
to reformat your code, unless you don't have, or don't care about, comments.
|
||||
</p>
|
||||
<p>
|
||||
In fact it's not the beautifier who discards comments — they are dumped at
|
||||
the parsing stage, when we build the initial AST. Comments don't really
|
||||
make sense in the AST, and while we could add nodes for them, it would be
|
||||
inconvenient because we'd have to add special rules to ignore them at all
|
||||
the processing stages.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_3" class="outline-3">
|
||||
<h3 id="sec-1_3"><span class="section-number-3">1.3</span> Compression – how good is it? </h3>
|
||||
<div class="outline-text-3" id="text-1_3">
|
||||
|
||||
|
||||
<p>
|
||||
(XXX: this is somewhat outdated. On the jQuery source code we beat Closure
|
||||
by 168 bytes (560 after gzip) and by many seconds.)
|
||||
</p>
|
||||
<p>
|
||||
There are a few popular JS minifiers nowadays – the two most well known
|
||||
being the GoogleClosure (GCL) compiler and the YUI compressor. For some
|
||||
reason they are both written in Java. I didn't really hope to beat any of
|
||||
them, but finally I did – UglifyJS compresses better than the YUI
|
||||
compressor, and safer than GoogleClosure.
|
||||
</p>
|
||||
<p>
|
||||
I tested it on two big libraries. <a href="http://www.dynarchlib.com/">DynarchLIB</a> is my own, and it's big enough
|
||||
to contain probably all the JavaScript tricks known to mankind. <a href="http://jquery.com/">jQuery</a> is
|
||||
definitely the most popular JavaScript library (to some people, it's a
|
||||
synonym to JavaScript itself).
|
||||
</p>
|
||||
<p>
|
||||
I cannot swear that there are no bugs in the generated codes, but they
|
||||
appear to work fine.
|
||||
</p>
|
||||
<p>
|
||||
Compression results:
|
||||
</p>
|
||||
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
|
||||
<caption></caption>
|
||||
<colgroup><col align="left" /><col align="right" /><col align="right" /><col align="left" /><col align="left" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr><th scope="col">Library</th><th scope="col">Orig. size</th><th scope="col">UglifyJS</th><th scope="col">YUI</th><th scope="col">GCL</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>DynarchLIB</td><td>636896</td><td>241441</td><td>246452 (+5011)</td><td>240439 (-1002) (buggy)</td></tr>
|
||||
<tr><td>jQuery</td><td>163855</td><td>72006</td><td>79702 (+7696)</td><td>71858 (-148)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<p>
|
||||
UglifyJS is the fastest to run. On my laptop UglifyJS takes 1.35s for
|
||||
DynarchLIB, while YUI takes 2.7s and GCL takes 6.5s.
|
||||
</p>
|
||||
<p>
|
||||
GoogleClosure does a lot of smart ass optimizations. I had to strive really
|
||||
hard to get close to it. It should be possible to even beat it, but then
|
||||
again, GCL has a gazillion lines of code and runs terribly slow, so I'm not
|
||||
sure it worths spending the effort to save a few bytes. Also, GCL doesn't
|
||||
cope with <code>eval()</code> or <code>with{}</code> – it just dumps a warning and proceeds to
|
||||
mangle names anyway; my DynarchLIB compiled with it is buggy because of
|
||||
this.
|
||||
</p>
|
||||
<p>
|
||||
UglifyJS consists of ~1100 lines of code for the tokenizer/parser, and ~1100
|
||||
lines for the compressor and code generator. That should make it very
|
||||
maintainable and easily extensible, so I would say it has a good place in
|
||||
this field and it's bound to become the de-facto standard JS minifier. And
|
||||
I shall rule the world. :-) Use it, and <b>spread the word</b>!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_4" class="outline-3">
|
||||
<h3 id="sec-1_4"><span class="section-number-3">1.4</span> Bugs? </h3>
|
||||
<div class="outline-text-3" id="text-1_4">
|
||||
|
||||
|
||||
<p>
|
||||
Unfortunately, for the time being there is no automated test suite. But I
|
||||
ran the compressor manually on non-trivial code, and then I tested that the
|
||||
generated code works as expected. A few hundred times.
|
||||
</p>
|
||||
<p>
|
||||
DynarchLIB was started in times when there was no good JS minifier.
|
||||
Therefore I was quite religious about trying to write short code manually,
|
||||
and as such DL contains a lot of syntactic hacks<sup><a class="footref" name="fnr.1" href="#fn.1">1</a></sup> such as “foo == bar ? a
|
||||
= 10 : b = 20”, though the more readable version would clearly be to use
|
||||
“if/else”.
|
||||
</p>
|
||||
<p>
|
||||
Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
|
||||
that it's solid enough for production use. If you can identify any bugs,
|
||||
I'd love to hear about them (<a href="http://groups.google.com/group/uglifyjs">use the Google Group</a> or email me directly).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_5" class="outline-3">
|
||||
<h3 id="sec-1_5"><span class="section-number-3">1.5</span> Links </h3>
|
||||
<div class="outline-text-3" id="text-1_5">
|
||||
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Project at GitHub: <a href="http://github.com/mishoo/UglifyJS">http://github.com/mishoo/UglifyJS</a>
|
||||
</li>
|
||||
<li>
|
||||
Google Group: <a href="http://groups.google.com/group/uglifyjs">http://groups.google.com/group/uglifyjs</a>
|
||||
</li>
|
||||
<li>
|
||||
Common Lisp JS parser: <a href="http://marijn.haverbeke.nl/parse-js/">http://marijn.haverbeke.nl/parse-js/</a>
|
||||
</li>
|
||||
<li>
|
||||
JS-to-Lisp compiler: <a href="http://github.com/marijnh/js">http://github.com/marijnh/js</a>
|
||||
</li>
|
||||
<li>
|
||||
Common Lisp JS uglifier: <a href="http://github.com/mishoo/cl-uglify-js">http://github.com/mishoo/cl-uglify-js</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="outline-container-1_6" class="outline-3">
|
||||
<h3 id="sec-1_6"><span class="section-number-3">1.6</span> License </h3>
|
||||
<div class="outline-text-3" id="text-1_6">
|
||||
|
||||
|
||||
<p>
|
||||
UglifyJS is released under the BSD license:
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<pre class="example">Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footnotes">
|
||||
<h2 class="footnotes">Footnotes: </h2>
|
||||
<div id="text-footnotes">
|
||||
<p class="footnote"><sup><a class="footnum" name="fn.1" href="#fnr.1">1</a></sup> I even reported a few bugs and suggested some fixes in the original
|
||||
<a href="http://marijn.haverbeke.nl/parse-js/">parse-js</a> library, and Marijn pushed fixes literally in minutes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="postamble">
|
||||
<p class="author"> Author: Mihai Bazon
|
||||
</p>
|
||||
<p class="date"> Date: 2011-02-28 22:35:00 EET</p>
|
||||
<p class="creator">HTML generated by org-mode 7.01trans in emacs 23</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
408
support/socket.io-client/lib/vendor/uglifyjs/README.org
vendored
Executable file
408
support/socket.io-client/lib/vendor/uglifyjs/README.org
vendored
Executable file
@@ -0,0 +1,408 @@
|
||||
#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier
|
||||
#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier
|
||||
#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript
|
||||
#+STYLE: <link rel="stylesheet" type="text/css" href="docstyle.css" />
|
||||
#+AUTHOR: Mihai Bazon
|
||||
#+EMAIL: mihai.bazon@gmail.com
|
||||
|
||||
* UglifyJS --- a JavaScript parser/compressor/beautifier
|
||||
|
||||
*Update*: please read the section on [[unsafe transformations]].
|
||||
|
||||
This package implements a general-purpose JavaScript
|
||||
parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it
|
||||
should work on any JavaScript platform supporting the CommonJS module system
|
||||
(and if your platform of choice doesn't support CommonJS, you can easily
|
||||
implement it, or discard the =exports.*= lines from UglifyJS sources).
|
||||
|
||||
The tokenizer/parser generates an abstract syntax tree from JS code. You
|
||||
can then traverse the AST to learn more about the code, or do various
|
||||
manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a
|
||||
port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn
|
||||
Haverbeke]].
|
||||
|
||||
( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of
|
||||
UglifyJS. )
|
||||
|
||||
The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and
|
||||
manipulates the AST generated by the parser to provide the following:
|
||||
|
||||
- ability to re-generate JavaScript code from the AST. Optionally
|
||||
indented---you can use this if you want to “beautify” a program that has
|
||||
been compressed, so that you can inspect the source. But you can also run
|
||||
our code generator to print out an AST without any whitespace, so you
|
||||
achieve compression as well.
|
||||
|
||||
- shorten variable names (usually to single characters). Our mangler will
|
||||
analyze the code and generate proper variable names, depending on scope
|
||||
and usage, and is smart enough to deal with globals defined elsewhere, or
|
||||
with =eval()= calls or =with{}= statements. In short, if =eval()= or
|
||||
=with{}= are used in some scope, then all variables in that scope and any
|
||||
variables in the parent scopes will remain unmangled, and any references
|
||||
to such variables remain unmangled as well.
|
||||
|
||||
- various small optimizations that may lead to faster code but certainly
|
||||
lead to smaller code. Where possible, we do the following:
|
||||
|
||||
- foo["bar"] ==> foo.bar
|
||||
|
||||
- remove block brackets ={}=
|
||||
|
||||
- join consecutive var declarations:
|
||||
var a = 10; var b = 20; ==> var a=10,b=20;
|
||||
|
||||
- resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the
|
||||
replacement if the result occupies less bytes; for example 1/3 would
|
||||
translate to 0.333333333333, so in this case we don't replace it.
|
||||
|
||||
- consecutive statements in blocks are merged into a sequence; in many
|
||||
cases, this leaves blocks with a single statement, so then we can remove
|
||||
the block brackets.
|
||||
|
||||
- various optimizations for IF statements:
|
||||
|
||||
- if (foo) bar(); else baz(); ==> foo?bar():baz();
|
||||
- if (!foo) bar(); else baz(); ==> foo?baz():bar();
|
||||
- if (foo) bar(); ==> foo&&bar();
|
||||
- if (!foo) bar(); ==> foo||bar();
|
||||
- if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
|
||||
- if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
|
||||
|
||||
- remove some unreachable code and warn about it (code that follows a
|
||||
=return=, =throw=, =break= or =continue= statement, except
|
||||
function/variable declarations).
|
||||
|
||||
** <<Unsafe transformations>>
|
||||
|
||||
UglifyJS tries its best to achieve great compression while leaving the
|
||||
semantics of the code intact. In general, if your code logic is broken by
|
||||
UglifyJS then it's a bug in UglifyJS and you should report it and I should
|
||||
fix it. :-)
|
||||
|
||||
However, I opted to include the following potentially unsafe transformations
|
||||
as default behavior. Discussion is welcome, if you have ideas of how to
|
||||
handle this better, or any objections to these optimizations, please let me
|
||||
know.
|
||||
|
||||
*** Calls involving the global Array constructor
|
||||
|
||||
The following transformations occur:
|
||||
|
||||
#+BEGIN_SRC espresso
|
||||
new Array(1, 2, 3, 4) => [1,2,3,4]
|
||||
Array(a, b, c) => [a,b,c]
|
||||
new Array(5) => Array(5)
|
||||
new Array(a) => Array(a)
|
||||
#+END_SRC
|
||||
|
||||
These are all safe if the Array name isn't redefined. JavaScript does allow
|
||||
one to globally redefine Array (and pretty much everything, in fact) but I
|
||||
personally don't see why would anyone do that.
|
||||
|
||||
UglifyJS does handle the case where Array is redefined locally, or even
|
||||
globally but with a =function= or =var= declaration. Therefore, in the
|
||||
following cases UglifyJS *doesn't touch* calls or instantiations of Array:
|
||||
|
||||
#+BEGIN_SRC espresso
|
||||
// case 1. globally declared variable
|
||||
var Array;
|
||||
new Array(1, 2, 3);
|
||||
Array(a, b);
|
||||
|
||||
// or (can be declared later)
|
||||
new Array(1, 2, 3);
|
||||
var Array;
|
||||
|
||||
// or (can be a function)
|
||||
new Array(1, 2, 3);
|
||||
function Array() { ... }
|
||||
|
||||
// case 2. declared in a function
|
||||
(function(){
|
||||
a = new Array(1, 2, 3);
|
||||
b = Array(5, 6);
|
||||
var Array;
|
||||
})();
|
||||
|
||||
// or
|
||||
(function(Array){
|
||||
return Array(5, 6, 7);
|
||||
})();
|
||||
|
||||
// or
|
||||
(function(){
|
||||
return new Array(1, 2, 3, 4);
|
||||
function Array() { ... }
|
||||
})();
|
||||
|
||||
// etc.
|
||||
#+END_SRC
|
||||
|
||||
** Usage
|
||||
|
||||
There is a helper script now --- =bin/uglifyjs= --- that uses the library to
|
||||
compress a script using the maximum compression settings. Synopsis:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
uglifyjs [ options... ] [ filename ]
|
||||
#+END_SRC
|
||||
|
||||
=filename= should be the last argument and should name the file from which
|
||||
to read the JavaScript code. If you don't specify it, it will read code
|
||||
from STDIN.
|
||||
|
||||
Supported options:
|
||||
|
||||
- =-b= or =--beautify= --- output indented code; when passed, additional
|
||||
options control the beautifier:
|
||||
|
||||
- =-i N= or =--indent N= --- indentation level (number of spaces)
|
||||
|
||||
- =-q= or =--quote-keys= --- quote keys in literal objects (by default,
|
||||
only keys that cannot be identifier names will be quotes).
|
||||
|
||||
- =--ascii= --- pass this argument to encode non-ASCII characters as
|
||||
=\uXXXX= sequences. By default UglifyJS won't bother to do it and will
|
||||
output Unicode characters instead. (the output is always encoded in UTF8,
|
||||
but if you pass this option you'll only get ASCII).
|
||||
|
||||
- =-nm= or =--no-mangle= --- don't mangle variable names
|
||||
|
||||
- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various
|
||||
optimizations that result in smaller, less readable code).
|
||||
|
||||
- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too
|
||||
(by default we don't do this).
|
||||
|
||||
- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass
|
||||
=--no-squeeze=) it will reduce consecutive statements in blocks into a
|
||||
sequence. For example, "a = 10; b = 20; foo();" will be written as
|
||||
"a=10,b=20,foo();". In various occasions, this allows us to discard the
|
||||
block brackets (since the block becomes a single statement). This is ON
|
||||
by default because it seems safe and saves a few hundred bytes on some
|
||||
libs that I tested it on, but pass =--no-seqs= to disable it.
|
||||
|
||||
- =--no-dead-code= --- by default, UglifyJS will remove code that is
|
||||
obviously unreachable (code that follows a =return=, =throw=, =break= or
|
||||
=continue= statement and is not a function/variable declaration). Pass
|
||||
this option to disable this optimization.
|
||||
|
||||
- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial
|
||||
comment tokens in the generated code (assumed to be copyright information
|
||||
etc.). If you pass this it will discard it.
|
||||
|
||||
- =-o filename= or =--output filename= --- put the result in =filename=. If
|
||||
this isn't given, the result goes to standard output (or see next one).
|
||||
|
||||
- =--overwrite= --- if the code is read from a file (not from STDIN) and you
|
||||
pass =--overwrite= then the output will be written in the same file.
|
||||
|
||||
- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead
|
||||
of JavaScript as output. Useful for debugging or learning more about the
|
||||
internals.
|
||||
|
||||
- =-v= or =--verbose= --- output some notes on STDERR (for now just how long
|
||||
each operation takes).
|
||||
|
||||
- =--extra= --- enable additional optimizations that have not yet been
|
||||
extensively tested. These might, or might not, break your code. If you
|
||||
find a bug using this option, please report a test case.
|
||||
|
||||
- =--unsafe= --- enable other additional optimizations that are known to be
|
||||
unsafe in some contrived situations, but could still be generally useful.
|
||||
For now only this:
|
||||
|
||||
- foo.toString() ==> foo+""
|
||||
|
||||
- =--max-line-len= (default 32K characters) --- add a newline after around
|
||||
32K characters. I've seen both FF and Chrome croak when all the code was
|
||||
on a single line of around 670K. Pass --max-line-len 0 to disable this
|
||||
safety feature.
|
||||
|
||||
- =--reserved-names= --- some libraries rely on certain names to be used, as
|
||||
pointed out in issue #92 and #81, so this option allow you to exclude such
|
||||
names from the mangler. For example, to keep names =require= and =$super=
|
||||
intact you'd specify --reserved-names "require,$super".
|
||||
|
||||
*** API
|
||||
|
||||
Symlink the *lib* directory as *~/.node\_libraries/uglifyjs*, so that the
|
||||
*require* calls in the following sample will work:
|
||||
|
||||
#+BEGIN_SRC espresso
|
||||
var jsp = require("uglifyjs/parse-js");
|
||||
var pro = require("uglifyjs/process");
|
||||
|
||||
var orig_code = "... JS code here";
|
||||
var ast = jsp.parse(orig_code); // parse code and get the initial AST
|
||||
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
||||
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
|
||||
var final_code = pro.gen_code(ast); // compressed code here
|
||||
#+END_SRC
|
||||
|
||||
The above performs the full compression that is possible right now. As you
|
||||
can see, there are a sequence of steps which you can apply. For example if
|
||||
you want compressed output but for some reason you don't want to mangle
|
||||
variable names, you would simply skip the line that calls
|
||||
=pro.ast_mangle(ast)=.
|
||||
|
||||
Some of these functions take optional arguments. Here's a description:
|
||||
|
||||
- =jsp.parse(code, strict_semicolons)= -- parses JS code and returns an AST.
|
||||
=strict_semicolons= is optional and defaults to =false=. If you pass
|
||||
=true= then the parser will throw an error when it expects a semicolon and
|
||||
it doesn't find it. For most JS code you don't want that, but it's useful
|
||||
if you want to strictly sanitize your code.
|
||||
|
||||
- =pro.ast_mangle(ast, options)= -- generates a new AST containing mangled
|
||||
(compressed) variable and function names. It supports the following
|
||||
options:
|
||||
|
||||
- =toplevel= -- mangle toplevel names (by default we don't touch them).
|
||||
- =except= -- an array of names to exclude from compression.
|
||||
|
||||
- =pro.ast_squeeze(ast, options)= -- employs further optimizations designed
|
||||
to reduce the size of the code that =gen_code= would generate from the
|
||||
AST. Returns a new AST. =options= can be a hash; the supported options
|
||||
are:
|
||||
|
||||
- =make_seqs= (default true) which will cause consecutive statements in a
|
||||
block to be merged using the "sequence" (comma) operator
|
||||
|
||||
- =dead_code= (default true) which will remove unreachable code.
|
||||
|
||||
- =pro.gen_code(ast, options)= -- generates JS code from the AST. By
|
||||
default it's minified, but using the =options= argument you can get nicely
|
||||
formatted output. =options= is, well, optional :-) and if you pass it it
|
||||
must be an object and supports the following properties (below you can see
|
||||
the default values):
|
||||
|
||||
- =beautify: false= -- pass =true= if you want indented output
|
||||
- =indent_start: 0= (only applies when =beautify= is =true=) -- initial
|
||||
indentation in spaces
|
||||
- =indent_level: 4= (only applies when =beautify= is =true=) --
|
||||
indentation level, in spaces (pass an even number)
|
||||
- =quote_keys: false= -- if you pass =true= it will quote all keys in
|
||||
literal objects
|
||||
- =space_colon: false= (only applies when =beautify= is =true=) -- wether
|
||||
to put a space before the colon in object literals
|
||||
- =ascii_only: false= -- pass =true= if you want to encode non-ASCII
|
||||
characters as =\uXXXX=.
|
||||
|
||||
*** Beautifier shortcoming -- no more comments
|
||||
|
||||
The beautifier can be used as a general purpose indentation tool. It's
|
||||
useful when you want to make a minified file readable. One limitation,
|
||||
though, is that it discards all comments, so you don't really want to use it
|
||||
to reformat your code, unless you don't have, or don't care about, comments.
|
||||
|
||||
In fact it's not the beautifier who discards comments --- they are dumped at
|
||||
the parsing stage, when we build the initial AST. Comments don't really
|
||||
make sense in the AST, and while we could add nodes for them, it would be
|
||||
inconvenient because we'd have to add special rules to ignore them at all
|
||||
the processing stages.
|
||||
|
||||
** Compression -- how good is it?
|
||||
|
||||
(XXX: this is somewhat outdated. On the jQuery source code we beat Closure
|
||||
by 168 bytes (560 after gzip) and by many seconds.)
|
||||
|
||||
There are a few popular JS minifiers nowadays -- the two most well known
|
||||
being the GoogleClosure (GCL) compiler and the YUI compressor. For some
|
||||
reason they are both written in Java. I didn't really hope to beat any of
|
||||
them, but finally I did -- UglifyJS compresses better than the YUI
|
||||
compressor, and safer than GoogleClosure.
|
||||
|
||||
I tested it on two big libraries. [[http://www.dynarchlib.com/][DynarchLIB]] is my own, and it's big enough
|
||||
to contain probably all the JavaScript tricks known to mankind. [[http://jquery.com/][jQuery]] is
|
||||
definitely the most popular JavaScript library (to some people, it's a
|
||||
synonym to JavaScript itself).
|
||||
|
||||
I cannot swear that there are no bugs in the generated codes, but they
|
||||
appear to work fine.
|
||||
|
||||
Compression results:
|
||||
|
||||
| Library | Orig. size | UglifyJS | YUI | GCL |
|
||||
|------------+------------+----------+----------------+------------------------|
|
||||
| DynarchLIB | 636896 | 241441 | 246452 (+5011) | 240439 (-1002) (buggy) |
|
||||
| jQuery | 163855 | 72006 | 79702 (+7696) | 71858 (-148) |
|
||||
|
||||
UglifyJS is the fastest to run. On my laptop UglifyJS takes 1.35s for
|
||||
DynarchLIB, while YUI takes 2.7s and GCL takes 6.5s.
|
||||
|
||||
GoogleClosure does a lot of smart ass optimizations. I had to strive really
|
||||
hard to get close to it. It should be possible to even beat it, but then
|
||||
again, GCL has a gazillion lines of code and runs terribly slow, so I'm not
|
||||
sure it worths spending the effort to save a few bytes. Also, GCL doesn't
|
||||
cope with =eval()= or =with{}= -- it just dumps a warning and proceeds to
|
||||
mangle names anyway; my DynarchLIB compiled with it is buggy because of
|
||||
this.
|
||||
|
||||
UglifyJS consists of ~1100 lines of code for the tokenizer/parser, and ~1100
|
||||
lines for the compressor and code generator. That should make it very
|
||||
maintainable and easily extensible, so I would say it has a good place in
|
||||
this field and it's bound to become the de-facto standard JS minifier. And
|
||||
I shall rule the world. :-) Use it, and *spread the word*!
|
||||
|
||||
** Bugs?
|
||||
|
||||
Unfortunately, for the time being there is no automated test suite. But I
|
||||
ran the compressor manually on non-trivial code, and then I tested that the
|
||||
generated code works as expected. A few hundred times.
|
||||
|
||||
DynarchLIB was started in times when there was no good JS minifier.
|
||||
Therefore I was quite religious about trying to write short code manually,
|
||||
and as such DL contains a lot of syntactic hacks[1] such as “foo == bar ? a
|
||||
= 10 : b = 20”, though the more readable version would clearly be to use
|
||||
“if/else”.
|
||||
|
||||
Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
|
||||
that it's solid enough for production use. If you can identify any bugs,
|
||||
I'd love to hear about them ([[http://groups.google.com/group/uglifyjs][use the Google Group]] or email me directly).
|
||||
|
||||
[1] I even reported a few bugs and suggested some fixes in the original
|
||||
[[http://marijn.haverbeke.nl/parse-js/][parse-js]] library, and Marijn pushed fixes literally in minutes.
|
||||
|
||||
** Links
|
||||
|
||||
- Project at GitHub: [[http://github.com/mishoo/UglifyJS][http://github.com/mishoo/UglifyJS]]
|
||||
- Google Group: [[http://groups.google.com/group/uglifyjs][http://groups.google.com/group/uglifyjs]]
|
||||
- Common Lisp JS parser: [[http://marijn.haverbeke.nl/parse-js/][http://marijn.haverbeke.nl/parse-js/]]
|
||||
- JS-to-Lisp compiler: [[http://github.com/marijnh/js][http://github.com/marijnh/js]]
|
||||
- Common Lisp JS uglifier: [[http://github.com/mishoo/cl-uglify-js][http://github.com/mishoo/cl-uglify-js]]
|
||||
|
||||
** License
|
||||
|
||||
UglifyJS is released under the BSD license:
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
#+END_EXAMPLE
|
||||
212
support/socket.io-client/lib/vendor/uglifyjs/bin/uglifyjs
vendored
Executable file
212
support/socket.io-client/lib/vendor/uglifyjs/bin/uglifyjs
vendored
Executable file
@@ -0,0 +1,212 @@
|
||||
#! /usr/bin/env node
|
||||
// -*- js2 -*-
|
||||
|
||||
global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
|
||||
var fs = require("fs");
|
||||
var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
|
||||
jsp = uglify.parser,
|
||||
pro = uglify.uglify;
|
||||
|
||||
var options = {
|
||||
ast: false,
|
||||
mangle: true,
|
||||
mangle_toplevel: false,
|
||||
squeeze: true,
|
||||
make_seqs: true,
|
||||
dead_code: true,
|
||||
verbose: false,
|
||||
show_copyright: true,
|
||||
out_same_file: false,
|
||||
max_line_length: 32 * 1024,
|
||||
unsafe: false,
|
||||
reserved_names: null,
|
||||
codegen_options: {
|
||||
ascii_only: false,
|
||||
beautify: false,
|
||||
indent_level: 4,
|
||||
indent_start: 0,
|
||||
quote_keys: false,
|
||||
space_colon: false
|
||||
},
|
||||
output: true // stdout
|
||||
};
|
||||
|
||||
var args = jsp.slice(process.argv, 2);
|
||||
var filename;
|
||||
|
||||
out: while (args.length > 0) {
|
||||
var v = args.shift();
|
||||
switch (v) {
|
||||
case "-b":
|
||||
case "--beautify":
|
||||
options.codegen_options.beautify = true;
|
||||
break;
|
||||
case "-i":
|
||||
case "--indent":
|
||||
options.codegen_options.indent_level = args.shift();
|
||||
break;
|
||||
case "-q":
|
||||
case "--quote-keys":
|
||||
options.codegen_options.quote_keys = true;
|
||||
break;
|
||||
case "-mt":
|
||||
case "--mangle-toplevel":
|
||||
options.mangle_toplevel = true;
|
||||
break;
|
||||
case "--no-mangle":
|
||||
case "-nm":
|
||||
options.mangle = false;
|
||||
break;
|
||||
case "--no-squeeze":
|
||||
case "-ns":
|
||||
options.squeeze = false;
|
||||
break;
|
||||
case "--no-seqs":
|
||||
options.make_seqs = false;
|
||||
break;
|
||||
case "--no-dead-code":
|
||||
options.dead_code = false;
|
||||
break;
|
||||
case "--no-copyright":
|
||||
case "-nc":
|
||||
options.show_copyright = false;
|
||||
break;
|
||||
case "-o":
|
||||
case "--output":
|
||||
options.output = args.shift();
|
||||
break;
|
||||
case "--overwrite":
|
||||
options.out_same_file = true;
|
||||
break;
|
||||
case "-v":
|
||||
case "--verbose":
|
||||
options.verbose = true;
|
||||
break;
|
||||
case "--ast":
|
||||
options.ast = true;
|
||||
break;
|
||||
case "--unsafe":
|
||||
options.unsafe = true;
|
||||
break;
|
||||
case "--max-line-len":
|
||||
options.max_line_length = parseInt(args.shift(), 10);
|
||||
break;
|
||||
case "--reserved-names":
|
||||
options.reserved_names = args.shift().split(",");
|
||||
break;
|
||||
case "--ascii":
|
||||
options.codegen_options.ascii_only = true;
|
||||
break;
|
||||
default:
|
||||
filename = v;
|
||||
break out;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.verbose) {
|
||||
pro.set_logger(function(msg){
|
||||
sys.debug(msg);
|
||||
});
|
||||
}
|
||||
|
||||
jsp.set_logger(function(msg){
|
||||
sys.debug(msg);
|
||||
});
|
||||
|
||||
if (filename) {
|
||||
fs.readFile(filename, "utf8", function(err, text){
|
||||
if (err) throw err;
|
||||
output(squeeze_it(text));
|
||||
});
|
||||
} else {
|
||||
var stdin = process.openStdin();
|
||||
stdin.setEncoding("utf8");
|
||||
var text = "";
|
||||
stdin.on("data", function(chunk){
|
||||
text += chunk;
|
||||
});
|
||||
stdin.on("end", function() {
|
||||
output(squeeze_it(text));
|
||||
});
|
||||
}
|
||||
|
||||
function output(text) {
|
||||
var out;
|
||||
if (options.out_same_file && filename)
|
||||
options.output = filename;
|
||||
if (options.output === true) {
|
||||
out = process.stdout;
|
||||
} else {
|
||||
out = fs.createWriteStream(options.output, {
|
||||
flags: "w",
|
||||
encoding: "utf8",
|
||||
mode: 0644
|
||||
});
|
||||
}
|
||||
out.write(text);
|
||||
if (options.output !== true) {
|
||||
out.end();
|
||||
}
|
||||
};
|
||||
|
||||
// --------- main ends here.
|
||||
|
||||
function show_copyright(comments) {
|
||||
var ret = "";
|
||||
for (var i = 0; i < comments.length; ++i) {
|
||||
var c = comments[i];
|
||||
if (c.type == "comment1") {
|
||||
ret += "//" + c.value + "\n";
|
||||
} else {
|
||||
ret += "/*" + c.value + "*/";
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
function squeeze_it(code) {
|
||||
var result = "";
|
||||
if (options.show_copyright) {
|
||||
var tok = jsp.tokenizer(code), c;
|
||||
c = tok();
|
||||
result += show_copyright(c.comments_before);
|
||||
}
|
||||
try {
|
||||
var ast = time_it("parse", function(){ return jsp.parse(code); });
|
||||
if (options.mangle) ast = time_it("mangle", function(){
|
||||
return pro.ast_mangle(ast, {
|
||||
toplevel: options.mangle_toplevel,
|
||||
except: options.reserved_names
|
||||
});
|
||||
});
|
||||
if (options.squeeze) ast = time_it("squeeze", function(){
|
||||
ast = pro.ast_squeeze(ast, {
|
||||
make_seqs : options.make_seqs,
|
||||
dead_code : options.dead_code,
|
||||
keep_comps : !options.unsafe
|
||||
});
|
||||
if (options.unsafe)
|
||||
ast = pro.ast_squeeze_more(ast);
|
||||
return ast;
|
||||
});
|
||||
if (options.ast)
|
||||
return sys.inspect(ast, null, null);
|
||||
result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
|
||||
if (!options.codegen_options.beautify && options.max_line_length) {
|
||||
result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
|
||||
}
|
||||
return result;
|
||||
} catch(ex) {
|
||||
sys.debug(ex.stack);
|
||||
sys.debug(sys.inspect(ex));
|
||||
sys.debug(JSON.stringify(ex));
|
||||
}
|
||||
};
|
||||
|
||||
function time_it(name, cont) {
|
||||
if (!options.verbose)
|
||||
return cont();
|
||||
var t1 = new Date().getTime();
|
||||
try { return cont(); }
|
||||
finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
|
||||
};
|
||||
75
support/socket.io-client/lib/vendor/uglifyjs/docstyle.css
vendored
Executable file
75
support/socket.io-client/lib/vendor/uglifyjs/docstyle.css
vendored
Executable file
@@ -0,0 +1,75 @@
|
||||
html { font-family: "Lucida Grande","Trebuchet MS",sans-serif; font-size: 12pt; }
|
||||
body { max-width: 60em; }
|
||||
.title { text-align: center; }
|
||||
.todo { color: red; }
|
||||
.done { color: green; }
|
||||
.tag { background-color:lightblue; font-weight:normal }
|
||||
.target { }
|
||||
.timestamp { color: grey }
|
||||
.timestamp-kwd { color: CadetBlue }
|
||||
p.verse { margin-left: 3% }
|
||||
pre {
|
||||
border: 1pt solid #AEBDCC;
|
||||
background-color: #F3F5F7;
|
||||
padding: 5pt;
|
||||
font-family: monospace;
|
||||
font-size: 90%;
|
||||
overflow:auto;
|
||||
}
|
||||
pre.src {
|
||||
background-color: #333; color: #ffd; border: 1px solid #000;
|
||||
}
|
||||
table { border-collapse: collapse; }
|
||||
td, th { vertical-align: top; }
|
||||
dt { font-weight: bold; }
|
||||
div.figure { padding: 0.5em; }
|
||||
div.figure p { text-align: center; }
|
||||
.linenr { font-size:smaller }
|
||||
.code-highlighted {background-color:#ffff00;}
|
||||
.org-info-js_info-navigation { border-style:none; }
|
||||
#org-info-js_console-label { font-size:10px; font-weight:bold;
|
||||
white-space:nowrap; }
|
||||
.org-info-js_search-highlight {background-color:#ffff00; color:#000000;
|
||||
font-weight:bold; }
|
||||
|
||||
sup {
|
||||
vertical-align: baseline;
|
||||
position: relative;
|
||||
top: -0.5em;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sup a:link, sup a:visited {
|
||||
text-decoration: none;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
sup a:before { content: "["; color: #999; }
|
||||
sup a:after { content: "]"; color: #999; }
|
||||
|
||||
h1.title { border-bottom: 4px solid #000; padding-bottom: 5px; margin-bottom: 2em; }
|
||||
|
||||
#postamble {
|
||||
color: #777;
|
||||
font-size: 90%;
|
||||
padding-top: 1em; padding-bottom: 1em; border-top: 1px solid #999;
|
||||
margin-top: 2em;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#postamble p { margin: 0; }
|
||||
|
||||
#footnotes { border-top: 1px solid #000; }
|
||||
|
||||
h1 { font-size: 200% }
|
||||
h2 { font-size: 175% }
|
||||
h3 { font-size: 150% }
|
||||
h4 { font-size: 125% }
|
||||
|
||||
h1, h2, h3, h4 { font-family: "Bookman",Georgia,"Times New Roman",serif; font-weight: normal; }
|
||||
|
||||
@media print {
|
||||
html { font-size: 11pt; }
|
||||
}
|
||||
2
support/socket.io-client/lib/vendor/uglifyjs/index.js
vendored
Executable file
2
support/socket.io-client/lib/vendor/uglifyjs/index.js
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
exports.parser = require("./lib/parse-js");
|
||||
exports.uglify = require("./lib/process");
|
||||
1320
support/socket.io-client/lib/vendor/uglifyjs/lib/parse-js.js
vendored
Executable file
1320
support/socket.io-client/lib/vendor/uglifyjs/lib/parse-js.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1609
support/socket.io-client/lib/vendor/uglifyjs/lib/process.js
vendored
Executable file
1609
support/socket.io-client/lib/vendor/uglifyjs/lib/process.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
22
support/socket.io-client/lib/vendor/uglifyjs/lib/squeeze-more.js
vendored
Executable file
22
support/socket.io-client/lib/vendor/uglifyjs/lib/squeeze-more.js
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
var jsp = require("./parse-js"),
|
||||
pro = require("./process"),
|
||||
slice = jsp.slice,
|
||||
member = jsp.member,
|
||||
PRECEDENCE = jsp.PRECEDENCE,
|
||||
OPERATORS = jsp.OPERATORS;
|
||||
|
||||
function ast_squeeze_more(ast) {
|
||||
var w = pro.ast_walker(), walk = w.walk;
|
||||
return w.with_walkers({
|
||||
"call": function(expr, args) {
|
||||
if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
|
||||
// foo.toString() ==> foo+""
|
||||
return [ "binary", "+", expr[1], [ "string", "" ]];
|
||||
}
|
||||
}
|
||||
}, function() {
|
||||
return walk(ast);
|
||||
});
|
||||
};
|
||||
|
||||
exports.ast_squeeze_more = ast_squeeze_more;
|
||||
6
support/socket.io-client/lib/vendor/uglifyjs/package.json
vendored
Executable file
6
support/socket.io-client/lib/vendor/uglifyjs/package.json
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
{"name" : "uglify-js",
|
||||
"author" : "Mihai Bazon - http://github.com/mishoo",
|
||||
"version" : "0.0.1",
|
||||
"main" : "index.js",
|
||||
"bin" : { "uglifyjs" : "./bin/uglifyjs" },
|
||||
}
|
||||
28
support/socket.io-client/lib/vendor/uglifyjs/test/beautify.js
vendored
Executable file
28
support/socket.io-client/lib/vendor/uglifyjs/test/beautify.js
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
global.sys = require("sys");
|
||||
var fs = require("fs");
|
||||
|
||||
var jsp = require("../lib/parse-js");
|
||||
var pro = require("../lib/process");
|
||||
|
||||
var filename = process.argv[2];
|
||||
fs.readFile(filename, "utf8", function(err, text){
|
||||
try {
|
||||
var ast = time_it("parse", function(){ return jsp.parse(text); });
|
||||
ast = time_it("mangle", function(){ return pro.ast_mangle(ast); });
|
||||
ast = time_it("squeeze", function(){ return pro.ast_squeeze(ast); });
|
||||
var gen = time_it("generate", function(){ return pro.gen_code(ast, false); });
|
||||
sys.puts(gen);
|
||||
} catch(ex) {
|
||||
sys.debug(ex.stack);
|
||||
sys.debug(sys.inspect(ex));
|
||||
sys.debug(JSON.stringify(ex));
|
||||
}
|
||||
});
|
||||
|
||||
function time_it(name, cont) {
|
||||
var t1 = new Date().getTime();
|
||||
try { return cont(); }
|
||||
finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
|
||||
};
|
||||
402
support/socket.io-client/lib/vendor/uglifyjs/test/testparser.js
vendored
Executable file
402
support/socket.io-client/lib/vendor/uglifyjs/test/testparser.js
vendored
Executable file
@@ -0,0 +1,402 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
var parseJS = require("../lib/parse-js");
|
||||
var sys = require("sys");
|
||||
|
||||
// write debug in a very straightforward manner
|
||||
var debug = function(){
|
||||
sys.log(Array.prototype.slice.call(arguments).join(', '));
|
||||
};
|
||||
|
||||
ParserTestSuite(function(i, input, desc){
|
||||
try {
|
||||
parseJS.parse(input);
|
||||
debug("ok " + i + ": " + desc);
|
||||
} catch(e){
|
||||
debug("FAIL " + i + " " + desc + " (" + e + ")");
|
||||
}
|
||||
});
|
||||
|
||||
function ParserTestSuite(callback){
|
||||
var inps = [
|
||||
["var abc;", "Regular variable statement w/o assignment"],
|
||||
["var abc = 5;", "Regular variable statement with assignment"],
|
||||
["/* */;", "Multiline comment"],
|
||||
['/** **/;', 'Double star multiline comment'],
|
||||
["var f = function(){;};", "Function expression in var assignment"],
|
||||
['hi; // moo\n;', 'single line comment'],
|
||||
['var varwithfunction;', 'Dont match keywords as substrings'], // difference between `var withsomevar` and `"str"` (local search and lits)
|
||||
['a + b;', 'addition'],
|
||||
["'a';", 'single string literal'],
|
||||
["'a\\n';", 'single string literal with escaped return'],
|
||||
['"a";', 'double string literal'],
|
||||
['"a\\n";', 'double string literal with escaped return'],
|
||||
['"var";', 'string is a keyword'],
|
||||
['"variable";', 'string starts with a keyword'],
|
||||
['"somevariable";', 'string contains a keyword'],
|
||||
['"somevar";', 'string ends with a keyword'],
|
||||
['500;', 'int literal'],
|
||||
['500.;', 'float literal w/o decimals'],
|
||||
['500.432;', 'float literal with decimals'],
|
||||
['.432432;', 'float literal w/o int'],
|
||||
['(a,b,c);', 'parens and comma'],
|
||||
['[1,2,abc];', 'array literal'],
|
||||
['var o = {a:1};', 'object literal unquoted key'],
|
||||
['var o = {"b":2};', 'object literal quoted key'], // opening curly may not be at the start of a statement...
|
||||
['var o = {c:c};', 'object literal keyname is identifier'],
|
||||
['var o = {a:1,"b":2,c:c};', 'object literal combinations'],
|
||||
['var x;\nvar y;', 'two lines'],
|
||||
['var x;\nfunction n(){; }', 'function def'],
|
||||
['var x;\nfunction n(abc){; }', 'function def with arg'],
|
||||
['var x;\nfunction n(abc, def){ ;}', 'function def with args'],
|
||||
['function n(){ "hello"; }', 'function def with body'],
|
||||
['/a/;', 'regex literal'],
|
||||
['/a/b;', 'regex literal with flag'],
|
||||
['/a/ / /b/;', 'regex div regex'],
|
||||
['a/b/c;', 'triple division looks like regex'],
|
||||
['+function(){/regex/;};', 'regex at start of function body'],
|
||||
// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=86
|
||||
// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=430
|
||||
|
||||
// first tests for the lexer, should also parse as program (when you append a semi)
|
||||
|
||||
// comments
|
||||
['//foo!@#^&$1234\nbar;', 'single line comment'],
|
||||
['/* abcd!@#@$* { } && null*/;', 'single line multi line comment'],
|
||||
['/*foo\nbar*/;','multi line comment'],
|
||||
['/*x*x*/;','multi line comment with *'],
|
||||
['/**/;','empty comment'],
|
||||
// identifiers
|
||||
["x;",'1 identifier'],
|
||||
["_x;",'2 identifier'],
|
||||
["xyz;",'3 identifier'],
|
||||
["$x;",'4 identifier'],
|
||||
["x$;",'5 identifier'],
|
||||
["_;",'6 identifier'],
|
||||
["x5;",'7 identifier'],
|
||||
["x_y;",'8 identifier'],
|
||||
["x+5;",'9 identifier'],
|
||||
["xyz123;",'10 identifier'],
|
||||
["x1y1z1;",'11 identifier'],
|
||||
["foo\\u00D8bar;",'12 identifier unicode escape'],
|
||||
//["foo<6F>bar;",'13 identifier unicode embedded (might fail)'],
|
||||
// numbers
|
||||
["5;", '1 number'],
|
||||
["5.5;", '2 number'],
|
||||
["0;", '3 number'],
|
||||
["0.0;", '4 number'],
|
||||
["0.001;", '5 number'],
|
||||
["1.e2;", '6 number'],
|
||||
["1.e-2;", '7 number'],
|
||||
["1.E2;", '8 number'],
|
||||
["1.E-2;", '9 number'],
|
||||
[".5;", '10 number'],
|
||||
[".5e3;", '11 number'],
|
||||
[".5e-3;", '12 number'],
|
||||
["0.5e3;", '13 number'],
|
||||
["55;", '14 number'],
|
||||
["123;", '15 number'],
|
||||
["55.55;", '16 number'],
|
||||
["55.55e10;", '17 number'],
|
||||
["123.456;", '18 number'],
|
||||
["1+e;", '20 number'],
|
||||
["0x01;", '22 number'],
|
||||
["0XCAFE;", '23 number'],
|
||||
["0x12345678;", '24 number'],
|
||||
["0x1234ABCD;", '25 number'],
|
||||
["0x0001;", '26 number'],
|
||||
// strings
|
||||
["\"foo\";", '1 string'],
|
||||
["\'foo\';", '2 string'],
|
||||
["\"x\";", '3 string'],
|
||||
["\'\';", '4 string'],
|
||||
["\"foo\\tbar\";", '5 string'],
|
||||
["\"!@#$%^&*()_+{}[]\";", '6 string'],
|
||||
["\"/*test*/\";", '7 string'],
|
||||
["\"//test\";", '8 string'],
|
||||
["\"\\\\\";", '9 string'],
|
||||
["\"\\u0001\";", '10 string'],
|
||||
["\"\\uFEFF\";", '11 string'],
|
||||
["\"\\u10002\";", '12 string'],
|
||||
["\"\\x55\";", '13 string'],
|
||||
["\"\\x55a\";", '14 string'],
|
||||
["\"a\\\\nb\";", '15 string'],
|
||||
['";"', '16 string: semi in a string'],
|
||||
['"a\\\nb";', '17 string: line terminator escape'],
|
||||
// literals
|
||||
["null;", "null"],
|
||||
["true;", "true"],
|
||||
["false;", "false"],
|
||||
// regex
|
||||
["/a/;", "1 regex"],
|
||||
["/abc/;", "2 regex"],
|
||||
["/abc[a-z]*def/g;", "3 regex"],
|
||||
["/\\b/;", "4 regex"],
|
||||
["/[a-zA-Z]/;", "5 regex"],
|
||||
|
||||
// program tests (for as far as they havent been covered above)
|
||||
|
||||
// regexp
|
||||
["/foo(.*)/g;", "another regexp"],
|
||||
// arrays
|
||||
["[];", "1 array"],
|
||||
["[ ];", "2 array"],
|
||||
["[1];", "3 array"],
|
||||
["[1,2];", "4 array"],
|
||||
["[1,2,,];", "5 array"],
|
||||
["[1,2,3];", "6 array"],
|
||||
["[1,2,3,,,];", "7 array"],
|
||||
// objects
|
||||
["{};", "1 object"],
|
||||
["({x:5});", "2 object"],
|
||||
["({x:5,y:6});", "3 object"],
|
||||
["({x:5,});", "4 object"],
|
||||
["({if:5});", "5 object"],
|
||||
["({ get x() {42;} });", "6 object"],
|
||||
["({ set y(a) {1;} });", "7 object"],
|
||||
// member expression
|
||||
["o.m;", "1 member expression"],
|
||||
["o['m'];", "2 member expression"],
|
||||
["o['n']['m'];", "3 member expression"],
|
||||
["o.n.m;", "4 member expression"],
|
||||
["o.if;", "5 member expression"],
|
||||
// call and invoke expressions
|
||||
["f();", "1 call/invoke expression"],
|
||||
["f(x);", "2 call/invoke expression"],
|
||||
["f(x,y);", "3 call/invoke expression"],
|
||||
["o.m();", "4 call/invoke expression"],
|
||||
["o['m'];", "5 call/invoke expression"],
|
||||
["o.m(x);", "6 call/invoke expression"],
|
||||
["o['m'](x);", "7 call/invoke expression"],
|
||||
["o.m(x,y);", "8 call/invoke expression"],
|
||||
["o['m'](x,y);", "9 call/invoke expression"],
|
||||
["f(x)(y);", "10 call/invoke expression"],
|
||||
["f().x;", "11 call/invoke expression"],
|
||||
|
||||
// eval
|
||||
["eval('x');", "1 eval"],
|
||||
["(eval)('x');", "2 eval"],
|
||||
["(1,eval)('x');", "3 eval"],
|
||||
["eval(x,y);", "4 eval"],
|
||||
// new expression
|
||||
["new f();", "1 new expression"],
|
||||
["new o;", "2 new expression"],
|
||||
["new o.m;", "3 new expression"],
|
||||
["new o.m(x);", "4 new expression"],
|
||||
["new o.m(x,y);", "5 new expression"],
|
||||
// prefix/postfix
|
||||
["++x;", "1 pre/postfix"],
|
||||
["x++;", "2 pre/postfix"],
|
||||
["--x;", "3 pre/postfix"],
|
||||
["x--;", "4 pre/postfix"],
|
||||
["x ++;", "5 pre/postfix"],
|
||||
["x /* comment */ ++;", "6 pre/postfix"],
|
||||
["++ /* comment */ x;", "7 pre/postfix"],
|
||||
// unary operators
|
||||
["delete x;", "1 unary operator"],
|
||||
["void x;", "2 unary operator"],
|
||||
["+ x;", "3 unary operator"],
|
||||
["-x;", "4 unary operator"],
|
||||
["~x;", "5 unary operator"],
|
||||
["!x;", "6 unary operator"],
|
||||
// meh
|
||||
["new Date++;", "new date ++"],
|
||||
["+x++;", " + x ++"],
|
||||
// expression expressions
|
||||
["1 * 2;", "1 expression expressions"],
|
||||
["1 / 2;", "2 expression expressions"],
|
||||
["1 % 2;", "3 expression expressions"],
|
||||
["1 + 2;", "4 expression expressions"],
|
||||
["1 - 2;", "5 expression expressions"],
|
||||
["1 << 2;", "6 expression expressions"],
|
||||
["1 >>> 2;", "7 expression expressions"],
|
||||
["1 >> 2;", "8 expression expressions"],
|
||||
["1 * 2 + 3;", "9 expression expressions"],
|
||||
["(1+2)*3;", "10 expression expressions"],
|
||||
["1*(2+3);", "11 expression expressions"],
|
||||
["x<y;", "12 expression expressions"],
|
||||
["x>y;", "13 expression expressions"],
|
||||
["x<=y;", "14 expression expressions"],
|
||||
["x>=y;", "15 expression expressions"],
|
||||
["x instanceof y;", "16 expression expressions"],
|
||||
["x in y;", "17 expression expressions"],
|
||||
["x&y;", "18 expression expressions"],
|
||||
["x^y;", "19 expression expressions"],
|
||||
["x|y;", "20 expression expressions"],
|
||||
["x+y<z;", "21 expression expressions"],
|
||||
["x<y+z;", "22 expression expressions"],
|
||||
["x+y+z;", "23 expression expressions"],
|
||||
["x+y<z;", "24 expression expressions"],
|
||||
["x<y+z;", "25 expression expressions"],
|
||||
["x&y|z;", "26 expression expressions"],
|
||||
["x&&y;", "27 expression expressions"],
|
||||
["x||y;", "28 expression expressions"],
|
||||
["x&&y||z;", "29 expression expressions"],
|
||||
["x||y&&z;", "30 expression expressions"],
|
||||
["x<y?z:w;", "31 expression expressions"],
|
||||
// assignment
|
||||
["x >>>= y;", "1 assignment"],
|
||||
["x <<= y;", "2 assignment"],
|
||||
["x = y;", "3 assignment"],
|
||||
["x += y;", "4 assignment"],
|
||||
["x /= y;", "5 assignment"],
|
||||
// comma
|
||||
["x, y;", "comma"],
|
||||
// block
|
||||
["{};", "1 block"],
|
||||
["{x;};", "2 block"],
|
||||
["{x;y;};", "3 block"],
|
||||
// vars
|
||||
["var x;", "1 var"],
|
||||
["var x,y;", "2 var"],
|
||||
["var x=1,y=2;", "3 var"],
|
||||
["var x,y=2;", "4 var"],
|
||||
// empty
|
||||
[";", "1 empty"],
|
||||
["\n;", "2 empty"],
|
||||
// expression statement
|
||||
["x;", "1 expression statement"],
|
||||
["5;", "2 expression statement"],
|
||||
["1+2;", "3 expression statement"],
|
||||
// if
|
||||
["if (c) x; else y;", "1 if statement"],
|
||||
["if (c) x;", "2 if statement"],
|
||||
["if (c) {} else {};", "3 if statement"],
|
||||
["if (c1) if (c2) s1; else s2;", "4 if statement"],
|
||||
// while
|
||||
["do s; while (e);", "1 while statement"],
|
||||
["do { s; } while (e);", "2 while statement"],
|
||||
["while (e) s;", "3 while statement"],
|
||||
["while (e) { s; };", "4 while statement"],
|
||||
// for
|
||||
["for (;;) ;", "1 for statement"],
|
||||
["for (;c;x++) x;", "2 for statement"],
|
||||
["for (i;i<len;++i){};", "3 for statement"],
|
||||
["for (var i=0;i<len;++i) {};", "4 for statement"],
|
||||
["for (var i=0,j=0;;){};", "5 for statement"],
|
||||
//["for (x in b; c; u) {};", "6 for statement"],
|
||||
["for ((x in b); c; u) {};", "7 for statement"],
|
||||
["for (x in a);", "8 for statement"],
|
||||
["for (var x in a){};", "9 for statement"],
|
||||
["for (var x=5 in a) {};", "10 for statement"],
|
||||
["for (var x = a in b in c) {};", "11 for statement"],
|
||||
["for (var x=function(){a+b;}; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
|
||||
["for (var x=function(){for (x=0; x<15; ++x) alert(foo); }; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
|
||||
// flow statements
|
||||
["while(1){ continue; }", "1 flow statement"],
|
||||
["label: while(1){ continue label; }", "2 flow statement"],
|
||||
["while(1){ break; }", "3 flow statement"],
|
||||
["somewhere: while(1){ break somewhere; }", "4 flow statement"],
|
||||
["while(1){ continue /* comment */ ; }", "5 flow statement"],
|
||||
["while(1){ continue \n; }", "6 flow statement"],
|
||||
["(function(){ return; })()", "7 flow statement"],
|
||||
["(function(){ return 0; })()", "8 flow statement"],
|
||||
["(function(){ return 0 + \n 1; })()", "9 flow statement"],
|
||||
// with
|
||||
["with (e) s;", "with statement"],
|
||||
// switch
|
||||
["switch (e) { case x: s; };", "1 switch statement"],
|
||||
["switch (e) { case x: s1;s2; default: s3; case y: s4; };", "2 switch statement"],
|
||||
["switch (e) { default: s1; case x: s2; case y: s3; };", "3 switch statement"],
|
||||
["switch (e) { default: s; };", "4 switch statement"],
|
||||
["switch (e) { case x: s1; case y: s2; };", "5 switch statement"],
|
||||
// labels
|
||||
["foo : x;", " flow statement"],
|
||||
// throw
|
||||
["throw x;", "1 throw statement"],
|
||||
["throw x\n;", "2 throw statement"],
|
||||
// try catch finally
|
||||
["try { s1; } catch (e) { s2; };", "1 trycatchfinally statement"],
|
||||
["try { s1; } finally { s2; };", "2 trycatchfinally statement"],
|
||||
["try { s1; } catch (e) { s2; } finally { s3; };", "3 trycatchfinally statement"],
|
||||
// debugger
|
||||
["debugger;", "debuger statement"],
|
||||
// function decl
|
||||
["function f(x) { e; return x; };", "1 function declaration"],
|
||||
["function f() { x; y; };", "2 function declaration"],
|
||||
["function f(x,y) { var z; return x; };", "3 function declaration"],
|
||||
// function exp
|
||||
["(function f(x) { return x; });", "1 function expression"],
|
||||
["(function empty() {;});", "2 function expression"],
|
||||
["(function empty() {;});", "3 function expression"],
|
||||
["(function (x) {; });", "4 function expression"],
|
||||
// program
|
||||
["var x; function f(){;}; null;", "1 program"],
|
||||
[";;", "2 program"],
|
||||
["{ x; y; z; }", "3 program"],
|
||||
["function f(){ function g(){;}};", "4 program"],
|
||||
["x;\n/*foo*/\n ;", "5 program"],
|
||||
|
||||
// asi
|
||||
["foo: while(1){ continue \n foo; }", "1 asi"],
|
||||
["foo: while(1){ break \n foo; }", "2 asi"],
|
||||
["(function(){ return\nfoo; })()", "3 asi"],
|
||||
["var x; { 1 \n 2 } 3", "4 asi"],
|
||||
["ab /* hi */\ncd", "5 asi"],
|
||||
["ab/*\n*/cd", "6 asi (multi line multilinecomment counts as eol)"],
|
||||
["foo: while(1){ continue /* wtf \n busta */ foo; }", "7 asi illegal with multi line comment"],
|
||||
["function f() { s }", "8 asi"],
|
||||
["function f() { return }", "9 asi"],
|
||||
|
||||
// use strict
|
||||
// XXX: some of these should actually fail?
|
||||
// no support for "use strict" yet...
|
||||
['"use strict"; \'bla\'\n; foo;', "1 directive"],
|
||||
['(function() { "use strict"; \'bla\';\n foo; });', "2 directive"],
|
||||
['"use\\n strict";', "3 directive"],
|
||||
['foo; "use strict";', "4 directive"],
|
||||
|
||||
// tests from http://es5conform.codeplex.com/
|
||||
|
||||
['"use strict"; var o = { eval: 42};', "8.7.2-3-1-s: the use of eval as property name is allowed"],
|
||||
['({foo:0,foo:1});', 'Duplicate property name allowed in not strict mode'],
|
||||
['function foo(a,a){}', 'Duplicate parameter name allowed in not strict mode'],
|
||||
['(function foo(eval){})', 'Eval allowed as parameter name in non strict mode'],
|
||||
['(function foo(arguments){})', 'Arguments allowed as parameter name in non strict mode'],
|
||||
|
||||
// empty programs
|
||||
|
||||
['', '1 Empty program'],
|
||||
['// test', '2 Empty program'],
|
||||
['//test\n', '3 Empty program'],
|
||||
['\n// test', '4 Empty program'],
|
||||
['\n// test\n', '5 Empty program'],
|
||||
['/* */', '6 Empty program'],
|
||||
['/*\ns,fd\n*/', '7 Empty program'],
|
||||
['/*\ns,fd\n*/\n', '8 Empty program'],
|
||||
[' ', '9 Empty program'],
|
||||
[' /*\nsmeh*/ \n ', '10 Empty program'],
|
||||
|
||||
// trailing whitespace
|
||||
|
||||
['a ', '1 Trailing whitespace'],
|
||||
['a /* something */', '2 Trailing whitespace'],
|
||||
['a\n // hah', '3 Trailing whitespace'],
|
||||
['/abc/de//f', '4 Trailing whitespace'],
|
||||
['/abc/de/*f*/\n ', '5 Trailing whitespace'],
|
||||
|
||||
// things the parser tripped over at one point or the other (prevents regression bugs)
|
||||
['for (x;function(){ a\nb };z) x;', 'for header with function body forcing ASI'],
|
||||
['c=function(){return;return};', 'resetting noAsi after literal'],
|
||||
['d\nd()', 'asi exception causing token overflow'],
|
||||
['for(;;){x=function(){}}', 'function expression in a for header'],
|
||||
['for(var k;;){}', 'parser failing due to ASI accepting the incorrect "for" rule'],
|
||||
['({get foo(){ }})', 'getter with empty function body'],
|
||||
['\nreturnr', 'eol causes return statement to ignore local search requirement'],
|
||||
[' / /', '1 whitespace before regex causes regex to fail?'],
|
||||
['/ // / /', '2 whitespace before regex causes regex to fail?'],
|
||||
['/ / / / /', '3 whitespace before regex causes regex to fail?'],
|
||||
|
||||
['\n\t// Used for trimming whitespace\n\ttrimLeft = /^\\s+/;\n\ttrimRight = /\\s+$/;\t\n','turned out this didnt crash (the test below did), but whatever.'],
|
||||
['/[\\/]/;', 'escaped forward slash inside class group (would choke on fwd slash)'],
|
||||
['/[/]/;', 'also broke but is valid in es5 (not es3)'],
|
||||
['({get:5});','get property name thats not a getter'],
|
||||
['({set:5});','set property name thats not a setter'],
|
||||
['l !== "px" && (d.style(h, c, (k || 1) + l), j = (k || 1) / f.cur() * j, d.style(h, c, j + l)), i[1] && (k = (i[1] === "-=" ? -1 : 1) * k + j), f.custom(j, k, l)', 'this choked regex/div at some point'],
|
||||
['(/\'/g, \'\\\\\\\'\') + "\'";', 'the sequence of escaped characters confused the tokenizer']
|
||||
];
|
||||
|
||||
for (var i=0; i<inps.length; ++i) {
|
||||
callback(i, inps[i][0], inps[i][1]);
|
||||
};
|
||||
};
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array1.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array1.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
[],Array(1),[1,2,3]
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array2.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array2.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
(function(){var a=function(){};return new a(1,2,3,4)})()
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array3.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array3.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
(function(){function a(){}return new a(1,2,3,4)})()
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array4.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/array4.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
(function(){function a(){}(function(){return new a(1,2,3)})()})()
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/assignment.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/assignment.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
a=1,b=a,c=1,d=b,e=d,longname=2;if(longname+1){x=3;if(x)var z=7}z=1,y=1,x=1,g+=1,h=g,++i,j=i,i++,j=i+17
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/concatstring.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/concatstring.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=a+"a"+"b"+1+c,b=a+"c"+"ds"+123+c,c=a+"c"+123+d+"ds"+c
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/const.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/const.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=13,b=1/3
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/empty-blocks.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/empty-blocks.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
function mak(){for(;;);}function foo(){while(bar());}function bar(){return--x}var x=5
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/forstatement.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/forstatement.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
a=func(),b=z;for(a++;i<10;i++)alert(i);var z=1;g=2;for(;i<10;i++)alert(i);var a=2;for(var i=1;i<10;i++)alert(i)
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/if.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/if.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=1;a==1?a=2:a=17
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/ifreturn.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/ifreturn.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
function a(a){return a==1?2:17}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue10.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue10.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
function f(){var a;return(a="a")?a:a}f()
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue11.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue11.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
new(A,B),new(A||B),new(X?A:B)
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue13.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue13.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=/^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#])(?::(\d))?)?(..?$|(?:[^?#\/]\/))([^?#]*)(?:\?([^#]))?(?:#(.))?/
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue14.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue14.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue16.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue16.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=3250441966
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue17.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue17.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=function(b){b(),a()}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue20.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue20.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
a:1
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue21.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue21.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=0;switch(a){case 0:a++}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue25.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue25.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
label1:{label2:break label2;console.log(1)}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue27.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue27.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
(a?b:c)?d:e
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue28.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue28.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
o={".5":.5},o={.5:.5},o={.5:.5}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue29.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue29.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
result=function(){return 1}()
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue30.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue30.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=8,b=4,c=4
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue34.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue34.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a={};a["this"]=1,a.that=2
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue4.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue4.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=2e3,b=.002,c=2e-5
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue48.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue48.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var s,i;s="",i=0
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue50.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue50.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
function bar(a){try{foo()}catch(b){alert("Exception caught (foo not defined)")}alert(a)}bar(10)
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue53.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue53.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
x=(y,z)
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue54.1.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue54.1.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
foo+"",a.toString(16),b.toString.call(c)
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue68.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue68.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
function f(){function b(){}a||b()}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue69.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue69.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
[(a,b)]
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue9.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/issue9.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a={a:1,b:2}
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/strict-equals.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/strict-equals.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
typeof a=="string",b+""!=c+"",d<e==f<g
|
||||
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/var.js
vendored
Executable file
1
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/expected/var.js
vendored
Executable file
@@ -0,0 +1 @@
|
||||
var a=1,b=2
|
||||
3
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array1.js
vendored
Executable file
3
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array1.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
new Array();
|
||||
new Array(1);
|
||||
new Array(1, 2, 3);
|
||||
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array2.js
vendored
Executable file
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array2.js
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
var Array = function(){};
|
||||
return new Array(1, 2, 3, 4);
|
||||
})();
|
||||
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array3.js
vendored
Executable file
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array3.js
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
(function(){
|
||||
return new Array(1, 2, 3, 4);
|
||||
function Array() {};
|
||||
})();
|
||||
6
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array4.js
vendored
Executable file
6
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/array4.js
vendored
Executable file
@@ -0,0 +1,6 @@
|
||||
(function(){
|
||||
(function(){
|
||||
return new Array(1, 2, 3);
|
||||
})();
|
||||
function Array(){};
|
||||
})();
|
||||
20
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/assignment.js
vendored
Executable file
20
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/assignment.js
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
a=1;
|
||||
b=a;
|
||||
c=1;
|
||||
d=b;
|
||||
e=d;
|
||||
longname=2;
|
||||
if (longname+1) {
|
||||
x=3;
|
||||
if (x) var z = 7;
|
||||
}
|
||||
z=1,y=1,x=1
|
||||
|
||||
g+=1;
|
||||
h=g;
|
||||
|
||||
++i;
|
||||
j=i;
|
||||
|
||||
i++;
|
||||
j=i+17;
|
||||
3
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/concatstring.js
vendored
Executable file
3
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/concatstring.js
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
var a = a + "a" + "b" + 1 + c;
|
||||
var b = a + "c" + "ds" + 123 + c;
|
||||
var c = a + "c" + 123 + d + "ds" + c;
|
||||
5
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/const.js
vendored
Executable file
5
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/const.js
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
// test that the calculation is fold to 13
|
||||
var a = 1 + 2 * 6;
|
||||
|
||||
// test that it isn't replaced with 0.3333 because that is more characters
|
||||
var b = 1/3;
|
||||
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/empty-blocks.js
vendored
Executable file
4
support/socket.io-client/lib/vendor/uglifyjs/test/unit/compress/test/empty-blocks.js
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
var x = 5;
|
||||
function bar() { return --x; }
|
||||
function foo() { while (bar()); }
|
||||
function mak() { for(;;); }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user