/** Socket.IO 0.6 - Built with build.js */ /** * Socket.IO client * * @author Guillermo Rauch * @license The MIT license. * @copyright Copyright (c) 2010 LearnBoost */ this.io = { version: '0.6', setPath: function(path){ this.path = /\/$/.test(path) ? path : path + '/'; // this is temporary until we get a fix for injecting Flash WebSocket javascript files dynamically, // as io.js shouldn't be aware of specific transports. WEB_SOCKET_SWF_LOCATION = path + 'lib/vendor/web-socket-js/WebSocketMain.swf'; } }; if ('jQuery' in this) jQuery.io = this.io; if (typeof window != 'undefined') this.io.setPath('/socket.io/'); /** * Socket.IO client * * @author Guillermo Rauch * @license The MIT license. * @copyright Copyright (c) 2010 LearnBoost */ (function(){ var _pageLoaded = false; io.util = { ios: false, load: function(fn){ if (document.readyState == 'complete' || _pageLoaded) return fn(); if ('attachEvent' in window){ window.attachEvent('onload', fn); } else { window.addEventListener('load', fn, false); } }, inherit: function(ctor, superCtor){ // no support for `instanceof` for now for (var i in superCtor.prototype){ ctor.prototype[i] = superCtor.prototype[i]; } }, 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; }, isArray: function(obj){ return Object.prototype.toString.call(obj) === '[object Array]'; } }; 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; }); })(); /** * Socket.IO client * * @author Guillermo Rauch * @license The MIT license. * @copyright Copyright (c) 2010 LearnBoost */ // 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 */ (function(){ var empty = new Function, request = function(xdomain){ if ('XDomainRequest' in window && xdomain) return new XDomainRequest(); if ('XMLHttpRequest' in window) 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 */ (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 */ (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 (!('path' in io)) throw new Error('The `flashsocket` transport requires that you call io.setPath() with the path to the socket.io client dir.'); 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 */ (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 */ (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 && 'multipart' in XMLHttpRequest.prototype; }; XHRMultipart.xdomainCheck = function(){ return true; }; })(); /** * Socket.IO client * * @author Guillermo Rauch * @license The MIT license. * @copyright Copyright (c) 2010 LearnBoost */ (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(); }; })(); /** * Socket.IO client * * @author Guillermo Rauch * @license The MIT license. * @copyright Copyright (c) 2010 LearnBoost */ io.JSONP = []; 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); }; 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('