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;
}
