if(typeof WebSocket === 'undefined') { // if websockets are not supported, then we just create // a dummy one that does nothing, but gives no errors WebSocket = new Class({ initialize: function() {}, send: function() {} }); } var JSONSocket = new Class({ initialize: function(options) { this.options = options || {}; this.options.url = this.options.url || null; this.options.onOpen = this.options.onOpen || function() {}; this.options.onClose = this.options.onClose || function() {}; this.options.autoreconnect = this.options.autoreconnect === false ? false : true; this.options.autoconnect = this.options.autoconnect === false ? false : true; this.options.connectWait = 1; this._stats = {in: 0, out: 0}; if(this.options.autoconnect) { this.initSocket(); } }, initSocket: function() { this.ws = new WebSocket(this.options.url); this.ws.onopen = this.onOpen.bind(this); this.ws.onclose = this.onClose.bind(this); this.ws.onmessage = function(e) { this.onMessage(JSON.parse(e.data)); }.bind(this); }, onOpen: function() { this.options.connectWait = 1; this.options.onOpen(this, this.ws); }, onClose: function() { this.options.onClose(this, this.ws); if(this.options.autoreconnect === true) { this.open.delay(this.options.connectWait * 1000, this); this.options.connectWait *= 2; if(this.options.connectWait > 30) { this.options.connectWait = 30; } } }, onMessage: function(msg) { if(msg.event && typeof msg.event == 'string') { this._stats.in++; (this.eventNameToFunction(msg.event))(msg.data); } }, send: function(eventType, data) { if(this.isConnected()) { var str = JSON.encode({event: eventType, data: data}); this.ws.send(str); this._stats.out++; } }, addEvents: function(events) { Object.each(events, function(fn, eventType) { this.addEvent(eventType, fn); }.bind(this)); return this; }, addEvent: function(eventType, fn) { if(eventType && typeof fn == 'function') { var fnName = 'on_' + eventType; if(!this.options[fnName]) { this.options[fnName] = fn; } else if(typeof this.options[fnName] == 'function') { var currentFn = this.options[fnName]; this.options[fnName] = function() { currentFn.apply(this, arguments); fn.apply(this, arguments); }; } } return this; }, eventNameToFunction: function(eventType) { if(eventType) { var fnName = 'on_' + eventType; return this.options[fnName] || (function() {}); } }, isConnected: function() { WebSocket.OPEN = WebSocket.OPEN || 1; // turns out not all browsers define the state consts return this.ws && this.ws.readyState === WebSocket.OPEN; }, close: function() { this.options.autoreconnect = false; this.ws.close(); }, open: function() { this.initSocket(); }, stats: function() { return this._stats; } }); JSONSocket.websocketUrlForPath = function(path) { var protocol = 'ws://'; if(document.location.protocol.indexOf('https') != -1) { protocol = 'wss://'; } return protocol + document.location.hostname + ':' + (document.location.port || '80') + (path || '/');; };