/** * @license Comet++ * Copyright 2010 Peter Petrov, ChessBomb.com * All Rights Reserved */ var CometPP = { settings: { url: location.protocol + "//" + location.host + "/cometpp/", timeoutComet: 70, timeoutDirect: 20 }, ifrBridge: null, cppBridge: null, queue: [], queueExec: [], init: function() { var ifr; if (this.ifrBridge) return; ifr = document.createElement("iframe"); ifr.style.display = 'block'; ifr.style.width = '0'; ifr.style.height = '0'; ifr.style.border = '0'; ifr.style.margin = '0'; ifr.style.padding = '0'; ifr.style.overflow = 'hidden'; ifr.style.visibility = 'hidden'; ifr.src = this.settings.url + "static/ifr.bridge.html"; document.body.appendChild(ifr); this.ifrBridge = ifr; }, bridgeReady: function(bridge) { this.cppBridge = bridge; this.processQueue(); this.processQueueExec(); }, processQueue: function() { while (this.queue.length > 0) { this.cppBridge.ajax(this.queue.shift()); } }, processQueueExec: function() { var self = this; if (self.queueExec.length <= 0 || !self.cppBridge) return; setTimeout(function() { while (self.queueExec.length > 0) { self.queueExec.shift()(); } }, 1); }, extend: function(o, v) { var key; for (key in v) if (v.hasOwnProperty(key)) { o[key] = v[key]; } return o; }, log: function() { if (true) return; if (typeof(console) != "undefined" && console.log) console.log.apply(console, arguments); }, ajax: function(opts) { var o = this.extend({ type: "GET", url: null, data: null, async: true, timeout: null, success: null, error: null }, opts); if (!this.cppBridge) { this.queue.push(o); return null; } return this.cppBridge.ajax(o); }, TCPSocket: function() { var self = this, cpp = CometPP, url = cpp.settings.url, closing = false, sid = "", sendSeq = 0, rcvSeq = 0, sendBufs = [], rcvBufs = {}, xhrDirect = null, xhrComets = [], fireDirect, fireComet, cbHandshake, cbComet, cbError; // Callbacks: self.onopen = function() {}; self.onread = function() {}; self.onclose = function() {}; // Public methods: self.open = function(_hostname, _port) { cpp.queueExec.push(function() { xhrDirect = fireDirect({ url: url + "handshake/", success: cbHandshake }); }); cpp.processQueueExec(); }; self.send = function(data) { cpp.log("CometPP.TCPSocket.send: ", data); if (xhrComets.length >= 2) { sendBufs.push(data); return; } xhrComets.push(fireComet({ data: data })); }; self.close = function() { closing = true; if (xhrDirect) { xhrDirect.abort(); xhrDirect = null; } while (xhrComets.length > 0) { xhrComets.shift().abort(); } if (sid) cpp.ajax({ type: "POST", url: url + "close/" + sid + "/", async: false, timeout: 1 * 1000 }); sid = ""; sendSeq = 0; rcvSeq = 0; sendBufs = []; rcvBufs = {}; self.onclose(); }; // Private stuff: fireDirect = function(o) { return cpp.ajax(cpp.extend({ type: "POST", timeout: cpp.settings.timeoutDirect * 1000, error: cbError }, o)); }; fireComet = function(o) { var seq = sendSeq++; return cpp.ajax(cpp.extend({ type: "POST", url: url + "comet/" + sid + "/" + seq + "/", timeout: cpp.settings.timeoutComet * 1000, success: cbComet, error: cbError, seq: seq }, o)); }; cbHandshake = function(data, textStatus, xhr) { sid = data; xhrDirect = null; xhrComets.push(fireComet({})); self.onopen(); }; cbComet = function(data, textStatus, xhr) { var i, buf = "", sendData = null; for (i = 0; i < xhrComets.length; ++i) if (xhrComets[i] === xhr) { xhrComets.splice(i, 1); break; } if (xhrComets.length < 2) { if (sendBufs.length > 0) { sendData = sendBufs.join(""); sendBufs = []; } if (xhrComets.length < 1 || sendData) { xhrComets.push(fireComet({ data: sendData })); } } rcvBufs[this.seq] = (data || ""); while (rcvSeq in rcvBufs) { buf += rcvBufs[rcvSeq]; delete rcvBufs[rcvSeq]; ++rcvSeq; } if (buf.length > 0) { cpp.log("CometPP.TCPSocket.onread: ", buf); self.onread(buf); } }; cbError = function(xhr, textStatus, errorThrown) { if (!closing) self.close(); }; } };