source: Dev/trunk/src/client/dojox/socket.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 7.3 KB
Line 
1define("dojox/socket", ["dojo", "dojo/on", "dojo/Evented", "dojo/cookie", "dojo/_base/url"], function(dojo, on, 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({"url://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                        on.emit(socket, event.type, event);
79                }, true);
80        }
81};
82Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
83        // summary:
84        //              Provides a simple long-poll based comet-style socket/connection to a server and returns an
85        //              object implementing the WebSocket interface:
86        //              http://dev.w3.org/html5/websockets/#websocket
87        // args:
88        //              This uses the same arguments as the other I/O functions in Dojo, with this addition:
89        //      args.interval:
90        //              Indicates the amount of time (in milliseconds) after a response was received
91        //              before another request is made. By default, a request is made immediately
92        //              after getting a response. The interval can be increased to reduce load on the
93        //              server or to do simple time-based polling where the server always responds
94        //              immediately.
95        //      args.transport:
96        //              Provide an alternate transport like dojo.io.script.get
97        // returns:
98        //              An object that implements the WebSocket API
99        // example:
100        //              | dojo.require("dojox.socket.LongPoll");
101        //              | var socket = dojox.socket.LongPoll({url:"/comet"});
102        //              or:
103        //              | dojo.require("dojox.socket.LongPoll");
104        //              | dojox.socket.LongPoll.add();
105        //              | var socket = dojox.socket({url:"/comet"});
106
107var cancelled = false,
108                first = true,
109                timeoutId,
110                connections = [];
111
112        // create the socket object
113        var socket = {
114                send: function(data){
115                        // summary:
116                        //              Send some data using XHR or provided transport
117                        var sendArgs = dojo.delegate(args);
118                        sendArgs.rawBody = data;
119                        clearTimeout(timeoutId);
120                        var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
121                                socket.transport(sendArgs);
122                        connections.push(deferred);
123                        deferred.then(function(response){
124                                // got a response
125                                socket.readyState = 1;
126                                // remove the current connection
127                                connections.splice(dojo.indexOf(connections, deferred), 1);
128                                // reconnect to listen for the next message if there are no active connections,
129                                // we queue it up in case one of the onmessage handlers has a message to send
130                                if(!connections.length){
131                                        timeoutId = setTimeout(connect, args.interval);
132                                }
133                                if(response){
134                                        // now send the message along to listeners
135                                        fire("message", {data: response}, deferred);
136                                }
137                        }, function(error){
138                                connections.splice(dojo.indexOf(connections, deferred), 1);
139                                // an error occurred, fire the appropriate event listeners
140                                if(!cancelled){
141                                        fire("error", {error:error}, deferred);
142                                        if(!connections.length){
143                                                socket.readyState = 3;
144                                                fire("close", {wasClean:false}, deferred);
145                                        }
146                                }
147                        });
148                        return deferred;
149                },
150                close: function(){
151                        // summary:
152                        //              Close the connection
153                        socket.readyState = 2;
154                        cancelled = true;
155                        for(var i = 0; i < connections.length; i++){
156                                connections[i].cancel();
157                        }
158                        socket.readyState = 3;
159                        fire("close", {wasClean:true});
160                },
161                transport: args.transport || dojo.xhrPost,
162                args: args,
163                url: args.url,
164                readyState: 0,
165                CONNECTING: 0,
166                OPEN: 1,
167                CLOSING: 2,
168                CLOSED: 3,
169                on: Evented.prototype.on,
170                firstRequest: function(args){
171                        // summary:
172                        //              This allows for special handling for the first request. This is useful for
173                        //              providing information to disambiguate between the first request and
174                        //              subsequent long-poll requests so the server can properly setup a
175                        //              connection on the first connection or reject a request for an expired
176                        //              connection if the request is not expecting to be the first for a connection.
177                        //              This method can be overriden. The default behavior is to include a Pragma
178                        //              header with a value of "start-long-poll"
179                        var headers = (args.headers || (args.headers = {}));
180                        headers.Pragma = "start-long-poll";
181                        try{
182                                return this.transport(args);
183                        }finally{
184                                // cleanup the header so it is not used on subsequent requests
185                                delete headers.Pragma;
186                        }
187                }
188        };
189        function connect(){
190                if(socket.readyState == 0){
191                        // we fire the open event now because we really don't know when the "socket"
192                        // is truly open, and this gives us a to do a send() and get it included in the
193                        // HTTP request
194                        fire("open",{});
195                }
196                // make the long-poll connection, to wait for response from the server
197                if(!connections.length){
198                        socket.send();
199                }
200        }
201        function fire(type, object, deferred){
202                if(socket["on" + type]){
203                        object.ioArgs = deferred && deferred.ioArgs;
204                        object.type = type;
205                        on.emit(socket, type, object);
206                }
207        }
208        // provide an alias for Dojo's connect method
209        socket.connect = socket.on;
210        // do the initial connection
211        setTimeout(connect);
212        return socket;
213};
214return Socket;
215});
Note: See TracBrowser for help on using the repository browser.