Lady and gentleman, this is the AvriComet !
It's very very inspired by moo-comet (Benjamin Hutchins) who have done a realy great work !
I have add a new method for comet for Firefox and make some changes.
If you have no idea of what is comet, you can see an exemple of the moo-comet on Benjamin Hutchins' web site
If you want more information about each solution used for each browser, check here : step by step comet tutorial
Download uncompressed
/* * @project AvriComet- Mootools 1.2.1> Request.Queue Class (in the function send), Can be found on www.clientcide.com * @author : Quentin Ambard * @version : 0.1 * @url http://www.avricot.com/blog * @license MIT Style License * it will create 4 differents types of tunnels, using the lasts specifications : it's -i think- the best solution, but the server will have to send all the informations in 4 differents ways... ->Trident : iframeForever (using ActiveXObject) Server : Specific Header : Content-Type: text/html; charset=utf-8 Push : <script type="text/javascript">parent._cometObject.fireEvent('dataPushed', {JSON: "DATA TO PUSH"})</script> You must flush the buffer, sending some noise when you start the connexion, for exemple :"<span></span><span></span><span></span><span></span><span></span>" ->presto : source-event Server : Specific Header : Content-Type: application/x-dom-event-stream Push : Event: "+this.cometName+"\ndata: {JSON: "DATA TO PUSH"}\n\n (cometName is send on each Bayeux Publish request on the field ex ) ->gecko : xhr object using multipart Server : Specific Header : Content-Type: multipart/x-mixed-replace;boundary=XXoXoX Push : Content-Type: text/json\r\n\r\n{JSON: "DATA TO PUSH"}\r\n\r\n--XXoXoX\r\n ->Others : addCallback Server : Specific Header : none Push : {JSON: "DATA TO PUSH"} AvriComet will fire 2 events : - error ('error text') ; - push (Object) When you send data to the server, you can do it throught the method send(whateverYouHaveToSend). It will catch the answer and fire the event : - answer (theAnswer) */ var AvriComet = new Class ({ Implements: [Options, Events], options: { cometName: "MooComet", //name of the comet connexion, essential for presto engine. urlServer: '' //Url of the server. }, length: 0, initialize: function(_options){ this.setOptions(_options); this.sender = new Request.Queue({ concurrent : 2 }); this.cometType = (Browser.Engine.trident ? 3 : (Browser.Engine.presto ? 2 : (Browser.Engine.gecko ? 1 : 0))); //ie if (this.cometType == 3) { this.tunnel = new ActiveXObject("htmlfile"); } //presto (opera) else if (this.cometType == 2){ this.tunnel = document.createElement("event-source"); } //gecko (firefox) else if (this.cometType == 1){ this.tunnel = new Request({ url: this.options.urlServer, method: 'get', header: '' }); this.tunnel.xhr.multipart = true ; this.tunnel.xhr.onload = function (event) { if (event.target.readyState == 4 ) { this.decodeJSON(event.target.responseText) ; //this.fireEvent('dataPushed', [event.target.responseText, event.target.responseXML]) ; } else { this.fireEvent('error', "Error xhr : readyState: "+event.target.readyState) ; } }.bind(this.tunnel); } //webkit (chrome/safari) else if (this.cometType == 0){ this.tunnel = new Request.XHR(this.options); this.tunnel.addCallback(this.onChange.bind(this), {readyState: false, status: false}); } return this; }, /** * Have to be invoked when you want to close the connection */ cancel: function() { if (this.cometType == 3) { this.tunnel.body.innerHTML="<iframe src='about:blank'></iframe>"; } //default iframe forever for all broswer or just for browser != than presto && gecko else if (this.cometType == 2) { document.body.removeChild(this.tunnel); } else { this.tunnel.cancel(); } return this; }, /** * Create the tunnel with the server. * 3 differents type of tunnel, depends of the browser's engine: * trident => iframe with ActiveXObject object * presto => event-source (html5) * firefox => XMLHttpRequest with multipart * post the information with : src="'+this.options.urlServer+_post'" **/ initTunnel: function(_post) { //Trident Engine (ie) if (this.cometType == 3) { this.tunnel.open(); this.tunnel.write("<html><body></body></html>"); this.tunnel.close(); this.tunnel.parentWindow._cometObject = this; this.tunnel.parentWindow._cometObject.addEvent('dataPushed', this.firePush.bindWithEvent(this)); this.tunnel.body.innerHTML = "<iframe id='test' src='"+this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post+"'></iframe>"; } //Presto Engine (opera) else if (this.cometType == 2) { this.tunnel.addEventListener(this.options.cometName, this.decodeJSON.bindWithEvent(this), false); this.tunnel.setAttribute("src", this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post); } //gecko Engine (Firefox) else //if (this.cometType == 1) { //this.tunnel.addEvent('dataPushed', this.decodeJSON.bindWithEvent(this)); this.tunnel.send('cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post); } //safari+chrome /*else { this.tunnel.send({data: {'cometType': this.cometType, 'cometName': this.options.cometName}}); }*/ }, send: function (_post) { var req = new Request.JSON({url: this.options.urlServer, link: 'cancel', method: 'get'}); req.addEvent('complete', this.answerSender.bindWithEvent(this)); this.sender.addRequest('sender', req); req.send(_post); }, decodeJSON: function (_textResponse) { if(this.cometType == 2) { _textResponse = _textResponse.data ; } else if (_textResponse != "") { var newLength = text.length ; if (newLength>this.length) {//fix firefox behaviour when push 2 information in the same time. _textResponse = text.substring(this.length,newLength-1) ; /* possible implementation for FF answer (be carreful with the final "," : {JSON: DATA TO PUSH}, * * text = "["+text.substring(this.length,newLength-1)+"]" ; //So you have [{objectData1},{objectData2},{objectData3}] var listAction = JSON.decode(text); //you get the array with all the json object from the server var size = listAction.length ; for(i=0;i<size;i++) { this.firePush(listAction[i]); } this.length = newLength ; */ } } this.firePush(_textResponse); }, firePush: function (_object) { try { if (typeof _object != "object") { _object = JSON.decode(_object); } } catch (e) { this.fireEvent('error', 'transmission error or server error. Sometimes the last bracket has a little bug : '+_object); } this.fireEvent('push', JSON.decode(_object)); }, answerSender: function (_answer) { if (typeof(_answer) != "undefined") { this.fireEvent('answer', _answer); } } }) ; Request.XHR = new Class({ Extends: Request, callbacks: [], check: function() { return true; /*if (!this.timer) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.start.bind(this, arguments)); return false; } return false;*/ }, addCallback: function(fn, options){ options = $extend({ fn: $empty, readyState: 4, status: 200 }, options); this.callbacks.push($extend(options, {fn: fn})); return this; }, onStateChange: function(){ //if (!this.running) return; this.status = 0; $try(function(){ this.status = this.xhr.status; }, this); this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML}; this.callbacks.forEach(function(callback) { if (callback.readyState != false && callback.readyState != this.xhr.readyState) return; if (callback.status != false && callback.status != this.status) return; callback.fn(this.processScripts(this.response.text), this.response.xml); }, this); } });
Coming soon :)
This isn't the final version....