diff --git a/README.md b/README.md index 22f44f2..d39f41e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Each page or widget wanting to use the service needs to import the following Jav socket.io.js wave.js -These can all be found in the "client" folder +These can all be found in the "client" folder; you can also load the current socket.io.js client from the server itself (e.g. http://localhost:8081/socket.io/socket.io.js) In addition you need to call the following methods in your own scripts after importing the wave.js library: diff --git a/client/socket.io.js b/client/socket.io.js index a05d262..49e9b60 100644 --- a/client/socket.io.js +++ b/client/socket.io.js @@ -1,4 +1,4 @@ -/*! Socket.IO.js build:0.7.4, development. Copyright(c) 2011 LearnBoost MIT Licensed */ +/*! Socket.IO.js build:0.9.5, development. Copyright(c) 2011 LearnBoost MIT Licensed */ /** * socket.io @@ -6,7 +6,7 @@ * MIT Licensed */ -(function (exports) { +(function (exports, global) { /** * IO namespace. @@ -22,7 +22,7 @@ * @api public */ - io.version = '0.7.4'; + io.version = '0.9.5'; /** * Protocol implemented. @@ -69,19 +69,22 @@ , uuri , socket; - if ('undefined' != typeof document) { - uri.protocol = uri.protocol || document.location.protocol.slice(0, -1); - uri.host = uri.host || document.domain; - uri.port = uri.port || document.location.port; + if (global && global.location) { + uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); + uri.host = uri.host || (global.document + ? global.document.domain : global.location.hostname); + uri.port = uri.port || global.location.port; } uuri = io.util.uniqueUri(uri); var options = { host: uri.host - , secure: uri.protocol == 'https' - , port: uri.port || 80 + , secure: 'https' == uri.protocol + , port: uri.port || ('https' == uri.protocol ? 443 : 80) + , query: uri.query || '' }; + io.util.merge(options, details); if (options['force new connection'] || !io.sockets[uuri]) { @@ -98,15 +101,14 @@ return socket.of(uri.path.length > 1 ? uri.path : ''); }; -})('object' === typeof module ? module.exports : (window.io = {})); - +})('object' === typeof module ? module.exports : (this.io = {}), this); /** * socket.io * Copyright(c) 2011 LearnBoost * MIT Licensed */ -(function (exports) { +(function (exports, global) { /** * Utilities namespace. @@ -153,7 +155,7 @@ , host = uri.host , port = uri.port; - if ('undefined' != typeof document) { + if ('document' in global) { host = host || document.domain; port = port || (protocol == 'https' && document.location.protocol !== 'https:' ? 443 : document.location.port); @@ -168,6 +170,52 @@ return (protocol || 'http') + '://' + host + ':' + (port || 80); }; + /** + * Mergest 2 query strings in to once unique query string + * + * @param {String} base + * @param {String} addition + * @api public + */ + + util.query = function (base, addition) { + var query = util.chunkQuery(base || '') + , components = []; + + util.merge(query, util.chunkQuery(addition || '')); + for (var part in query) { + if (query.hasOwnProperty(part)) { + components.push(part + '=' + query[part]); + } + } + + return components.length ? '?' + components.join('&') : ''; + }; + + /** + * Transforms a querystring in to an object + * + * @param {String} qs + * @api public + */ + + util.chunkQuery = function (qs) { + var query = {} + , params = qs.split('&') + , i = 0 + , l = params.length + , kv; + + for (; i < l; ++i) { + kv = params[i].split('='); + if (kv[0]) { + query[kv[0]] = kv[1]; + } + } + + return query; + }; + /** * Executes the given function when the page is loaded. * @@ -180,11 +228,11 @@ var pageLoaded = false; util.load = function (fn) { - if (document.readyState === 'complete' || pageLoaded) { + if ('document' in global && document.readyState === 'complete' || pageLoaded) { return fn(); } - util.on(window, 'load', fn, false); + util.on(global, 'load', fn, false); }; /** @@ -196,7 +244,7 @@ util.on = function (element, event, fn, capture) { if (element.attachEvent) { element.attachEvent('on' + event, fn); - } else { + } else if (element.addEventListener) { element.addEventListener(event, fn, capture); } }; @@ -210,20 +258,19 @@ */ util.request = function (xdomain) { - if ('undefined' != typeof window) { - if (xdomain && window.XDomainRequest) { - return new XDomainRequest(); - }; - if (window.XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { - return new XMLHttpRequest(); - }; + if (xdomain && 'undefined' != typeof XDomainRequest) { + return new XDomainRequest(); + } - if (!xdomain) { - try { - return new window.ActiveXObject('Microsoft.XMLHTTP'); - } catch(e) { } - } + if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { + return new XMLHttpRequest(); + } + + if (!xdomain) { + try { + return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); + } catch(e) { } } return null; @@ -254,7 +301,7 @@ */ util.defer = function (fn) { - if (!util.ua.webkit) { + if (!util.ua.webkit || 'undefined' != typeof importScripts) { return fn(); } @@ -305,8 +352,9 @@ */ util.inherit = function (ctor, ctor2) { - ctor.prototype = new ctor2; - util.merge(ctor, ctor2); + function f() {}; + f.prototype = ctor2.prototype; + ctor.prototype = new f; }; /** @@ -332,7 +380,7 @@ util.intersect = function (arr, arr2) { var ret = [] , longest = arr.length > arr2.length ? arr : arr2 - , shortest = arr.length > arr2.length ? arr2 : arr + , shortest = arr.length > arr2.length ? arr2 : arr; for (var i = 0, l = shortest.length; i < l; i++) { if (~util.indexOf(longest, shortest[i])) @@ -350,12 +398,9 @@ */ util.indexOf = function (arr, o, i) { - if (Array.prototype.indexOf) { - return Array.prototype.indexOf.call(arr, o, i); - } - - for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0 - ; i < j && arr[i] !== o; i++); + + for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; + i < j && arr[i] !== o; i++) {} return j <= i ? -1 : i; }; @@ -389,8 +434,7 @@ * @api public */ - util.ua.hasCORS = 'undefined' != typeof window && window.XMLHttpRequest && - (function () { + util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { try { var a = new XMLHttpRequest(); } catch (e) { @@ -409,7 +453,7 @@ util.ua.webkit = 'undefined' != typeof navigator && /webkit/i.test(navigator.userAgent); -})('undefined' != typeof window ? io : module.exports); +})('undefined' != typeof io ? io : module.exports, this); /** * socket.io @@ -990,10 +1034,10 @@ switch (packet.type) { case 'error': var reason = packet.reason ? indexOf(reasons, packet.reason) : '' - , adv = packet.advice ? indexOf(advice, packet.advice) : '' + , adv = packet.advice ? indexOf(advice, packet.advice) : ''; if (reason !== '' || adv !== '') - data = reason + (adv !== '' ? ('+' + adv) : '') + data = reason + (adv !== '' ? ('+' + adv) : ''); break; @@ -1057,7 +1101,7 @@ for (var i = 0, l = packets.length; i < l; i++) { var packet = packets[i]; - decoded += '\ufffd' + packet.length + '\ufffd' + packets[i] + decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; } return decoded; @@ -1069,7 +1113,7 @@ * @api private */ - var regexp = /^([^:]+):([0-9]+)?(\+)?:([^:]+)?:?(.*)?$/; + var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; parser.decodePacket = function (data) { var pieces = data.match(regexp); @@ -1178,7 +1222,6 @@ 'undefined' != typeof io ? io : module.exports , 'undefined' != typeof io ? io : module.parent.exports ); - /** * socket.io * Copyright(c) 2011 LearnBoost @@ -1222,7 +1265,13 @@ Transport.prototype.onData = function (data) { this.clearCloseTimeout(); - this.setCloseTimeout(); + + // If the connection in currently open (or in a reopening state) reset the close + // timeout since we have just received data. This check is necessary so + // that we don't reset the timeout on an explicitly disconnected connection. + if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { + this.setCloseTimeout(); + } if (data !== '') { // todo: we should only do decodePayload for xhr transports @@ -1245,6 +1294,8 @@ */ Transport.prototype.onPacket = function (packet) { + this.socket.setHeartbeatTimeout(); + if (packet.type == 'heartbeat') { return this.onHeartbeat(); } @@ -1253,6 +1304,10 @@ this.onConnect(); } + if (packet.type == 'error' && packet.advice == 'reconnect') { + this.open = false; + } + this.socket.onPacket(packet); return this; @@ -1281,7 +1336,7 @@ */ Transport.prototype.onDisconnect = function () { - if (this.close) this.close(); + if (this.close && this.open) this.close(); this.clearTimeouts(); this.socket.onDisconnect(); return this; @@ -1376,8 +1431,8 @@ }, this.socket.options['reopen delay']);*/ this.open = false; - this.setCloseTimeout(); this.socket.onClose(); + this.onDisconnect(); }; /** @@ -1396,18 +1451,29 @@ + options.resource + '/' + io.protocol + '/' + this.name + '/' + this.sessid; }; + + /** + * Checks if the transport is ready to start a connection. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + Transport.prototype.ready = function (socket, fn) { + fn.call(this); + }; })( 'undefined' != typeof io ? io : module.exports , 'undefined' != typeof io ? io : module.parent.exports ); - /** * socket.io * Copyright(c) 2011 LearnBoost * MIT Licensed */ -(function (exports, io) { +(function (exports, io, global) { /** * Expose constructor. @@ -1416,7 +1482,7 @@ exports.Socket = Socket; /** - * Create a new `Socket.IO client` which can establish a persisent + * Create a new `Socket.IO client` which can establish a persistent * connection with a Socket.IO enabled server. * * @api public @@ -1426,17 +1492,19 @@ this.options = { port: 80 , secure: false - , document: document + , document: 'document' in global ? document : false , resource: 'socket.io' , transports: io.transports , 'connect timeout': 10000 , 'try multiple transports': true , 'reconnect': true , 'reconnection delay': 500 + , 'reconnection limit': Infinity , 'reopen delay': 3000 , 'max reconnection attempts': 10 , 'sync disconnect on unload': true , 'auto connect': true + , 'flash policy port': 10843 }; io.util.merge(this.options, options); @@ -1453,7 +1521,7 @@ (!this.isXDomain() || io.util.ua.hasCORS)) { var self = this; - io.util.on(window, 'beforeunload', function () { + io.util.on(global, 'unload', function () { self.disconnectSync(); }, false); } @@ -1529,14 +1597,14 @@ var url = [ 'http' + (options.secure ? 's' : '') + ':/' , options.host + ':' + options.port - , this.options.resource + , options.resource , io.protocol - , '?t=' + + new Date + , io.util.query(this.options.query, 't=' + +new Date) ].join('/'); - if (this.isXDomain()) { + if (this.isXDomain() && !io.util.ua.hasCORS) { var insertAt = document.getElementsByTagName('script')[0] - , script = document.createElement('SCRIPT'); + , script = document.createElement('script'); script.src = url + '&jsonp=' + io.j.length; insertAt.parentNode.insertBefore(script, insertAt); @@ -1548,7 +1616,8 @@ } else { var xhr = io.util.request(); - xhr.open('GET', url); + xhr.open('GET', url, true); + xhr.withCredentials = true; xhr.onreadystatechange = function () { if (xhr.readyState == 4) { xhr.onreadystatechange = empty; @@ -1603,46 +1672,53 @@ self.sessionid = sid; self.closeTimeout = close * 1000; self.heartbeatTimeout = heartbeat * 1000; - self.transports = io.util.intersect( + self.transports = transports ? io.util.intersect( transports.split(',') , self.options.transports - ); + ) : self.options.transports; + + self.setHeartbeatTimeout(); function connect (transports){ + if (self.transport) self.transport.clearTimeouts(); + self.transport = self.getTransport(transports); if (!self.transport) return self.publish('connect_failed'); - self.connecting = true; - self.publish('connecting', self.transport.name); - self.transport.open(); + // once the transport is ready + self.transport.ready(self, function () { + self.connecting = true; + self.publish('connecting', self.transport.name); + self.transport.open(); - if (self.options['connect timeout']) { - self.connectTimeoutTimer = setTimeout(function () { - if (!self.connected) { - self.connecting = false; + if (self.options['connect timeout']) { + self.connectTimeoutTimer = setTimeout(function () { + if (!self.connected) { + self.connecting = false; - if (self.options['try multiple transports']) { - if (!self.remainingTransports) { - self.remainingTransports = self.transports.slice(0); - } + if (self.options['try multiple transports']) { + if (!self.remainingTransports) { + self.remainingTransports = self.transports.slice(0); + } - var remaining = self.remainingTransports; + var remaining = self.remainingTransports; - while (remaining.length > 0 && remaining.splice(0,1)[0] != - self.transport.name) {} + while (remaining.length > 0 && remaining.splice(0,1)[0] != + self.transport.name) {} - if (remaining.length){ - connect(remaining); - } else { - self.publish('connect_failed'); + if (remaining.length){ + connect(remaining); + } else { + self.publish('connect_failed'); + } } } - } - }, self.options['connect timeout']); - } + }, self.options['connect timeout']); + } + }); } - connect(); + connect(self.transports); self.once('connect', function (){ clearTimeout(self.connectTimeoutTimer); @@ -1654,6 +1730,22 @@ return this; }; + /** + * Clears and sets a new heartbeat timeout using the value given by the + * server during the handshake. + * + * @api private + */ + + Socket.prototype.setHeartbeatTimeout = function () { + clearTimeout(this.heartbeatTimeoutTimer); + + var self = this; + this.heartbeatTimeoutTimer = setTimeout(function () { + self.transport.onClose(); + }, this.heartbeatTimeout); + }; + /** * Sends a message. * @@ -1695,7 +1787,7 @@ */ Socket.prototype.disconnect = function () { - if (this.connected) { + if (this.connected || this.connecting) { if (this.open) { this.of('').packet({ type: 'disconnect' }); } @@ -1733,8 +1825,12 @@ */ Socket.prototype.isXDomain = function () { - var locPort = window.location.port || 80; - return this.options.host !== document.domain || this.options.port != locPort; + + var port = global.location.port || + ('https:' == global.location.protocol ? 443 : 80); + + return this.options.host !== global.location.hostname + || this.options.port != port; }; /** @@ -1744,13 +1840,15 @@ */ Socket.prototype.onConnect = function () { - this.connected = true; - this.connecting = false; - if (!this.doBuffer) { - // make sure to flush the buffer - this.setBuffer(false); + if (!this.connected) { + this.connected = true; + this.connecting = false; + if (!this.doBuffer) { + // make sure to flush the buffer + this.setBuffer(false); + } + this.emit('connect'); } - this.emit('connect'); }; /** @@ -1771,6 +1869,7 @@ Socket.prototype.onClose = function () { this.open = false; + clearTimeout(this.heartbeatTimeoutTimer); }; /** @@ -1791,9 +1890,11 @@ Socket.prototype.onError = function (err) { if (err && err.advice) { - if (err.advice === 'reconnect' && this.connected) { + if (err.advice === 'reconnect' && (this.connected || this.connecting)) { this.disconnect(); - this.reconnect(); + if (this.options.reconnect) { + this.reconnect(); + } } } @@ -1807,19 +1908,22 @@ */ Socket.prototype.onDisconnect = function (reason) { - var wasConnected = this.connected; + var wasConnected = this.connected + , wasConnecting = this.connecting; this.connected = false; this.connecting = false; this.open = false; - if (wasConnected) { + if (wasConnected || wasConnecting) { this.transport.close(); this.transport.clearTimeouts(); - this.publish('disconnect', reason); + if (wasConnected) { + this.publish('disconnect', reason); - if ('booted' != reason && this.options.reconnect && !this.reconnecting) { - this.reconnect(); + if ('booted' != reason && this.options.reconnect && !this.reconnecting) { + this.reconnect(); + } } } }; @@ -1838,12 +1942,20 @@ var self = this , maxAttempts = this.options['max reconnection attempts'] , tryMultiple = this.options['try multiple transports'] + , limit = this.options['reconnection limit']; function reset () { if (self.connected) { + for (var i in self.namespaces) { + if (self.namespaces.hasOwnProperty(i) && '' !== i) { + self.namespaces[i].packet({ type: 'connect' }); + } + } self.publish('reconnect', self.transport.name, self.reconnectionAttempts); } + clearTimeout(self.reconnectionTimer); + self.removeListener('connect_failed', maybeReconnect); self.removeListener('connect', maybeReconnect); @@ -1882,7 +1994,10 @@ reset(); } } else { - self.reconnectionDelay *= 2; // exponential back off + if (self.reconnectionDelay < limit) { + self.reconnectionDelay *= 2; // exponential back off + } + self.connect(); self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); @@ -1898,6 +2013,7 @@ })( 'undefined' != typeof io ? io : module.exports , 'undefined' != typeof io ? io : module.parent.exports + , this ); /** * socket.io @@ -2148,7 +2264,7 @@ * MIT Licensed */ -(function (exports, io) { +(function (exports, io, global) { /** * Expose constructor. @@ -2194,9 +2310,17 @@ */ WS.prototype.open = function () { - this.websocket = new WebSocket(this.prepareUrl()); + var query = io.util.query(this.socket.options.query) + , self = this + , Socket + + + if (!Socket) { + Socket = global.MozWebSocket || global.WebSocket; + } + + this.websocket = new Socket(this.prepareUrl() + query); - var self = this; this.websocket.onopen = function () { self.onOpen(); self.socket.setBuffer(false); @@ -2283,7 +2407,8 @@ */ WS.check = function () { - return 'WebSocket' in window && !('__addTask' in WebSocket); + return ('WebSocket' in global && !('__addTask' in WebSocket)) + || 'MozWebSocket' in global; }; /** @@ -2308,6 +2433,7 @@ })( 'undefined' != typeof io ? io.Transport : module.exports , 'undefined' != typeof io ? io : module.parent.exports + , this ); /** @@ -2316,530 +2442,14 @@ * MIT Licensed */ -(function (exports, io) { +(function (exports, io, global) { /** * Expose constructor. - */ - - exports.flashsocket = Flashsocket; - - /** - * The Flashsocket transport. This is a API wrapper for the HTML5 WebSocket - * specification. It uses a .swf file to communicate with the server. If you want - * to serve the .swf file from a other server than where the Socket.IO script is - * coming from you need to use the insecure version of the .swf. More information - * about this can be found on the github page. - * - * @constructor - * @extends {io.Transport.websocket} - * @api public - */ - - function Flashsocket () { - io.Transport.websocket.apply(this, arguments); - }; - - /** - * Inherits from Transport. - */ - - io.util.inherit(Flashsocket, io.Transport.websocket); - - /** - * Transport name - * - * @api public - */ - - Flashsocket.prototype.name = 'flashsocket'; - - /** - *Disconnect the established `Flashsocket` connection. This is done by adding a - * new task to the Flashsocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.open = function () { - var self = this, args = arguments; - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.open.apply(self, args); - }); - return this; - }; - - /** - * Sends a message to the Socket.IO server. This is done by adding a new - * task to the Flashsocket. The rest will be handled off by the `WebSocket` - * transport. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.send = function () { - var self = this, args = arguments; - WebSocket.__addTask(function () { - io.Transport.websocket.prototype.send.apply(self, args); - }); - return this; - }; - - /** - * Disconnects the established `Flashsocket` connection. - * - * @returns {Transport} - * @api public - */ - - Flashsocket.prototype.close = function () { - WebSocket.__tasks.length = 0; - io.Transport.websocket.prototype.close.call(this); - return this; - }; - - /** - * Check if the Flashsocket transport is supported as it requires that the Adobe - * Flash Player plugin version `10.0.0` or greater is installed. And also check if - * the polyfill is correctly loaded. * - * @returns {Boolean} * @api public */ - Flashsocket.check = function (socket) { - if ( - typeof WebSocket == 'undefined' - || !('__initialize' in WebSocket) || !swfobject - ) return false; - - var supported = swfobject.getFlashPlayerVersion().major >= 10 - , options = socket.options - , path = [ - 'http' + (options.secure ? 's' : '') + ':/' - , options.host + ':' + options.port - , options.resource - , 'static/flashsocket' - , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' - ]; - - // Only start downloading the swf file when the checked that this browser - // actually supports it - if (supported && !Flashsocket.loaded) { - if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { - // Set the correct file based on the XDomain settings - WEB_SOCKET_SWF_LOCATION = path.join('/'); - } - - WebSocket.__initialize(); - Flashsocket.loaded = true; - } - - return supported; - }; - - /** - * Check if the Flashsocket transport can be used as cross domain / cross origin - * transport. Because we can't see which type (secure or insecure) of .swf is used - * we will just return true. - * - * @returns {Boolean} - * @api public - */ - - Flashsocket.xdomainCheck = function () { - return true; - }; - - /** - * Disable AUTO_INITIALIZATION - */ - - if (typeof window != 'undefined') { - WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; - } - - /** - * Add the transport to your public io.transports array. - * - * @api private - */ - - io.transports.push('flashsocket'); -})( - 'undefined' != typeof io ? io.Transport : module.exports - , 'undefined' != typeof io ? io : module.parent.exports -); -/* SWFObject v2.2 - is released under the MIT License -*/ -var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab -// License: New BSD License -// Reference: http://dev.w3.org/html5/websockets/ -// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol - -(function() { - - if (window.WebSocket) return; - - var console = window.console; - if (!console || !console.log || !console.error) { - console = {log: function(){ }, error: function(){ }}; - } - - if (!swfobject.hasFlashPlayerVersion("10.0.0")) { - console.error("Flash Player >= 10.0.0 is required."); - return; - } - if (location.protocol == "file:") { - console.error( - "WARNING: web-socket-js doesn't work in file:///... URL " + - "unless you set Flash Security Settings properly. " + - "Open the page via Web server i.e. http://..."); - } - - /** - * This class represents a faux web socket. - * @param {string} url - * @param {array or string} protocols - * @param {string} proxyHost - * @param {int} proxyPort - * @param {string} headers - */ - WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { - var self = this; - self.__id = WebSocket.__nextId++; - WebSocket.__instances[self.__id] = self; - self.readyState = WebSocket.CONNECTING; - self.bufferedAmount = 0; - self.__events = {}; - if (!protocols) { - protocols = []; - } else if (typeof protocols == "string") { - protocols = [protocols]; - } - // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. - // Otherwise, when onopen fires immediately, onopen is called before it is set. - setTimeout(function() { - WebSocket.__addTask(function() { - WebSocket.__flash.create( - self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); - }); - }, 0); - }; - - /** - * Send data to the web socket. - * @param {string} data The data to send to the socket. - * @return {boolean} True for success, false for failure. - */ - WebSocket.prototype.send = function(data) { - if (this.readyState == WebSocket.CONNECTING) { - throw "INVALID_STATE_ERR: Web Socket connection has not been established"; - } - // We use encodeURIComponent() here, because FABridge doesn't work if - // the argument includes some characters. We don't use escape() here - // because of this: - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions - // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't - // preserve all Unicode characters either e.g. "\uffff" in Firefox. - // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require - // additional testing. - var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); - if (result < 0) { // success - return true; - } else { - this.bufferedAmount += result; - return false; - } - }; - - /** - * Close this web socket gracefully. - */ - WebSocket.prototype.close = function() { - if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { - return; - } - this.readyState = WebSocket.CLOSING; - WebSocket.__flash.close(this.__id); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.addEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) { - this.__events[type] = []; - } - this.__events[type].push(listener); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture - * @return void - */ - WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { - if (!(type in this.__events)) return; - var events = this.__events[type]; - for (var i = events.length - 1; i >= 0; --i) { - if (events[i] === listener) { - events.splice(i, 1); - break; - } - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {Event} event - * @return void - */ - WebSocket.prototype.dispatchEvent = function(event) { - var events = this.__events[event.type] || []; - for (var i = 0; i < events.length; ++i) { - events[i](event); - } - var handler = this["on" + event.type]; - if (handler) handler(event); - }; - - /** - * Handles an event from Flash. - * @param {Object} flashEvent - */ - WebSocket.prototype.__handleEvent = function(flashEvent) { - if ("readyState" in flashEvent) { - this.readyState = flashEvent.readyState; - } - if ("protocol" in flashEvent) { - this.protocol = flashEvent.protocol; - } - - var jsEvent; - if (flashEvent.type == "open" || flashEvent.type == "error") { - jsEvent = this.__createSimpleEvent(flashEvent.type); - } else if (flashEvent.type == "close") { - // TODO implement jsEvent.wasClean - jsEvent = this.__createSimpleEvent("close"); - } else if (flashEvent.type == "message") { - var data = decodeURIComponent(flashEvent.message); - jsEvent = this.__createMessageEvent("message", data); - } else { - throw "unknown event type: " + flashEvent.type; - } - - this.dispatchEvent(jsEvent); - }; - - WebSocket.prototype.__createSimpleEvent = function(type) { - if (document.createEvent && window.Event) { - var event = document.createEvent("Event"); - event.initEvent(type, false, false); - return event; - } else { - return {type: type, bubbles: false, cancelable: false}; - } - }; - - WebSocket.prototype.__createMessageEvent = function(type, data) { - if (document.createEvent && window.MessageEvent && !window.opera) { - var event = document.createEvent("MessageEvent"); - event.initMessageEvent("message", false, false, data, null, null, window, null); - return event; - } else { - // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. - return {type: type, data: data, bubbles: false, cancelable: false}; - } - }; - - /** - * Define the WebSocket readyState enumeration. - */ - WebSocket.CONNECTING = 0; - WebSocket.OPEN = 1; - WebSocket.CLOSING = 2; - WebSocket.CLOSED = 3; - - WebSocket.__flash = null; - WebSocket.__instances = {}; - WebSocket.__tasks = []; - WebSocket.__nextId = 0; - - /** - * Load a new flash security policy file. - * @param {string} url - */ - WebSocket.loadFlashPolicyFile = function(url){ - WebSocket.__addTask(function() { - WebSocket.__flash.loadManualPolicyFile(url); - }); - }; - - /** - * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. - */ - WebSocket.__initialize = function() { - if (WebSocket.__flash) return; - - if (WebSocket.__swfLocation) { - // For backword compatibility. - window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; - } - if (!window.WEB_SOCKET_SWF_LOCATION) { - console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); - return; - } - var container = document.createElement("div"); - container.id = "webSocketContainer"; - // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents - // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). - // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash - // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is - // the best we can do as far as we know now. - container.style.position = "absolute"; - if (WebSocket.__isFlashLite()) { - container.style.left = "0px"; - container.style.top = "0px"; - } else { - container.style.left = "-100px"; - container.style.top = "-100px"; - } - var holder = document.createElement("div"); - holder.id = "webSocketFlash"; - container.appendChild(holder); - document.body.appendChild(container); - // See this article for hasPriority: - // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html - swfobject.embedSWF( - WEB_SOCKET_SWF_LOCATION, - "webSocketFlash", - "1" /* width */, - "1" /* height */, - "10.0.0" /* SWF version */, - null, - null, - {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, - null, - function(e) { - if (!e.success) { - console.error("[WebSocket] swfobject.embedSWF failed"); - } - }); - }; - - /** - * Called by Flash to notify JS that it's fully loaded and ready - * for communication. - */ - WebSocket.__onFlashInitialized = function() { - // We need to set a timeout here to avoid round-trip calls - // to flash during the initialization process. - setTimeout(function() { - WebSocket.__flash = document.getElementById("webSocketFlash"); - WebSocket.__flash.setCallerUrl(location.href); - WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); - for (var i = 0; i < WebSocket.__tasks.length; ++i) { - WebSocket.__tasks[i](); - } - WebSocket.__tasks = []; - }, 0); - }; - - /** - * Called by Flash to notify WebSockets events are fired. - */ - WebSocket.__onFlashEvent = function() { - setTimeout(function() { - try { - // Gets events using receiveEvents() instead of getting it from event object - // of Flash event. This is to make sure to keep message order. - // It seems sometimes Flash events don't arrive in the same order as they are sent. - var events = WebSocket.__flash.receiveEvents(); - for (var i = 0; i < events.length; ++i) { - WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); - } - } catch (e) { - console.error(e); - } - }, 0); - return true; - }; - - // Called by Flash. - WebSocket.__log = function(message) { - console.log(decodeURIComponent(message)); - }; - - // Called by Flash. - WebSocket.__error = function(message) { - console.error(decodeURIComponent(message)); - }; - - WebSocket.__addTask = function(task) { - if (WebSocket.__flash) { - task(); - } else { - WebSocket.__tasks.push(task); - } - }; - - /** - * Test if the browser is running flash lite. - * @return {boolean} True if flash lite is running, false otherwise. - */ - WebSocket.__isFlashLite = function() { - if (!window.navigator || !window.navigator.mimeTypes) { - return false; - } - var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; - if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { - return false; - } - return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; - }; - - if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { - if (window.addEventListener) { - window.addEventListener("load", function(){ - WebSocket.__initialize(); - }, false); - } else { - window.attachEvent("onload", function(){ - WebSocket.__initialize(); - }); - } - } - -})(); - -/** - * socket.io - * Copyright(c) 2011 LearnBoost - * MIT Licensed - */ - -(function (exports, io) { - - /** - * Expose constructor. - * - * @api public - */ - exports.XHR = XHR; /** @@ -2944,7 +2554,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho this.sendXHR = this.request('POST'); - if (window.XDomainRequest && this.sendXHR instanceof XDomainRequest) { + if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { this.sendXHR.onload = this.sendXHR.onerror = onload; } else { this.sendXHR.onreadystatechange = stateChange; @@ -2956,7 +2566,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho /** * Disconnects the established `XHR` connection. * - * @returns {Transport} + * @returns {Transport} * @api public */ @@ -2975,8 +2585,10 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho */ XHR.prototype.request = function (method) { - var req = io.util.request(this.socket.isXDomain()); - req.open(method || 'GET', this.prepareUrl() + '?t' + (+ new Date)); + var req = io.util.request(this.socket.isXDomain()) + , query = io.util.query(this.socket.options.query, 't=' + +new Date); + + req.open(method || 'GET', this.prepareUrl() + query, true); if (method == 'POST') { try { @@ -3012,17 +2624,21 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho XHR.check = function (socket, xdomain) { try { - if (io.util.request(xdomain)) { + var request = io.util.request(xdomain), + usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), + socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), + isXProtocol = (socketProtocol != global.location.protocol); + if (request && !(usesXDomReq && isXProtocol)) { return true; } } catch(e) {} return false; }; - + /** - * Check if the XHR transport supports corss domain requests. - * + * Check if the XHR transport supports cross domain requests. + * * @returns {Boolean} * @api public */ @@ -3034,8 +2650,8 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho })( 'undefined' != typeof io ? io.Transport : module.exports , 'undefined' != typeof io ? io : module.parent.exports + , this ); - /** * socket.io * Copyright(c) 2011 LearnBoost @@ -3080,7 +2696,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho HTMLFile.prototype.name = 'htmlfile'; /** - * Creates a new ActiveX `htmlfile` with a forever loading iframe + * Creates a new Ac...eX `htmlfile` with a forever loading iframe * that can be used to listen to messages. Inside the generated * `htmlfile` a reference will be made to the HTMLFile transport. * @@ -3088,7 +2704,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho */ HTMLFile.prototype.get = function () { - this.doc = new ActiveXObject('htmlfile'); + this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); this.doc.open(); this.doc.write(''); this.doc.close(); @@ -3102,9 +2718,10 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho iframeC.appendChild(this.iframe); - this.iframe.src = this.prepareUrl() + '/?t=' + (+ new Date); + var self = this + , query = io.util.query(this.socket.options.query, 't='+ +new Date); - var self = this; + this.iframe.src = this.prepareUrl() + query; io.util.on(window, 'unload', function () { self.destroy(); @@ -3165,16 +2782,16 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho /** * Checks if the browser supports this transport. The browser - * must have an `ActiveXObject` implementation. + * must have an `Ac...eXObject` implementation. * * @return {Boolean} * @api public */ HTMLFile.check = function () { - if ('ActiveXObject' in window){ + if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ try { - var a = new ActiveXObject('htmlfile'); + var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); return a && io.Transport.XHR.check(); } catch(e){} } @@ -3213,7 +2830,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho * MIT Licensed */ -(function (exports, io) { +(function (exports, io, global) { /** * Expose constructor. @@ -3239,6 +2856,12 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho io.util.inherit(XHRPolling, io.Transport.XHR); + /** + * Merge the properties from XHR transport + */ + + io.util.merge(XHRPolling, io.Transport.XHR); + /** * Transport name * @@ -3258,10 +2881,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho XHRPolling.prototype.open = function () { var self = this; - io.util.defer(function () { - io.Transport.XHR.prototype.open.call(self); - }); - + io.Transport.XHR.prototype.open.call(self); return false; }; @@ -3293,14 +2913,20 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho function onload () { this.onload = empty; + this.onerror = empty; self.onData(this.responseText); self.get(); }; + function onerror () { + self.onClose(); + }; + this.xhr = this.request(); - if (window.XDomainRequest && this.xhr instanceof XDomainRequest) { - this.xhr.onload = this.xhr.onerror = onload; + if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { + this.xhr.onload = onload; + this.xhr.onerror = onerror; } else { this.xhr.onreadystatechange = stateChange; } @@ -3318,7 +2944,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho io.Transport.XHR.prototype.onClose.call(this); if (this.xhr) { - this.xhr.onreadystatechange = this.xhr.onload = empty; + this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; try { this.xhr.abort(); } catch(e){} @@ -3326,6 +2952,25 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } }; + /** + * Webkit based browsers show a infinit spinner when you start a XHR request + * before the browsers onload event is called so we need to defer opening of + * the transport until the onload event is called. Wrapping the cb in our + * defer method solve this. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + XHRPolling.prototype.ready = function (socket, fn) { + var self = this; + + io.util.defer(function () { + fn.call(self); + }); + }; + /** * Add the transport to your public io.transports array. * @@ -3337,6 +2982,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho })( 'undefined' != typeof io ? io.Transport : module.exports , 'undefined' != typeof io ? io : module.parent.exports + , this ); /** @@ -3345,7 +2991,17 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho * MIT Licensed */ -(function (exports, io) { +(function (exports, io, global) { + /** + * There is a way to hide the loading indicator in Firefox. If you create and + * remove a iframe it will stop showing the current loading indicator. + * Unfortunately we can't feature detect that and UA sniffing is evil. + * + * @api private + */ + + var indicator = global.document && "MozAppearance" in + global.document.documentElement.style; /** * Expose constructor. @@ -3401,11 +3057,15 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho */ JSONPPolling.prototype.post = function (data) { - var self = this; + var self = this + , query = io.util.query( + this.socket.options.query + , 't='+ (+new Date) + '&i=' + this.index + ); if (!this.form) { - var form = document.createElement('FORM') - , area = document.createElement('TEXTAREA') + var form = document.createElement('form') + , area = document.createElement('textarea') , id = this.iframeId = 'socketio_iframe_' + this.index , iframe; @@ -3415,6 +3075,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho form.style.left = '-1000px'; form.target = id; form.method = 'POST'; + form.setAttribute('accept-charset', 'utf-8'); area.name = 'd'; form.appendChild(area); document.body.appendChild(form); @@ -3423,7 +3084,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho this.area = area; } - this.form.action = this.prepareUrl() + '?t=' + (+new Date) + '&i=' + this.index; + this.form.action = this.prepareUrl() + query; function complete () { initIframe(); @@ -3451,7 +3112,9 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho initIframe(); - this.area.value = data; + // we temporarily stringify until we figure out how to prevent + // browsers from turning `\n` into `\r\n` in form inputs + this.area.value = io.JSON.stringify(data); try { this.form.submit(); @@ -3466,6 +3129,8 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } else { this.iframe.onload = complete; } + + this.socket.setBuffer(true); }; /** @@ -3477,7 +3142,11 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho JSONPPolling.prototype.get = function () { var self = this - , script = document.createElement('SCRIPT'); + , script = document.createElement('script') + , query = io.util.query( + this.socket.options.query + , 't='+ (+new Date) + '&i=' + this.index + ); if (this.script) { this.script.parentNode.removeChild(this.script); @@ -3485,7 +3154,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } script.async = true; - script.src = this.prepareUrl() + '/?t=' + (+new Date) + '&i=' + this.index; + script.src = this.prepareUrl() + query; script.onerror = function () { self.onClose(); }; @@ -3493,6 +3162,14 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho var insertAt = document.getElementsByTagName('script')[0] insertAt.parentNode.insertBefore(script, insertAt); this.script = script; + + if (indicator) { + setTimeout(function () { + var iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + document.body.removeChild(iframe); + }, 100); + } }; /** @@ -3510,6 +3187,23 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho return this; }; + /** + * The indicator hack only works after onload + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + JSONPPolling.prototype.ready = function (socket, fn) { + var self = this; + if (!indicator) return fn.call(this); + + io.util.load(function () { + fn.call(self); + }); + }; + /** * Checks if browser supports this transport. * @@ -3518,7 +3212,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho */ JSONPPolling.check = function () { - return true; + return 'document' in global; }; /** @@ -3543,4 +3237,5 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho })( 'undefined' != typeof io ? io.Transport : module.exports , 'undefined' != typeof io ? io : module.parent.exports + , this ); diff --git a/examples/chat/index.htm b/examples/chat/index.htm index dd37838..c392ece 100755 --- a/examples/chat/index.htm +++ b/examples/chat/index.htm @@ -24,7 +24,7 @@ - + diff --git a/examples/chat/index_alice.htm b/examples/chat/index_alice.htm index ceab6e4..429eb2e 100755 --- a/examples/chat/index_alice.htm +++ b/examples/chat/index_alice.htm @@ -24,7 +24,7 @@ - + - + - + diff --git a/examples/presence/chris.htm b/examples/presence/chris.htm index 3111127..1c34556 100755 --- a/examples/presence/chris.htm +++ b/examples/presence/chris.htm @@ -24,7 +24,7 @@ - + - + - + diff --git a/examples/undergroundempire/player1.htm b/examples/undergroundempire/player1.htm index d2403b1..8dc8538 100755 --- a/examples/undergroundempire/player1.htm +++ b/examples/undergroundempire/player1.htm @@ -25,7 +25,7 @@ - + diff --git a/examples/undergroundempire/player2.htm b/examples/undergroundempire/player2.htm index 0328396..41eca59 100755 --- a/examples/undergroundempire/player2.htm +++ b/examples/undergroundempire/player2.htm @@ -25,7 +25,7 @@ - + diff --git a/examples/wookie/feature/nodewave/socket.io.js b/examples/wookie/feature/nodewave/socket.io.js index 4d30591..49e9b60 100644 --- a/examples/wookie/feature/nodewave/socket.io.js +++ b/examples/wookie/feature/nodewave/socket.io.js @@ -1,1925 +1,3241 @@ -/** Socket.IO 0.6 - Built with build.js */ +/*! Socket.IO.js build:0.9.5, development. Copyright(c) 2011 LearnBoost MIT Licensed */ + /** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed */ -this.io = { - version: '0.6', - - setPath: function(path){ - if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf'); - this.path = /\/$/.test(path) ? path : path + '/'; - WEB_SOCKET_SWF_LOCATION = path + 'lib/vendor/web-socket-js/WebSocketMain.swf'; - } -}; +(function (exports, global) { -if ('jQuery' in this) jQuery.io = this.io; + /** + * IO namespace. + * + * @namespace + */ -if (typeof window != 'undefined'){ - // WEB_SOCKET_SWF_LOCATION = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//cdn.socket.io/' + this.io.version + '/WebSocketMain.swf'; - WEB_SOCKET_SWF_LOCATION = '/socket.io/lib/vendor/web-socket-js/WebSocketMain.swf'; -} -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + var io = exports; -(function(){ + /** + * Socket.IO version + * + * @api public + */ - var _pageLoaded = false; + io.version = '0.9.5'; - io.util = { + /** + * Protocol implemented. + * + * @api public + */ - ios: false, + io.protocol = 1; - load: function(fn){ - if (document.readyState == 'complete' || _pageLoaded) return fn(); - if ('attachEvent' in window){ - window.attachEvent('onload', fn); - } else { - window.addEventListener('load', fn, false); - } - }, + /** + * Available transports, these will be populated with the available transports + * + * @api public + */ - inherit: function(ctor, superCtor){ - // no support for `instanceof` for now - for (var i in superCtor.prototype){ - ctor.prototype[i] = superCtor.prototype[i]; - } - }, + io.transports = []; - indexOf: function(arr, item, from){ - for (var l = arr.length, i = (from < 0) ? Math.max(0, l + from) : from || 0; i < l; i++){ - if (arr[i] === item) return i; - } - return -1; - }, + /** + * Keep track of jsonp callbacks. + * + * @api private + */ - isArray: function(obj){ - return Object.prototype.toString.call(obj) === '[object Array]'; - } + io.j = []; - }; + /** + * Keep track of our io.Sockets + * + * @api private + */ + io.sockets = {}; - io.util.ios = /iphone|ipad/i.test(navigator.userAgent); - io.util.android = /android/i.test(navigator.userAgent); - io.util.opera = /opera/i.test(navigator.userAgent); - io.util.load(function(){ - _pageLoaded = true; - }); + /** + * Manages connections to hosts. + * + * @param {String} uri + * @Param {Boolean} force creation of new socket (defaults to false) + * @api public + */ -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + io.connect = function (host, details) { + var uri = io.util.parseUri(host) + , uuri + , socket; -// abstract - -(function(){ - - var frame = '~m~', - - stringify = function(message){ - if (Object.prototype.toString.call(message) == '[object Object]'){ - if (!('JSON' in window)){ - if ('console' in window && console.error) console.error('Trying to encode as JSON, but JSON.stringify is missing.'); - return '{ "$error": "Invalid message" }'; - } - return '~j~' + JSON.stringify(message); - } else { - return String(message); - } - }; - - Transport = io.Transport = function(base, options){ - this.base = base; - this.options = { - timeout: 15000 // based on heartbeat interval default - }; - for (var i in options) - if (this.options.hasOwnProperty(i)) - this.options[i] = options[i]; - }; - - Transport.prototype.send = function(){ - throw new Error('Missing send() implementation'); - }; - - Transport.prototype.connect = function(){ - throw new Error('Missing connect() implementation'); - }; - - Transport.prototype.disconnect = function(){ - throw new Error('Missing disconnect() implementation'); - }; - - Transport.prototype._encode = function(messages){ - var ret = '', message, - messages = io.util.isArray(messages) ? messages : [messages]; - for (var i = 0, l = messages.length; i < l; i++){ - message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]); - ret += frame + message.length + frame + message; - } - return ret; - }; - - Transport.prototype._decode = function(data){ - var messages = [], number, n; - do { - if (data.substr(0, 3) !== frame) return messages; - data = data.substr(3); - number = '', n = ''; - for (var i = 0, l = data.length; i < l; i++){ - n = Number(data.substr(i, 1)); - if (data.substr(i, 1) == n){ - number += n; - } else { - data = data.substr(number.length + frame.length) - number = Number(number); - break; - } - } - messages.push(data.substr(0, number)); // here - data = data.substr(number); - } while(data !== ''); - return messages; - }; - - Transport.prototype._onData = function(data){ - this._setTimeout(); - var msgs = this._decode(data); - if (msgs && msgs.length){ - for (var i = 0, l = msgs.length; i < l; i++){ - this._onMessage(msgs[i]); - } - } - }; - - Transport.prototype._setTimeout = function(){ - var self = this; - if (this._timeout) clearTimeout(this._timeout); - this._timeout = setTimeout(function(){ - self._onTimeout(); - }, this.options.timeout); - }; - - Transport.prototype._onTimeout = function(){ - this._onDisconnect(); - }; - - Transport.prototype._onMessage = function(message){ - if (!this.sessionid){ - this.sessionid = message; - this._onConnect(); - } else if (message.substr(0, 3) == '~h~'){ - this._onHeartbeat(message.substr(3)); - } else if (message.substr(0, 3) == '~j~'){ - this.base._onMessage(JSON.parse(message.substr(3))); - } else { - this.base._onMessage(message); - } - }, - - Transport.prototype._onHeartbeat = function(heartbeat){ - this.send('~h~' + heartbeat); // echo - }; - - Transport.prototype._onConnect = function(){ - this.connected = true; - this.connecting = false; - this.base._onConnect(); - this._setTimeout(); - }; - - Transport.prototype._onDisconnect = function(){ - this.connecting = false; - this.connected = false; - this.sessionid = null; - this.base._onDisconnect(); - }; - - Transport.prototype._prepareUrl = function(){ - return (this.base.options.secure ? 'https' : 'http') - + '://' + this.base.host - + ':' + this.base.options.port - + '/' + this.base.options.resource - + '/' + this.type - + (this.sessionid ? ('/' + this.sessionid) : '/'); - }; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + if (global && global.location) { + uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); + uri.host = uri.host || (global.document + ? global.document.domain : global.location.hostname); + uri.port = uri.port || global.location.port; + } -(function(){ - - var empty = new Function, - - XMLHttpRequestCORS = (function(){ - if (!('XMLHttpRequest' in window)) return false; - // CORS feature detection - var a = new XMLHttpRequest(); - return a.withCredentials != undefined; - })(), - - request = function(xdomain){ - if ('XDomainRequest' in window && xdomain) return new XDomainRequest(); - if ('XMLHttpRequest' in window && (!xdomain || XMLHttpRequestCORS)) return new XMLHttpRequest(); - if (!xdomain){ - try { - var a = new ActiveXObject('MSXML2.XMLHTTP'); - return a; - } catch(e){} - - try { - var b = new ActiveXObject('Microsoft.XMLHTTP'); - return b; - } catch(e){} - } - return false; - }, - - XHR = io.Transport.XHR = function(){ - io.Transport.apply(this, arguments); - this._sendBuffer = []; - }; - - io.util.inherit(XHR, io.Transport); - - XHR.prototype.connect = function(){ - this._get(); - return this; - }; - - XHR.prototype._checkSend = function(){ - if (!this._posting && this._sendBuffer.length){ - var encoded = this._encode(this._sendBuffer); - this._sendBuffer = []; - this._send(encoded); - } - }; - - XHR.prototype.send = function(data){ - if (io.util.isArray(data)){ - this._sendBuffer.push.apply(this._sendBuffer, data); - } else { - this._sendBuffer.push(data); - } - this._checkSend(); - return this; - }; - - XHR.prototype._send = function(data){ - var self = this; - this._posting = true; - this._sendXhr = this._request('send', 'POST'); - this._sendXhr.onreadystatechange = function(){ - var status; - if (self._sendXhr.readyState == 4){ - self._sendXhr.onreadystatechange = empty; - try { status = self._sendXhr.status; } catch(e){} - self._posting = false; - if (status == 200){ - self._checkSend(); - } else { - self._onDisconnect(); - } - } - }; - this._sendXhr.send('data=' + encodeURIComponent(data)); - }, - - XHR.prototype.disconnect = function(){ - // send disconnection signal - this._onDisconnect(); - return this; - } - - XHR.prototype._onDisconnect = function(){ - if (this._xhr){ - this._xhr.onreadystatechange = this._xhr.onload = empty; - this._xhr.abort(); - this._xhr = null; - } - if (this._sendXhr){ - this._sendXhr.onreadystatechange = this._sendXhr.onload = empty; - this._sendXhr.abort(); - this._sendXhr = null; - } - this._sendBuffer = []; - io.Transport.prototype._onDisconnect.call(this); - }; - - XHR.prototype._request = function(url, method, multipart){ - var req = request(this.base._isXDomain()); - if (multipart) req.multipart = true; - req.open(method || 'GET', this._prepareUrl() + (url ? '/' + url : '')); - if (method == 'POST' && 'setRequestHeader' in req){ - req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8'); - } - return req; - }; - - XHR.check = function(xdomain){ - try { - if (request(xdomain)) return true; - } catch(e){} - return false; - }; - - XHR.xdomainCheck = function(){ - return XHR.check(true); - }; - - XHR.request = request; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + uuri = io.util.uniqueUri(uri); -(function(){ - - var WS = io.Transport.websocket = function(){ - io.Transport.apply(this, arguments); - }; - - io.util.inherit(WS, io.Transport); - - WS.prototype.type = 'websocket'; - - WS.prototype.connect = function(){ - var self = this; - this.socket = new WebSocket(this._prepareUrl()); - this.socket.onmessage = function(ev){ self._onData(ev.data); }; - this.socket.onclose = function(ev){ self._onClose(); }; - return this; - }; - - WS.prototype.send = function(data){ - this.socket.send(this._encode(data)); - return this; - } - - WS.prototype.disconnect = function(){ - this.socket.close(); - return this; - }; - - WS.prototype._onClose = function(){ - this._onDisconnect(); - return this; - }; - - WS.prototype._prepareUrl = function(){ - return (this.base.options.secure ? 'wss' : 'ws') - + '://' + this.base.host - + ':' + this.base.options.port - + '/' + this.base.options.resource - + '/' + this.type - + (this.sessionid ? ('/' + this.sessionid) : ''); - }; - - WS.check = function(){ - // we make sure WebSocket is not confounded with a previously loaded flash WebSocket - return 'WebSocket' in window && WebSocket.prototype && ( WebSocket.prototype.send && !!WebSocket.prototype.send.toString().match(/native/i)) && typeof WebSocket !== "undefined"; - }; - - WS.xdomainCheck = function(){ - return true; - }; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + var options = { + host: uri.host + , secure: 'https' == uri.protocol + , port: uri.port || ('https' == uri.protocol ? 443 : 80) + , query: uri.query || '' + }; -(function(){ - - var Flashsocket = io.Transport.flashsocket = function(){ - io.Transport.websocket.apply(this, arguments); - }; - - io.util.inherit(Flashsocket, io.Transport.websocket); - - Flashsocket.prototype.type = 'flashsocket'; - - Flashsocket.prototype.connect = function(){ - var self = this, args = arguments; - WebSocket.__addTask(function(){ - io.Transport.websocket.prototype.connect.apply(self, args); - }); - return this; - }; - - Flashsocket.prototype.send = function(){ - var self = this, args = arguments; - WebSocket.__addTask(function(){ - io.Transport.websocket.prototype.send.apply(self, args); - }); - return this; - }; - - Flashsocket.prototype._onClose = function(){ - if (!this.base.connected){ - // something failed, we might be behind a proxy, so we'll try another transport - this.base.options.transports.splice(io.util.indexOf(this.base.options.transports, 'flashsocket'), 1); - this.base.transport = this.base.getTransport(); - this.base.connect(); - return; - } - return io.Transport.websocket.prototype._onClose.call(this); - }; - - Flashsocket.check = function(){ - if (typeof WebSocket == 'undefined' || !('__addTask' in WebSocket)) return false; - if (io.util.opera) return false; // opera is buggy with this transport - if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']){ - return !!navigator.plugins['Shockwave Flash'].description; - } - if ('ActiveXObject' in window) { - try { - return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); - } catch (e) {} - } - return false; - }; - - Flashsocket.xdomainCheck = function(){ - return true; - }; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + io.util.merge(options, details); -(function(){ - - var HTMLFile = io.Transport.htmlfile = function(){ - io.Transport.XHR.apply(this, arguments); - }; - - io.util.inherit(HTMLFile, io.Transport.XHR); - - HTMLFile.prototype.type = 'htmlfile'; - - HTMLFile.prototype._get = function(){ - var self = this; - this._open(); - window.attachEvent('onunload', function(){ self._destroy(); }); - }; - - HTMLFile.prototype._open = function(){ - this._doc = new ActiveXObject('htmlfile'); - this._doc.open(); - this._doc.write(''); - this._doc.parentWindow.s = this; - this._doc.close(); - - var _iframeC = this._doc.createElement('div'); - this._doc.body.appendChild(_iframeC); - this._iframe = this._doc.createElement('iframe'); - _iframeC.appendChild(this._iframe); - this._iframe.src = this._prepareUrl() + '/' + (+ new Date); - }; - - HTMLFile.prototype._ = function(data, doc){ - this._onData(data); - var script = doc.getElementsByTagName('script')[0]; - script.parentNode.removeChild(script); - }; - - HTMLFile.prototype._destroy = function(){ - this._iframe.src = 'about:blank'; - this._doc = null; - CollectGarbage(); - }; - - HTMLFile.prototype.disconnect = function(){ - this._destroy(); - return io.Transport.XHR.prototype.disconnect.call(this); - }; - - HTMLFile.check = function(){ - if ('ActiveXObject' in window){ - try { - var a = new ActiveXObject('htmlfile'); - return a && io.Transport.XHR.check(); - } catch(e){} - } - return false; - }; - - HTMLFile.xdomainCheck = function(){ - // we can probably do handling for sub-domains, we should test that it's cross domain but a subdomain here - return false; - }; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + if (options['force new connection'] || !io.sockets[uuri]) { + socket = new io.Socket(options); + } -(function(){ - - var XHRMultipart = io.Transport['xhr-multipart'] = function(){ - io.Transport.XHR.apply(this, arguments); - }; - - io.util.inherit(XHRMultipart, io.Transport.XHR); - - XHRMultipart.prototype.type = 'xhr-multipart'; - - XHRMultipart.prototype._get = function(){ - var self = this; - this._xhr = this._request('', 'GET', true); - this._xhr.onreadystatechange = function(){ - if (self._xhr.readyState == 3) self._onData(self._xhr.responseText); - }; - this._xhr.send(); - }; - - XHRMultipart.check = function(){ - return 'XMLHttpRequest' in window && 'prototype' in XMLHttpRequest && 'multipart' in XMLHttpRequest.prototype; - }; - - XHRMultipart.xdomainCheck = function(){ - return true; - }; - -})(); -/** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost - */ + if (!options['force new connection'] && socket) { + io.sockets[uuri] = socket; + } + + socket = socket || io.sockets[uuri]; -(function(){ - - var empty = new Function(), - - XHRPolling = io.Transport['xhr-polling'] = function(){ - io.Transport.XHR.apply(this, arguments); - }; - - io.util.inherit(XHRPolling, io.Transport.XHR); - - XHRPolling.prototype.type = 'xhr-polling'; - - XHRPolling.prototype.connect = function(){ - if (io.util.ios || io.util.android){ - var self = this; - io.util.load(function(){ - setTimeout(function(){ - io.Transport.XHR.prototype.connect.call(self); - }, 10); - }); - } else { - io.Transport.XHR.prototype.connect.call(this); - } - }; - - XHRPolling.prototype._get = function(){ - var self = this; - this._xhr = this._request(+ new Date, 'GET'); - if ('onload' in this._xhr){ - this._xhr.onload = function(){ - self._onData(this.responseText); - self._get(); - }; - } else { - this._xhr.onreadystatechange = function(){ - var status; - if (self._xhr.readyState == 4){ - self._xhr.onreadystatechange = empty; - try { status = self._xhr.status; } catch(e){} - if (status == 200){ - self._onData(self._xhr.responseText); - self._get(); - } else { - self._onDisconnect(); - } - } - }; - } - this._xhr.send(); - }; - - XHRPolling.check = function(){ - return io.Transport.XHR.check(); - }; - - XHRPolling.xdomainCheck = function(){ - return io.Transport.XHR.xdomainCheck(); - }; - -})(); + // if path is different from '' or / + return socket.of(uri.path.length > 1 ? uri.path : ''); + }; + +})('object' === typeof module ? module.exports : (this.io = {}), this); /** - * Socket.IO client - * - * @author Guillermo Rauch - * @license The MIT license. - * @copyright Copyright (c) 2010 LearnBoost + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed */ -io.JSONP = []; +(function (exports, global) { -JSONPPolling = io.Transport['jsonp-polling'] = function(){ - io.Transport.XHR.apply(this, arguments); - this._insertAt = document.getElementsByTagName('script')[0]; - this._index = io.JSONP.length; - io.JSONP.push(this); -}; + /** + * Utilities namespace. + * + * @namespace + */ -io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); - -JSONPPolling.prototype.type = 'jsonp-polling'; - -JSONPPolling.prototype._send = function(data){ - var self = this; - if (!('_form' in this)){ - var form = document.createElement('FORM'), - area = document.createElement('TEXTAREA'), - id = this._iframeId = 'socket_io_iframe_' + this._index, - iframe; - - form.style.position = 'absolute'; - form.style.top = '-1000px'; - form.style.left = '-1000px'; - form.target = id; - form.method = 'POST'; - form.action = this._prepareUrl() + '/' + (+new Date) + '/' + this._index; - area.name = 'data'; - form.appendChild(area); - this._insertAt.parentNode.insertBefore(form, this._insertAt); - document.body.appendChild(form); - - this._form = form; - this._area = area; - } - - function complete(){ - initIframe(); - self._posting = false; - self._checkSend(); - }; - - function initIframe(){ - if (self._iframe){ - self._form.removeChild(self._iframe); - } - - try { - // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) - iframe = document.createElement('