|
@@ -0,0 +1,169 @@
|
|
|
|
+function bullet(url) {
|
|
|
|
+
|
|
|
|
+ var CONNECTING = 0;
|
|
|
|
+ var OPEN = 1;
|
|
|
|
+ var CLOSING = 2;
|
|
|
|
+ var CLOSED = 3;
|
|
|
|
+
|
|
|
|
+ var transports = {
|
|
|
|
+
|
|
|
|
+ websocket: function() {
|
|
|
|
+ var transport = null;
|
|
|
|
+ if (window.WebSocket) { transport = window.WebSocket; }
|
|
|
|
+ if (window.MozWebSocket) { transport = window.MozWebSocket; }
|
|
|
|
+ if (transport) { return {'heart': true, 'transport': transport}; }
|
|
|
|
+ return null;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ xhrPolling: function() {
|
|
|
|
+ var timeout;
|
|
|
|
+ var xhr;
|
|
|
|
+
|
|
|
|
+ nextPoll();
|
|
|
|
+
|
|
|
|
+ var fake = {
|
|
|
|
+ readyState: CONNECTING,
|
|
|
|
+
|
|
|
|
+ receive: function(data) {
|
|
|
|
+ if (fake.readyState == CONNECTING) { fake.readyState = OPEN; fake.onopen(fake); }
|
|
|
|
+ if (data.length != 0) { fake.onmessage({'data': data }); }
|
|
|
|
+ if (fake.readyState == OPEN) { nextPoll(); }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ send: function(data) {
|
|
|
|
+ if (this.readyState != CONNECTING && this.readyState != OPEN) return false;
|
|
|
|
+ var fakeurl = url.replace('ws:', 'http:').replace('wss:', 'https:');
|
|
|
|
+ var request = new XMLHttpRequest();
|
|
|
|
+ request.open('POST',fakeurl,true);
|
|
|
|
+ request.setRequestHeader('Content-Type',
|
|
|
|
+ 'application/x-www-form-urlencoded; charset=utf-8');
|
|
|
|
+ request.setRequestHeader('X-Socket-Transport','xhrPolling');
|
|
|
|
+ request.onload = function() { fake.receive(request.response); }
|
|
|
|
+ request.send(data);
|
|
|
|
+ return true;
|
|
|
|
+ },
|
|
|
|
+ close: function(){
|
|
|
|
+ this.readyState = CLOSED;
|
|
|
|
+ xhr.abort();
|
|
|
|
+ clearTimeout(timeout);
|
|
|
|
+ fake.onclose();
|
|
|
|
+ },
|
|
|
|
+ onopen: function(){},
|
|
|
|
+ onmessage: function(){},
|
|
|
|
+ onerror: function(){},
|
|
|
|
+ onclose: function(){}
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ function poll(pooling){
|
|
|
|
+ var fakeurl = url.replace('ws:', 'http:').replace('wss:', 'https:');
|
|
|
|
+ var request = new XMLHttpRequest();
|
|
|
|
+ request.open('GET',fakeurl,true);
|
|
|
|
+ request.setRequestHeader('Content-Type',
|
|
|
|
+ 'application/x-www-form-urlencoded; charset=utf-8');
|
|
|
|
+ request.setRequestHeader('X-Socket-Transport','xhrPolling');
|
|
|
|
+ request.onload = function() { fake.receive(request.response); }
|
|
|
|
+ request.onerror = function() { fake.onerror(); }
|
|
|
|
+ request.send({});
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function nextPoll() { timeout = setTimeout(function(){poll();}, 1000); }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return {'heart': false, 'transport': function(){ return fake; fake.nextPoll(); }};
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var tn = 0;
|
|
|
|
+ function next() {
|
|
|
|
+ var c = 0;
|
|
|
|
+
|
|
|
|
+ for (var f in transports) {
|
|
|
|
+ if (tn == c) {
|
|
|
|
+ var t = transports[f]();
|
|
|
|
+ if (t) { var ret = new t.transport(url); ret.heart = t.heart; return ret; }
|
|
|
|
+ tn++;
|
|
|
|
+ }
|
|
|
|
+ c++;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var stream = new function() {
|
|
|
|
+ var isClosed = true;
|
|
|
|
+ var readyState = CLOSED;
|
|
|
|
+ var heartbeat;
|
|
|
|
+ var delay = 80;
|
|
|
|
+ var delayDefault = 80;
|
|
|
|
+ var delayMax = 10000;
|
|
|
|
+
|
|
|
|
+ var transport;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ this.transport = function () { return transport; }
|
|
|
|
+
|
|
|
|
+ function init() {
|
|
|
|
+
|
|
|
|
+ isClosed = false;
|
|
|
|
+ readyState = CONNECTING;
|
|
|
|
+ transport = next();
|
|
|
|
+
|
|
|
|
+ if (!transport) {
|
|
|
|
+ delay = delayDefault;
|
|
|
|
+ tn = 0;
|
|
|
|
+ stream.ondisconnect();
|
|
|
|
+ setTimeout(function(){init();}, delayMax);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ transport.onopen = function() {
|
|
|
|
+ delay = delayDefault;
|
|
|
|
+
|
|
|
|
+ if (transport.heart) {
|
|
|
|
+ heartbeat = setInterval(function(){stream.onheartbeat();}, 4000);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (readyState != OPEN) { readyState = OPEN; stream.onopen(); }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ transport.onclose = function() {
|
|
|
|
+ if (isClosed) { return; }
|
|
|
|
+
|
|
|
|
+ transport = null;
|
|
|
|
+ clearInterval(heartbeat);
|
|
|
|
+
|
|
|
|
+ if (readyState == CLOSING){
|
|
|
|
+ readyState = CLOSED;
|
|
|
|
+ stream.onclose();
|
|
|
|
+ } else {
|
|
|
|
+ if (readyState == CONNECTING) tn++;
|
|
|
|
+ delay *= 2;
|
|
|
|
+ if (delay > delayMax) { delay = delayMax; }
|
|
|
|
+ isClosed = true;
|
|
|
|
+ setTimeout(function() { init(); }, delay);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ transport.onerror = transport.onclose;
|
|
|
|
+ transport.onmessage = function(e){
|
|
|
|
+ stream.onmessage(e);
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ init();
|
|
|
|
+
|
|
|
|
+ this.onopen = function(){}; this.oninit = function(){};
|
|
|
|
+ this.onmessage = function(){}; this.ondisconnect = function(){ initialized = false; };
|
|
|
|
+ this.onclose = function(){}; this.onheartbeat = function(){ return this.send('PING'); };
|
|
|
|
+
|
|
|
|
+ this.setURL = function(newURL) { url = newURL; };
|
|
|
|
+ this.send = function(data){
|
|
|
|
+ if (transport) return transport.send(data); else return false;
|
|
|
|
+ };
|
|
|
|
+ this.close = function(){
|
|
|
|
+ readyState = CLOSING;
|
|
|
|
+ if (transport) transport.close();
|
|
|
|
+ };
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ return stream;
|
|
|
|
+}
|