// SimpleComet v1.0 by Mandor <mandor@mandor.net>
// For more info, see http://www.mandor.net/2008/12/23/12

var comet = {

	// Determines the proper technique based on browser type.
	technique: /(Firefox|Safari)/i.test(navigator.userAgent) ? 1 : /MSIE/i.test(navigator.userAgent) ? 2 : 3,

	// Binds the proper transport to a specified stream.
	open: function(URL, push, disconnected) {
		this.push = push || new Function; this.disconnected = disconnected || new Function;
		this.URL = URL; this.active = true;
		switch (this.technique) {
			case 1: // We're doing XHR streaming!
				if (!this.transport) { this.transport = new XMLHttpRequest(); }
				this.eventID = 0;
				this.transport.open('GET', this.URL, true);
				this.transport.onreadystatechange = this.scope(this.XHRcallback, this);
				this.transport.send(null);
				break;
			case 2: // We're using a forever frame with that fancy IE hack.
				if (!this.transport) { this.transport = new ActiveXObject('htmlfile'); }
				this.transport.open();
				this.transport.write('<html><body></body></html>');
				this.transport.close();
				this.transport.parentWindow.push = this.push;
				this.transport.body.innerHTML = '<iframe id="SimpleComet" src="'+this.URL+'"></iframe>';
				setTimeout(this.scope(this.checkFrame, this), 500);
				break;
			case 3: // Falling back on using a standard forever frame.
				if (!this.transport) { this.transport = document.createElement('iframe'); }
				this.transport.style.display = 'none';
				this.transport.src = this.URL;
				this.transport.onload = this.scope(this.close, this);
				document.body.appendChild(this.transport);
				break;
		}
	},

	// Unbinds the transport.
	close: function() {
		if (!this.active) { return; } else { this.active = false; }
		switch (this.technique) {
			case 1: if (this.transport.readyState != 4) { this.transport.abort(); } break;
			case 2: this.transport.body.removeChild(this.transport.getElementById('SimpleComet')); break;
			case 3: this.transport.src = 'about:blank'; document.body.removeChild(this.transport); break;
		}
		this.disconnected();
	},

	// If the transport is incremental XHR (technique 1), we need to fetch _NEW_ events from the stream.
	XHRcallback: function() {
		if (this.transport.readyState == 4) { this.close(); return; }
		var events = this.transport.responseText.match(/<comet>(.+?)<\/comet>/g);
		while (events && events[this.eventID]) {
			this.push(events[this.eventID].substring(7, events[this.eventID].length-8));
			this.eventID++; 
		}
	},

	// If the transport is an IE forever frame (technique 2), we need to check periodically if it's still alive.
	checkFrame: function() {
		if (!this.transport.getElementById('SimpleComet')) { return; }
		if (this.transport.getElementById('SimpleComet').readyState == 'complete') { this.close(); return; }
		setTimeout(this.scope(this.checkFrame, this), 500);
	},

	// Used to change the scope of callback functions.
	scope: function(f, scope) { return function() { return f.apply(scope); } }

}
