source: Dev/branches/rest-dojo-ui/client/dojox/socket.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 7.6 KB
RevLine 
[256]1define("dojox/socket", ["dojo", "dojo/Evented", "dojo/cookie", "dojo/_base/url"], function(dojo, Evented) {
2
3var WebSocket = window.WebSocket;
4
5function Socket(/*dojo.__XhrArgs*/ argsOrUrl){
6        // summary:
7        //              Provides a simple socket connection using WebSocket, or alternate
8        //              communication mechanisms in legacy browsers for comet-style communication. This is based
9        //              on the WebSocket API and returns an object that implements the WebSocket interface:
10        //              http://dev.w3.org/html5/websockets/#websocket
11        //      description:
12        //              Provides socket connections. This can be used with virtually any Comet protocol.
13        //      argsOrUrl:
14        //              This uses the same arguments as the other I/O functions in Dojo, or a
15        //              URL to connect to. The URL should be a relative URL in order to properly
16        //              work with WebSockets (it can still be host relative, like //other-site.org/endpoint)
17        // returns:
18        //              An object that implements the WebSocket API
19        // example:
20        //              | dojo.require("dojox.socket");
21        //              | var socket = dojox.socket({"//comet-server/comet");
22        //              | // we could also add auto-reconnect support
23        //              | // now we can connect to standard HTML5 WebSocket-style events
24        //              | dojo.connect(socket, "onmessage", function(event){
25        //              |    var message = event.data;
26        //              |    // do something with the message
27        //              | });
28        //              | // send something
29        //              | socket.send("hi there");
30        //              | whenDone(function(){
31        //              |   socket.close();
32        //              | });
33        //              You can also use the Reconnect module:
34        //              | dojo.require("dojox.socket");
35        //              | dojo.require("dojox.socket.Reconnect");
36        //              | var socket = dojox.socket({url:"/comet"});
37        //              | // add auto-reconnect support
38        //              | socket = dojox.socket.Reconnect(socket);
39        if(typeof argsOrUrl == "string"){
40                argsOrUrl = {url: argsOrUrl};
41        }
42        return WebSocket ? dojox.socket.WebSocket(argsOrUrl, true) : dojox.socket.LongPoll(argsOrUrl);
43};
44dojox.socket = Socket;
45
46Socket.WebSocket = function(args, fallback){
47        // summary:
48        //              A wrapper for WebSocket, than handles standard args and relative URLs
49        var ws = new WebSocket(new dojo._Url(document.baseURI.replace(/^http/i,'ws'), args.url));
50        ws.on = function(type, listener){
51                ws.addEventListener(type, listener, true);
52        };
53        var opened;
54        dojo.connect(ws, "onopen", function(event){
55                opened = true;
56        });
57        dojo.connect(ws, "onclose", function(event){
58                if(opened){
59                        return;
60                }
61                if(fallback){
62                        Socket.replace(ws, dojox.socket.LongPoll(args), true);
63                }
64        });
65        return ws;
66};
67Socket.replace = function(socket, newSocket, listenForOpen){
68        // make the original socket a proxy for the new socket
69        socket.send = dojo.hitch(newSocket, "send");
70        socket.close = dojo.hitch(newSocket, "close");
71        if(listenForOpen){
72                proxyEvent("open");
73        }
74        // redirect the events as well
75        dojo.forEach(["message", "close", "error"], proxyEvent);
76        function proxyEvent(type){
77                (newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){
78                        var newEvent = document.createEvent("MessageEvent");
79                        newEvent.initMessageEvent(event.type, false, false, event.data, event.origin, event.lastEventId, event.source);
80                        socket.dispatchEvent(newEvent);
81                }, true);
82        }
83};
84Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
85        // summary:
86        //              Provides a simple long-poll based comet-style socket/connection to a server and returns an
87        //              object implementing the WebSocket interface:
88        //              http://dev.w3.org/html5/websockets/#websocket
89        //      args:
90        //              This uses the same arguments as the other I/O functions in Dojo, with this addition:
91        //      args.interval:
92        //              Indicates the amount of time (in milliseconds) after a response was received
93        //              before another request is made. By default, a request is made immediately
94        //              after getting a response. The interval can be increased to reduce load on the
95        //              server or to do simple time-based polling where the server always responds
96        //              immediately.
97        //      args.transport:
98        //              Provide an alternate transport like dojo.io.script.get
99        // returns:
100        //              An object that implements the WebSocket API
101        // example:
102        //              | dojo.require("dojox.socket.LongPoll");
103        //              | var socket = dojox.socket.LongPoll({url:"/comet"});
104        //              or:
105        //              | dojo.require("dojox.socket.LongPoll");
106        //              | dojox.socket.LongPoll.add();
107        //              | var socket = dojox.socket({url:"/comet"});
108
109var cancelled = false,
110                first = true,
111                timeoutId,
112                connections = [];
113
114        // create the socket object
115        var socket = {
116                send: function(data){
117                        // summary:
118                        //              Send some data using XHR or provided transport
119                        var sendArgs = dojo.delegate(args);
120                        sendArgs.rawBody = data;
121                        clearTimeout(timeoutId);
122                        var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
123                                socket.transport(sendArgs);
124                        connections.push(deferred);
125                        deferred.then(function(response){
126                                // got a response
127                                socket.readyState = 1;
128                                // remove the current connection
129                                connections.splice(dojo.indexOf(connections, deferred), 1);
130                                // reconnect to listen for the next message if there are no active connections,
131                                // we queue it up in case one of the onmessage handlers has a message to send
132                                if(!connections.length){
133                                        timeoutId = setTimeout(connect, args.interval);
134                                }
135                                if(response){
136                                        // now send the message along to listeners
137                                        fire("message", {data: response}, deferred);
138                                }
139                        }, function(error){
140                                connections.splice(dojo.indexOf(connections, deferred), 1);
141                                // an error occurred, fire the appropriate event listeners
142                                if(!cancelled){
143                                        fire("error", {error:error}, deferred);
144                                        if(!connections.length){
145                                                socket.readyState = 3;
146                                                fire("close", {wasClean:false}, deferred);
147                                        }
148                                }
149                        });
150                        return deferred;
151                },
152                close: function(){
153                        // summary:
154                        //              Close the connection
155                        socket.readyState = 2;
156                        cancelled = true;
157                        for(var i = 0; i < connections.length; i++){
158                                connections[i].cancel();
159                        }
160                        socket.readyState = 3;
161                        fire("close", {wasClean:true});
162                },
163                transport: args.transport || dojo.xhrPost,
164                args: args,
165                url: args.url,
166                readyState: 0,
167                CONNECTING: 0,
168                OPEN: 1,
169                CLOSING: 2,
170                CLOSED: 3,
171                dispatchEvent: function(event){
172                        fire(event.type, event);
173                },
174                on: Evented.prototype.on,
175                firstRequest: function(args){
176                        // summary:
177                        //              This allows for special handling for the first request. This is useful for
178                        //              providing information to disambiguate between the first request and
179                        //              subsequent long-poll requests so the server can properly setup a
180                        //              connection on the first connection or reject a request for an expired
181                        //              connection if the request is not expecting to be the first for a connection.
182                        //              This method can be overriden. The default behavior is to include a Pragma
183                        //              header with a value of "start-long-poll"
184                        var headers = (args.headers || (args.headers = {}));
185                        headers.Pragma = "start-long-poll";
186                        try{
187                                return this.transport(args);
188                        }finally{
189                                // cleanup the header so it is not used on subsequent requests
190                                delete headers.Pragma;
191                        }
192                }
193        };
194        function connect(){
195                if(socket.readyState == 0){
196                        // we fire the open event now because we really don't know when the "socket"
197                        // is truly open, and this gives us a to do a send() and get it included in the
198                        // HTTP request
199                        fire("open",{});
200                }
201                // make the long-poll connection, to wait for response from the server
202                if(!connections.length){
203                        socket.send();
204                }
205        }
206        function fire(type, object, deferred){
207                if(socket["on" + type]){
208                        var event = document.createEvent("HTMLEvents");
209                        event.initEvent(type, false, false);
210                        dojo.mixin(event, object);
211                        event.ioArgs = deferred && deferred.ioArgs;
212                        socket["on" + type](event);
213                }
214        }
215        // provide an alias for Dojo's connect method
216        socket.connect = socket.on;
217        // do the initial connection
218        setTimeout(connect);
219        return socket;
220};
221return Socket;
222});
Note: See TracBrowser for help on using the repository browser.