aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc3
-rw-r--r--src/jsifier.js6
-rw-r--r--src/library_sdl.js198
-rw-r--r--src/runtime.js12
-rw-r--r--tests/cases/switch64c_ta2.ll68
-rw-r--r--tests/cases/switch64c_ta2.txt3
-rw-r--r--tests/core/test_inlinejs3.in5
-rw-r--r--tests/core/test_inlinejs3.out2
-rwxr-xr-xtests/runner.py4
-rw-r--r--tests/sockets/p2p/.gitignore2
-rw-r--r--tests/sockets/p2p/broker/p2p-broker.js231
-rw-r--r--tests/sockets/p2p/client/p2p-client.js4485
-rw-r--r--tests/sockets/p2p/package.json17
-rw-r--r--tests/sockets/webrtc_host.c11
-rw-r--r--tests/sockets/webrtc_peer.c4
-rw-r--r--tests/test_other.py4
-rw-r--r--tests/test_sockets.py14
17 files changed, 4973 insertions, 96 deletions
diff --git a/emcc b/emcc
index 4ba63f21..738179ad 100755
--- a/emcc
+++ b/emcc
@@ -1912,3 +1912,6 @@ finally:
else:
logging.info('emcc saved files are in:' + temp_dir)
+#//eliminate a = a in js opt. will kill STACKTOP = STACKTOP in funcs that do not use the C stack! add test for no STACKTOP or sp in such a func
+#// minify if into ?: ?
+
diff --git a/src/jsifier.js b/src/jsifier.js
index 503f0b71..065c66a8 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -397,8 +397,10 @@ function JSify(data, functionsOnly) {
if (!LibraryManager.library.hasOwnProperty(ident) && !LibraryManager.library.hasOwnProperty(ident + '__inline')) {
if (notDep) {
- if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident);
- else if (VERBOSE || (WARN_ON_UNDEFINED_SYMBOLS && !LINKABLE)) warn('unresolved symbol: ' + ident);
+ if (VERBOSE || ident.substr(0, 11) !== 'emscripten_') { // avoid warning on emscripten_* functions which are for internal usage anyhow
+ if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + ident);
+ else if (VERBOSE || (WARN_ON_UNDEFINED_SYMBOLS && !LINKABLE)) warn('unresolved symbol: ' + ident);
+ }
}
// emit a stub that will fail at runtime
LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 2606bafc..3d96a693 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -83,27 +83,27 @@ var LibrarySDL = {
DOMEventToSDLEvent: {},
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
+ // For keys that don't have unicode value, we map DOM codes with the corresponding scan codes + 1024 (using "| 1 << 10")
+ 16: 225 | 1<<10, // shift
+ 17: 224 | 1<<10, // control (right, or left)
+ 18: 226 | 1<<10, // alt
+ 20: 57 | 1<<10, // caps lock
+
+ 33: 75 | 1<<10, // pagedup
+ 34: 78 | 1<<10, // pagedown
+ 35: 77 | 1<<10, // end
+ 36: 74 | 1<<10, // home
+ 37: 80 | 1<<10, // left arrow
+ 38: 82 | 1<<10, // up arrow
+ 39: 79 | 1<<10, // right arrow
+ 40: 81 | 1<<10, // down arrow
+ 44: 316, // print screen
+ 45: 73 | 1<<10, // insert
46: 127, // SDLK_DEL == '\177'
- 38: 1106, // up arrow
- 40: 1105, // down arrow
- 37: 1104, // left arrow
- 39: 1103, // right arrow
-
- 33: 1099, // pagedup
- 34: 1102, // pagedown
-
- 35: 1101, // end
- 36: 1098, // home
- 45: 1097, // insert
-
- 17: 1248, // control (right, or left)
- 18: 1250, // alt
- 173: 45, // minus
- 16: 1249, // shift
+ 91: 227 | 1<<10, // windows key or super key on linux (doesn't work on Mac)
+ 93: 101 | 1<<10, // application
- 20: 301, // caps lock
-
96: 98 | 1<<10, // keypad 0
97: 89 | 1<<10, // keypad 1
98: 90 | 1<<10, // keypad 2
@@ -114,16 +114,11 @@ var LibrarySDL = {
103: 95 | 1<<10, // keypad 7
104: 96 | 1<<10, // keypad 8
105: 97 | 1<<10, // keypad 9
-
106: 85 | 1<<10, // keypad multiply
107: 87 | 1<<10, // keypad plus
- 109: 86 | 1<<10, // keypad minus
- 111: 84 | 1<<10, // keypad divide
-
+ 109: 86 | 1<<10, // keypad minus
110: 99 | 1<<10, // keypad decimal point
-
- 144: 83 | 1<<10, // keypad num lock
-
+ 111: 84 | 1<<10, // keypad divide
112: 58 | 1<<10, // F1
113: 59 | 1<<10, // F2
114: 60 | 1<<10, // F3
@@ -135,70 +130,121 @@ var LibrarySDL = {
120: 66 | 1<<10, // F9
121: 67 | 1<<10, // F10
122: 68 | 1<<10, // F11
- 123: 69 | 1<<10, // F12
+ 123: 69 | 1<<10, // F12
+ 124: 104 | 1<<10, // F13
+ 125: 105 | 1<<10, // F14
+ 126: 106 | 1<<10, // F15
+ 127: 107 | 1<<10, // F16
+ 128: 108 | 1<<10, // F17
+ 129: 109 | 1<<10, // F18
+ 130: 110 | 1<<10, // F19
+ 131: 111 | 1<<10, // F20
+ 132: 112 | 1<<10, // F21
+ 133: 113 | 1<<10, // F22
+ 134: 114 | 1<<10, // F23
+ 135: 115 | 1<<10, // F24
+ 144: 83 | 1<<10, // keypad num lock
+
+ 160: 94, // caret
+ 161: 33, // exclaim
+ 162: 34, // double quote
+ 163: 35, // hash
+ 164: 36, // dollar
+ 165: 37, // percent
+ 166: 38, // ampersand
+ 167: 95, // underscore
+ 168: 40, // open parenthesis
+ 169: 41, // close parenthesis
+ 170: 42, // asterix
+ 171: 43, // plus
+ 172: 124, // pipe
+ 173: 45, // minus
+ 174: 123, // open curly bracket
+ 175: 125, // close curly bracket
+ 176: 126, // tilde
+
+ 181: 127, // audio mute
+ 182: 129, // audio volume down
+ 183: 128, // audio volume up
+
188: 44, // comma
190: 46, // period
191: 47, // slash (/)
192: 96, // backtick/backquote (`)
+ 219: 91, // open square bracket
+ 220: 92, // back slash
+ 221: 93, // close square bracket
+ 222: 39, // quote
},
scanCodes: { // SDL keycode ==> SDL scancode. See SDL_scancode.h
+ 8: 42, // backspace
+ 9: 43, // tab
+ 13: 40, // return
+ 27: 41, // escape
+ 32: 44, // space
+ 35: 204, // hash
+
+ 39: 53, // grave
+
+ 44: 54, // comma
+ 46: 55, // period
+ 47: 56, // slash
+ 48: 39, // 0
+ 49: 30, // 1
+ 50: 31, // 2
+ 51: 32, // 3
+ 52: 33, // 4
+ 53: 34, // 5
+ 54: 35, // 6
+ 55: 36, // 7
+ 56: 37, // 8
+ 57: 38, // 9
+ 58: 203, // colon
+ 59: 51, // semicolon
+
+ 61: 46, // equals
+
+ 91: 47, // left bracket
+ 92: 49, // backslash
+ 93: 48, // right bracket
+
+ 96: 52, // apostrophe
97: 4, // A
- 98: 5,
- 99: 6,
- 100: 7,
- 101: 8,
- 102: 9,
- 103: 10,
- 104: 11,
- 105: 12,
- 106: 13,
- 107: 14,
- 108: 15,
- 109: 16,
- 110: 17,
- 111: 18,
- 112: 19,
- 113: 20,
- 114: 21,
- 115: 22,
- 116: 23,
- 117: 24,
- 118: 25,
- 119: 26,
- 120: 27,
- 121: 28,
+ 98: 5, // B
+ 99: 6, // C
+ 100: 7, // D
+ 101: 8, // E
+ 102: 9, // F
+ 103: 10, // G
+ 104: 11, // H
+ 105: 12, // I
+ 106: 13, // J
+ 107: 14, // K
+ 108: 15, // L
+ 109: 16, // M
+ 110: 17, // N
+ 111: 18, // O
+ 112: 19, // P
+ 113: 20, // Q
+ 114: 21, // R
+ 115: 22, // S
+ 116: 23, // T
+ 117: 24, // U
+ 118: 25, // V
+ 119: 26, // W
+ 120: 27, // X
+ 121: 28, // Y
122: 29, // Z
- 49: 30, // 1
- 50: 31,
- 51: 32,
- 52: 33,
- 53: 34,
- 54: 35,
- 55: 36,
- 56: 37,
- 57: 38, // 9
- 48: 39, // 0
- 13: 40, // return
- 27: 41, // escape
- 8: 42, // backspace
- 9: 43, // tab
- 301: 57, // caps lock
- 32: 44, // space
- 61: 46, // equals
- 91: 47, // left bracket
- 93: 48, // right bracket
- 92: 49, // backslash
- 96: 43, // grave
- 59: 51, // ;
- 96: 52, // apostrophe
- 44: 54, // comma
- 46: 55, // period
- 47: 56, // slash
+
127: 76, // delete
+
305: 224, // ctrl
+
308: 226, // alt
+
+ 316: 70, // print screen
},
loadRect: function(rect) {
return {
diff --git a/src/runtime.js b/src/runtime.js
index 2ae68279..edcbf637 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -399,17 +399,17 @@ var Runtime = {
for (var i = 0; i < numArgs; i++) {
args.push(String.fromCharCode(36) + i); // $0, $1 etc
}
- code = Pointer_stringify(code);
- if (code[0] === '"') {
+ var source = Pointer_stringify(code);
+ if (source[0] === '"') {
// tolerate EM_ASM("..code..") even though EM_ASM(..code..) is correct
- if (code.indexOf('"', 1) === code.length-1) {
- code = code.substr(1, code.length-2);
+ if (source.indexOf('"', 1) === source.length-1) {
+ source = source.substr(1, source.length-2);
} else {
// something invalid happened, e.g. EM_ASM("..code($0)..", input)
- abort('invalid EM_ASM input |' + code + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)');
+ abort('invalid EM_ASM input |' + source + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)');
}
}
- return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + code + ' })'); // new Function does not allow upvars in node
+ return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + source + ' })'); // new Function does not allow upvars in node
},
warnOnce: function(text) {
diff --git a/tests/cases/switch64c_ta2.ll b/tests/cases/switch64c_ta2.ll
new file mode 100644
index 00000000..6826a412
--- /dev/null
+++ b/tests/cases/switch64c_ta2.ll
@@ -0,0 +1,68 @@
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128"
+target triple = "asmjs-unknown-emscripten"
+
+@.str = private constant [18 x i8] c"hello, world: %d\0A\00", align 1
+
+declare i32 @printf(i8*, ...)
+
+define i32 @main() {
+ %a333 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str, i32 0, i32 0), i32 5)
+ %a400 = zext i32 %a333 to i64
+ %check = trunc i32 %a333 to i1
+ br i1 %check, label %l1, label %l2
+
+l1:
+ %bbb = phi i64 [ %a400, %0 ], [ 10, %l2 ]
+ %bbb32 = trunc i64 %bbb to i32
+ %a333z = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str, i32 0, i32 0), i32 %bbb32)
+ %check2 = trunc i32 %bbb32 to i1
+ br i1 %check2, label %l2, label %label999
+
+l2:
+ %a410 = phi i64 [ %a400, %0 ], [ %bbb, %l1 ]
+ %a444 = udiv i64 %a410, 3
+ switch i64 %a444, label %l1 [
+ i64 1000, label %label9950
+ i64 1001, label %label9951
+ i64 1002, label %label9952
+ i64 1003, label %label9953
+ i64 1004, label %label9954
+ i64 1005, label %label9955
+ i64 1006, label %label9956
+ i64 1007, label %label9957
+ i64 1008, label %label9958
+ i64 1009, label %label9959
+ ]
+
+label9950:
+ %waka = phi i64 [1000, %l2], [0, %label9951], [1, %label9952], [2, %label9953], [3, %label9954], [4, %label9955], [5, %label9956], [6, %label9957], [7, %label9958], [8, %label9959]
+ %waka32 = trunc i64 %waka to i32
+ %a333b = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str, i32 0, i32 0), i32 %waka32)
+ br label %label999
+
+label9951:
+ br label %label9950
+label9952:
+ br label %label9950
+label9953:
+ br label %label9950
+label9954:
+ br label %label9950
+label9955:
+ br label %label9950
+label9956:
+ br label %label9950
+label9957:
+ br label %label9950
+label9958:
+ br label %label9950
+label9959:
+ br label %label9950
+
+label999: ; preds = %555
+ %last = phi i64 [1, %l1], [2, %label9950]
+ %last32 = trunc i64 %last to i32
+ %a333c = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8]* @.str, i32 0, i32 0), i32 %last32)
+ ret i32 0
+}
+
diff --git a/tests/cases/switch64c_ta2.txt b/tests/cases/switch64c_ta2.txt
new file mode 100644
index 00000000..29999663
--- /dev/null
+++ b/tests/cases/switch64c_ta2.txt
@@ -0,0 +1,3 @@
+hello, world: 5
+hello, world: 10
+hello, world: 1
diff --git a/tests/core/test_inlinejs3.in b/tests/core/test_inlinejs3.in
index da720a3d..b45abe95 100644
--- a/tests/core/test_inlinejs3.in
+++ b/tests/core/test_inlinejs3.in
@@ -1,6 +1,10 @@
#include <stdio.h>
#include <emscripten.h>
+void loop_iter() {
+ EM_ASM(Module.print('loop iter!'));
+}
+
int main(int argc, char **argv) {
EM_ASM(Module.print('hello dere1'));
EM_ASM("Module.print('hello dere2');");
@@ -21,5 +25,6 @@ int main(int argc, char **argv) {
sum = 0;
sum = EM_ASM_INT_V({ return globalVar }); // no inputs, just output
printf("sum: %d\n", sum);
+ for (int i = 0; i < argc*2; i++) loop_iter();
return 0;
}
diff --git a/tests/core/test_inlinejs3.out b/tests/core/test_inlinejs3.out
index 1f64a89a..5d185adc 100644
--- a/tests/core/test_inlinejs3.out
+++ b/tests/core/test_inlinejs3.out
@@ -11,3 +11,5 @@ i: 0,0.00
i: 1,0.08
i: 2,0.17
sum: 6
+loop iter!
+loop iter!
diff --git a/tests/runner.py b/tests/runner.py
index 26912f25..1f72c0b0 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -790,9 +790,9 @@ To run one of those parts, do something like
To run a specific set of tests, you can do things like
- python tests/runner.py o1
+ python tests/runner.py asm2
-(that runs the o1 (-O1) tests). You can run individual tests with
+(that runs the asm2 (asm.js, -O2) tests). You can run individual tests with
python tests/runner.py test_hello_world
diff --git a/tests/sockets/p2p/.gitignore b/tests/sockets/p2p/.gitignore
new file mode 100644
index 00000000..065a4ac0
--- /dev/null
+++ b/tests/sockets/p2p/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+ssl
diff --git a/tests/sockets/p2p/broker/p2p-broker.js b/tests/sockets/p2p/broker/p2p-broker.js
new file mode 100644
index 00000000..028eb25b
--- /dev/null
+++ b/tests/sockets/p2p/broker/p2p-broker.js
@@ -0,0 +1,231 @@
+var crypto = require('crypto');
+var fs = require('fs');
+var https = require('https');
+
+var SSL_KEY = 'ssl/ssl.key';
+var SSL_CERT = 'ssl/ssl-unified.crt';
+var PORT = 8080;
+
+var sslSupported = false;
+if(fs.existsSync(SSL_KEY) && fs.existsSync(SSL_CERT) && fs.statSync(SSL_KEY).isFile() && fs.statSync(SSL_CERT).isFile()) {
+ sslSupported = true;
+}
+
+function handler(req, res) {
+ res.writeHead(200);
+ res.end("p2p");
+};
+
+var app, port;
+if(sslSupported) {
+ var sslopts = {
+ key: fs.readFileSync(SSL_KEY),
+ cert: fs.readFileSync(SSL_CERT)
+ };
+ sslopts.agent = new https.Agent(sslopts);
+ app = require('https').createServer(sslopts, handler);
+ port = 8081;
+ console.info('ssl mode enabled');
+} else {
+ app = require('http').createServer(handler);
+ port = 8080;
+ console.info('ssl mode disabled');
+}
+console.info('listening on port', port);
+
+var io = require('socket.io').listen(app, {
+ 'log level': 2
+});
+
+app.listen(port);
+
+var jsMime = {
+ type: 'application/javascript',
+ encoding: 'utf8',
+ gzip: true
+};
+
+io.static.add('/p2p-client.js', {
+ mime: jsMime,
+ file: 'client/p2p-client.js'
+});
+
+/*
+io.static.add('/p2p-client.min.js', {
+ mime: jsMime,
+ file: 'client/p2p-client.min.js'
+});
+*/
+
+function mkguid() {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+ var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+ return v.toString(16);
+ }).toUpperCase();
+};
+
+var peers = {};
+var hosts = {};
+
+var E = {
+ OK: 'ok'
+ , NOROUTE: 'no such route'
+ , ISNOTHOST: 'peer is not a host'
+};
+
+function Peer(socket) {
+ this.socket = socket;
+ this.host = null;
+};
+
+function Host(options) {
+ this.route = options['route'];
+ this.url = options['url'];
+ this.listed = (undefined !== options['listed']) ? options['listed'] : false;
+ this.metadata = options['metadata'] || {};
+ this.ctime = Date.now();
+ this.mtime = Date.now();
+};
+Host.prototype.update = function update(options) {
+ this.url = options['url'];
+ this.listed = (undefined !== options['listed']) ? options['listed'] : false;
+ this.metadata = options['metadata'] || {};
+ this.mtime = Date.now();
+};
+
+io.of('/peer').on('connection', function(socket) {
+ var route = crypto.createHash('md5').update(socket['id']).digest('hex');
+ socket.emit('route', route);
+
+ socket.on('disconnect', function() {
+ if(hosts[route]) {
+ var host = hosts[route];
+ changeList('remove', host);
+ }
+ delete hosts[route];
+ delete peers[route];
+ });
+
+ socket.on('send', function(message, callback) {
+ var to = message['to'];
+
+ if(!peers.hasOwnProperty(to)) {
+ callback({'error': E.NOROUTE});
+ return;
+ }
+
+ var from = route;
+ var data = message['data'];
+ peers[to].emit('receive', {
+ 'from': from,
+ 'data': data
+ });
+ });
+
+ socket.on('listen', function(options, callback) {
+ options['route'] = route;
+ if(hosts.hasOwnProperty(route)) {
+ hosts[route].update(options);
+ changeList('update', hosts[route]);
+ } else {
+ hosts[route] = new Host(options);
+ changeList('append', hosts[route]);
+ }
+
+ callback();
+ });
+
+ socket.on('ignore', function(message, callback) {
+ if(!hosts.hasOwnProperty(route)) {
+ callback({'error': E.ISNOTHOST});
+ return;
+ }
+
+ var host = hosts[route];
+ delete hosts[route];
+
+ changeList('remove', host);
+
+ callback();
+ });
+
+ peers[route] = socket;
+});
+
+function Filter(socket, options) {
+ this.options = options || {};
+ this.socket = socket;
+};
+Filter.prototype.test = function test(host) {
+ var filter = this.options;
+ var result;
+
+ if(filter['url'] && typeof host['url'] === 'string') {
+ try {
+ result = host['url'].match(filter['url']);
+ if(!result)
+ return true;
+ } catch(e) {
+ return true;
+ }
+ }
+
+ if(filter['metadata'] && host['metadata']) {
+ var metadataFilter = filter['metadata'];
+ var metadataHost = host['metadata'];
+
+ if(metadataFilter['name'] && typeof metadataHost['name'] === 'string') {
+ try {
+ result = metadataHost['name'].match(metadataFilter['name']);
+ if(!result)
+ return true;
+ } catch(e) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+};
+
+var lists = {};
+
+function changeList(operation, host) {
+ var clients = Object.keys(lists);
+ clients.forEach(function(client) {
+ var filter = lists[client];
+ if(!host['listed'])
+ return;
+ if(!filter.test(host)) {
+ var data = operation === 'remove' ? host['route'] : host;
+ filter.socket.emit(operation, data);
+ }
+ });
+};
+
+io.of('/list').on('connection', function(socket) {
+ var id = socket['id'];
+
+ socket.on('disconnect', function() {
+ delete lists[id];
+ });
+
+ socket.on('list', function(options) {
+ var filter = new Filter(socket, options);
+
+ var result = [];
+
+ var hostIds = Object.keys(hosts);
+ hostIds.forEach(function(hostId) {
+ var host = hosts[hostId];
+ if(!host['listed'])
+ return;
+ if(!filter.test(host))
+ result.push(host);
+ });
+
+ lists[id] = filter;
+
+ socket.emit('truncate', result);
+ });
+});
diff --git a/tests/sockets/p2p/client/p2p-client.js b/tests/sockets/p2p/client/p2p-client.js
new file mode 100644
index 00000000..2c660210
--- /dev/null
+++ b/tests/sockets/p2p/client/p2p-client.js
@@ -0,0 +1,4485 @@
+
+;(function(define, global) { 'use strict';
+define(['module'], function(module) {
+
+ /*! Socket.IO.js build:0.9.11, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed
+ Modified to work in-line; Removed Flash transport code */
+ var io = ('undefined' === typeof module ? {} : module.exports);
+ (function() {
+
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, global) {
+
+ /**
+ * IO namespace.
+ *
+ * @namespace
+ */
+
+ var io = exports;
+
+ /**
+ * Socket.IO version
+ *
+ * @api public
+ */
+
+ io.version = '0.9.11';
+
+ /**
+ * Protocol implemented.
+ *
+ * @api public
+ */
+
+ io.protocol = 1;
+
+ /**
+ * Available transports, these will be populated with the available transports
+ *
+ * @api public
+ */
+
+ io.transports = [];
+
+ /**
+ * Keep track of jsonp callbacks.
+ *
+ * @api private
+ */
+
+ io.j = [];
+
+ /**
+ * Keep track of our io.Sockets
+ *
+ * @api private
+ */
+ io.sockets = {};
+
+
+ /**
+ * Manages connections to hosts.
+ *
+ * @param {String} uri
+ * @Param {Boolean} force creation of new socket (defaults to false)
+ * @api public
+ */
+
+ io.connect = function (host, details) {
+ var uri = io.util.parseUri(host)
+ , uuri
+ , socket;
+
+ if (global && global.location) {
+ uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
+ uri.host = uri.host || (global.document
+ ? global.document.domain : global.location.hostname);
+ uri.port = uri.port || global.location.port;
+ }
+
+ uuri = io.util.uniqueUri(uri);
+
+ var options = {
+ host: uri.host
+ , secure: 'https' == uri.protocol
+ , port: uri.port || ('https' == uri.protocol ? 443 : 80)
+ , query: uri.query || ''
+ };
+
+ io.util.merge(options, details);
+
+ if (options['force new connection'] || !io.sockets[uuri]) {
+ socket = new io.Socket(options);
+ }
+
+ if (!options['force new connection'] && socket) {
+ io.sockets[uuri] = socket;
+ }
+
+ socket = socket || io.sockets[uuri];
+
+ // if path is different from '' or /
+ return socket.of(uri.path.length > 1 ? uri.path : '');
+ };
+
+ })('object' === typeof module ? module.exports : (io = {}), global);
+ /**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
+
+ (function (exports, global) {
+
+ /**
+ * Utilities namespace.
+ *
+ * @namespace
+ */
+
+ var util = exports.util = {};
+
+ /**
+ * Parses an URI
+ *
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
+ * @api public
+ */
+
+ var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
+
+ var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password',
+ 'host', 'port', 'relative', 'path', 'directory', 'file', 'query',
+ 'anchor'];
+
+ util.parseUri = function (str) {
+ var m = re.exec(str || '')
+ , uri = {}
+ , i = 14;
+
+ while (i--) {
+ uri[parts[i]] = m[i] || '';
+ }
+
+ return uri;
+ };
+
+ /**
+ * Produces a unique url that identifies a Socket.IO connection.
+ *
+ * @param {Object} uri
+ * @api public
+ */
+
+ util.uniqueUri = function (uri) {
+ var protocol = uri.protocol
+ , host = uri.host
+ , port = uri.port;
+
+ if ('document' in global) {
+ host = host || document.domain;
+ port = port || (protocol == 'https'
+ && document.location.protocol !== 'https:' ? 443 : document.location.port);
+ } else {
+ host = host || 'localhost';
+
+ if (!port && protocol == 'https') {
+ port = 443;
+ }
+ }
+
+ return (protocol || 'http') + '://' + host + ':' + (port || 80);
+ };
+
+ /**
+ * Mergest 2 query strings in to once unique query string
+ *
+ * @param {String} base
+ * @param {String} addition
+ * @api public
+ */
+
+ util.query = function (base, addition) {
+ var query = util.chunkQuery(base || '')
+ , components = [];
+
+ util.merge(query, util.chunkQuery(addition || ''));
+ for (var part in query) {
+ if (query.hasOwnProperty(part)) {
+ components.push(part + '=' + query[part]);
+ }
+ }
+
+ return components.length ? '?' + components.join('&') : '';
+ };
+
+ /**
+ * Transforms a querystring in to an object
+ *
+ * @param {String} qs
+ * @api public
+ */
+
+ util.chunkQuery = function (qs) {
+ var query = {}
+ , params = qs.split('&')
+ , i = 0
+ , l = params.length
+ , kv;
+
+ for (; i < l; ++i) {
+ kv = params[i].split('=');
+ if (kv[0]) {
+ query[kv[0]] = kv[1];
+ }
+ }
+
+ return query;
+ };
+
+ /**
+ * Executes the given function when the page is loaded.
+ *
+ * io.util.load(function () { console.log('page loaded'); });
+ *
+ * @param {Function} fn
+ * @api public
+ */
+
+ var pageLoaded = false;
+
+ util.load = function (fn) {
+ if ('document' in global && document.readyState === 'complete' || pageLoaded) {
+ return fn();
+ }
+
+ util.on(global, 'load', fn, false);
+ };
+
+ /**
+ * Adds an event.
+ *
+ * @api private
+ */
+
+ util.on = function (element, event, fn, capture) {
+ if (element.attachEvent) {
+ element.attachEvent('on' + event, fn);
+ } else if (element.addEventListener) {
+ element.addEventListener(event, fn, capture);
+ }
+ };
+
+ /**
+ * 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.
+ * @api private
+ */
+
+ util.request = function (xdomain) {
+
+ if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) {
+ return new XDomainRequest();
+ }
+
+ if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) {
+ return new XMLHttpRequest();
+ }
+
+ if (!xdomain) {
+ try {
+ return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP');
+ } catch(e) { }
+ }
+
+ return null;
+ };
+
+ /**
+ * XHR based transport constructor.
+ *
+ * @constructor
+ * @api public
+ */
+
+ /**
+ * Change the internal pageLoaded value.
+ */
+
+ if ('undefined' != typeof window) {
+ util.load(function () {
+ pageLoaded = true;
+ });
+ }
+
+ /**
+ * Defers a function to ensure a spinner is not displayed by the browser
+ *
+ * @param {Function} fn
+ * @api public
+ */
+
+ util.defer = function (fn) {
+ if (!util.ua.webkit || 'undefined' != typeof importScripts) {
+ return fn();
+ }
+
+ util.load(function () {
+ setTimeout(fn, 100);
+ });
+ };
+
+ /**
+ * Merges two objects.
+ *
+ * @api public
+ */
+
+ util.merge = function merge (target, additional, deep, lastseen) {
+ var seen = lastseen || []
+ , depth = typeof deep == 'undefined' ? 2 : deep
+ , prop;
+
+ for (prop in additional) {
+ if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) {
+ if (typeof target[prop] !== 'object' || !depth) {
+ target[prop] = additional[prop];
+ seen.push(additional[prop]);
+ } else {
+ util.merge(target[prop], additional[prop], depth - 1, seen);
+ }
+ }
+ }
+
+ return target;
+ };
+
+ /**
+ * Merges prototypes from objects
+ *
+ * @api public
+ */
+
+ util.mixin = function (ctor, ctor2) {
+ util.merge(ctor.prototype, ctor2.prototype);
+ };
+
+ /**
+ * Shortcut for prototypical and static inheritance.
+ *
+ * @api private
+ */
+
+ util.inherit = function (ctor, ctor2) {
+ function f() {};
+ f.prototype = ctor2.prototype;
+ ctor.prototype = new f;
+ };
+
+ /**
+ * Checks if the given objec