123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- define("dojox/socket", [
- "dojo/_base/array",
- "dojo/_base/lang",
- "dojo/_base/xhr",
- "dojo/aspect",
- "dojo/on",
- "dojo/Evented",
- "dojo/_base/url"
- ], function(array, lang, xhr, aspect, on, Evented, dBaseUrl) {
- var WebSocket = window.WebSocket;
- var Socket = function(/*dojo.__XhrArgs*/ argsOrUrl){
- // summary:
- // Provides a simple socket connection using WebSocket, or alternate
- // communication mechanisms in legacy browsers for comet-style communication. This is based
- // on the WebSocket API and returns an object that implements the WebSocket interface:
- // http://dev.w3.org/html5/websockets/#websocket
- // description:
- // Provides socket connections. This can be used with virtually any Comet protocol.
- // argsOrUrl:
- // This uses the same arguments as the other I/O functions in Dojo, or a
- // URL to connect to. The URL should be a relative URL in order to properly
- // work with WebSockets (it can still be host relative, like //other-site.org/endpoint)
- // returns:
- // An object that implements the WebSocket API
- // example:
- // | require(["dojox/socket", "dojo/aspect"], function(socket, aspect) {
- // | var sock = socket({"url://comet-server/comet");
- // | // we could also add auto-reconnect support
- // | // now we can connect to standard HTML5 WebSocket-style events
- // | aspect.after(socket, "onmessage", function(event){
- // | var message = event.data;
- // | // do something with the message
- // | });
- // | // send something
- // | sock.send("hi there");
- // | ...
- // | });
- // You can also use the Reconnect module:
- // | require["dojox/socket", "dojox/socket/Reconnect"], function(dxSocket, reconnect){
- // | var socket = dxSocket({url:"/comet"});
- // | // add auto-reconnect support
- // | socket = reconnect(socket);
- if(typeof argsOrUrl == "string"){
- argsOrUrl = {url: argsOrUrl};
- }
- return WebSocket ? Socket.WebSocket(argsOrUrl, true) : Socket.LongPoll(argsOrUrl);
- };
- Socket.WebSocket = function(args, fallback){
- // summary:
- // A wrapper for WebSocket, than handles standard args and relative URLs
- var baseURI = document.baseURI || window.location.href;
- var ws = new WebSocket(new dBaseUrl(baseURI.replace(/^http/i,'ws'), args.url));
- ws.on = function(type, listener){
- ws.addEventListener(type, listener, true);
- };
- var opened;
- aspect.after(ws, "onopen", function(event){
- opened = true;
- }, true);
- aspect.after(ws, "onclose", function(event){
- if(opened){
- return;
- }
- if(fallback){
- Socket.replace(ws, Socket.LongPoll(args), true);
- }
- }, true);
- return ws;
- };
- Socket.replace = function(socket, newSocket, listenForOpen){
- // make the original socket a proxy for the new socket
- socket.send = lang.hitch(newSocket, "send");
- socket.close = lang.hitch(newSocket, "close");
- var proxyEvent = function(type){
- (newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){
- on.emit(socket, event.type, event);
- }, true);
- };
- if(listenForOpen){
- proxyEvent("open");
- }
- // redirect the events as well
- array.forEach(["message", "close", "error"], proxyEvent);
- };
- Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
- // summary:
- // Provides a simple long-poll based comet-style socket/connection to a server and returns an
- // object implementing the WebSocket interface:
- // http://dev.w3.org/html5/websockets/#websocket
- // args:
- // This uses the same arguments as the other I/O functions in Dojo, with this addition:
- // args.interval:
- // Indicates the amount of time (in milliseconds) after a response was received
- // before another request is made. By default, a request is made immediately
- // after getting a response. The interval can be increased to reduce load on the
- // server or to do simple time-based polling where the server always responds
- // immediately.
- // args.transport:
- // Provide an alternate transport like dojo.io.script.get
- // returns:
- // An object that implements the WebSocket API
- // example:
- // | dojo.require("dojox.socket.LongPoll");
- // | var socket = dojox.socket.LongPoll({url:"/comet"});
- // or:
- // | dojo.require("dojox.socket.LongPoll");
- // | dojox.socket.LongPoll.add();
- // | var socket = dojox.socket({url:"/comet"});
- var cancelled = false,
- first = true,
- timeoutId,
- connections = [];
- // create the socket object
- var fire, connect;
- var socket = {
- send: function(data){
- // summary:
- // Send some data using XHR or provided transport
- var sendArgs = lang.delegate(args);
- sendArgs.rawBody = data;
- clearTimeout(timeoutId);
- var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
- socket.transport(sendArgs);
- connections.push(deferred);
- deferred.then(function(response){
- // got a response
- socket.readyState = 1;
- // remove the current connection
- connections.splice(array.indexOf(connections, deferred), 1);
- // reconnect to listen for the next message if there are no active connections,
- // we queue it up in case one of the onmessage handlers has a message to send
- if(!connections.length){
- timeoutId = setTimeout(connect, args.interval);
- }
- if(response){
- // now send the message along to listeners
- fire("message", {data: response}, deferred);
- }
- }, function(error){
- connections.splice(array.indexOf(connections, deferred), 1);
- // an error occurred, fire the appropriate event listeners
- if(!cancelled){
- fire("error", {error:error}, deferred);
- if(!connections.length){
- socket.readyState = 3;
- fire("close", {wasClean:false}, deferred);
- }
- }
- });
- return deferred;
- },
- close: function(){
- // summary:
- // Close the connection
- socket.readyState = 2;
- cancelled = true;
- var i;
- for(i = 0; i < connections.length; i++){
- connections[i].cancel();
- }
- socket.readyState = 3;
- fire("close", {wasClean:true});
- },
- transport: args.transport || xhr.post,
- args: args,
- url: args.url,
- readyState: 0,
- CONNECTING: 0,
- OPEN: 1,
- CLOSING: 2,
- CLOSED: 3,
- on: Evented.prototype.on,
- firstRequest: function(args){
- // summary:
- // This allows for special handling for the first request. This is useful for
- // providing information to disambiguate between the first request and
- // subsequent long-poll requests so the server can properly setup a
- // connection on the first connection or reject a request for an expired
- // connection if the request is not expecting to be the first for a connection.
- // This method can be overriden. The default behavior is to include a Pragma
- // header with a value of "start-long-poll"
- var headers = (args.headers || (args.headers = {}));
- headers.Pragma = "start-long-poll";
- try{
- return this.transport(args);
- }finally{
- // cleanup the header so it is not used on subsequent requests
- delete headers.Pragma;
- }
- }
- };
- fire = function(type, object, deferred){
- if(socket["on" + type]){
- object.ioArgs = deferred && deferred.ioArgs;
- object.type = type;
- on.emit(socket, type, object);
- }
- };
- connect = function(){
- if(socket.readyState == 0){
- // we fire the open event now because we really don't know when the "socket"
- // is truly open, and this gives us a to do a send() and get it included in the
- // HTTP request
- fire("open",{});
- }
- // make the long-poll connection, to wait for response from the server
- if(!connections.length){
- socket.send();
- }
- };
- // provide an alias for Dojo's connect method
- socket.connect = socket.on;
- // do the initial connection
- setTimeout(connect);
- return socket;
- };
- return Socket;
- });
|