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 
  1. /*
  2. * @project AvriComet- Mootools 1.2.1> Request.Queue Class (in the function send), Can be found on www.clientcide.com
  3. * @author : Quentin Ambard
  4. * @version : 0.1
  5. * @url http://www.avricot.com/blog
  6. * @license MIT Style License
  7. *
  8.  
  9. it will create 4 differents types of tunnels, using the lasts specifications :
  10. it's -i think- the best solution, but the server will have to send all the informations in 4 differents ways...
  11. ->Trident : iframeForever (using ActiveXObject)
  12. Server :
  13. Specific Header :
  14. Content-Type: text/html; charset=utf-8
  15. Push :
  16. <script type="text/javascript">parent._cometObject.fireEvent('dataPushed', {JSON: "DATA TO PUSH"})</script>
  17. 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>"
  18. ->presto : source-event
  19. Server :
  20. Specific Header :
  21. Content-Type: application/x-dom-event-stream
  22. Push :
  23. Event: "+this.cometName+"\ndata: {JSON: "DATA TO PUSH"}\n\n (cometName is send on each Bayeux Publish request on the field ex )
  24. ->gecko : xhr object using multipart
  25. Server :
  26. Specific Header :
  27. Content-Type: multipart/x-mixed-replace;boundary=XXoXoX
  28. Push :
  29. Content-Type: text/json\r\n\r\n{JSON: "DATA TO PUSH"}\r\n\r\n--XXoXoX\r\n
  30.  
  31. ->Others : addCallback
  32. Server :
  33. Specific Header :
  34. none
  35. Push :
  36. {JSON: "DATA TO PUSH"}
  37.  
  38.  
  39.  
  40. AvriComet will fire 2 events :
  41. - error ('error text') ;
  42. - push (Object)
  43. When you send data to the server, you can do it throught the method send(whateverYouHaveToSend).
  44. It will catch the answer and fire the event :
  45. - answer (theAnswer)
  46.  
  47. */
  48. var AvriComet = new Class ({
  49. Implements: [Options, Events],
  50. options: {
  51. cometName: "MooComet", //name of the comet connexion, essential for presto engine.
  52. urlServer: '' //Url of the server.
  53. },
  54. length: 0,
  55. initialize: function(_options){
  56. this.setOptions(_options);
  57.  
  58. this.sender = new Request.Queue({
  59. concurrent : 2
  60. });
  61.  
  62.  
  63. this.cometType = (Browser.Engine.trident ? 3 : (Browser.Engine.presto ? 2 : (Browser.Engine.gecko ? 1 : 0)));
  64.  
  65. //ie
  66. if (this.cometType == 3) {
  67. this.tunnel = new ActiveXObject("htmlfile");
  68. }
  69. //presto (opera)
  70. else if (this.cometType == 2){
  71. this.tunnel = document.createElement("event-source");
  72. }
  73. //gecko (firefox)
  74. else if (this.cometType == 1){
  75. this.tunnel = new Request({
  76. url: this.options.urlServer,
  77. method: 'get',
  78. header: ''
  79. });
  80. this.tunnel.xhr.multipart = true ;
  81. this.tunnel.xhr.onload = function (event) {
  82. if (event.target.readyState == 4 ) {
  83. this.decodeJSON(event.target.responseText) ;
  84. //this.fireEvent('dataPushed', [event.target.responseText, event.target.responseXML]) ;
  85. }
  86. else {
  87. this.fireEvent('error', "Error xhr : readyState: "+event.target.readyState) ;
  88. }
  89. }.bind(this.tunnel);
  90. }
  91. //webkit (chrome/safari)
  92. else if (this.cometType == 0){
  93. this.tunnel = new Request.XHR(this.options);
  94. this.tunnel.addCallback(this.onChange.bind(this), {readyState: false, status: false});
  95. }
  96. return this;
  97. },
  98. /**
  99. * Have to be invoked when you want to close the connection
  100. */
  101. cancel: function() {
  102. if (this.cometType == 3) {
  103. this.tunnel.body.innerHTML="<iframe src='about:blank'></iframe>";
  104. }
  105. //default iframe forever for all broswer or just for browser != than presto && gecko
  106. else if (this.cometType == 2) {
  107. document.body.removeChild(this.tunnel);
  108. }
  109. else {
  110. this.tunnel.cancel();
  111. }
  112. return this;
  113. },
  114. /**
  115. * Create the tunnel with the server.
  116. * 3 differents type of tunnel, depends of the browser's engine:
  117. * trident => iframe with ActiveXObject object
  118. * presto => event-source (html5)
  119. * firefox => XMLHttpRequest with multipart
  120. * post the information with : src="'+this.options.urlServer+_post'"
  121. **/
  122. initTunnel: function(_post) {
  123. //Trident Engine (ie)
  124. if (this.cometType == 3) {
  125. this.tunnel.open();
  126. this.tunnel.write("<html><body></body></html>");
  127. this.tunnel.close();
  128. this.tunnel.parentWindow._cometObject = this;
  129. this.tunnel.parentWindow._cometObject.addEvent('dataPushed', this.firePush.bindWithEvent(this));
  130. this.tunnel.body.innerHTML = "<iframe id='test' src='"+this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post+"'></iframe>";
  131. }
  132. //Presto Engine (opera)
  133. else if (this.cometType == 2) {
  134. this.tunnel.addEventListener(this.options.cometName, this.decodeJSON.bindWithEvent(this), false);
  135. this.tunnel.setAttribute("src", this.options.urlServer+'?cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post);
  136. }
  137. //gecko Engine (Firefox)
  138. else //if (this.cometType == 1)
  139. {
  140. //this.tunnel.addEvent('dataPushed', this.decodeJSON.bindWithEvent(this));
  141. this.tunnel.send('cometType='+this.cometType+'&cometName='+this.options.cometName+'&'+_post);
  142. }
  143. //safari+chrome
  144. /*else {
  145. this.tunnel.send({data: {'cometType': this.cometType, 'cometName': this.options.cometName}});
  146. }*/
  147. },
  148. send: function (_post) {
  149. var req = new Request.JSON({url: this.options.urlServer, link: 'cancel', method: 'get'});
  150. req.addEvent('complete', this.answerSender.bindWithEvent(this));
  151. this.sender.addRequest('sender', req);
  152. req.send(_post);
  153. },
  154. decodeJSON: function (_textResponse) {
  155. if(this.cometType == 2) {
  156. _textResponse = _textResponse.data ;
  157. }
  158. else if (_textResponse != "") {
  159. var newLength = text.length ;
  160. if (newLength>this.length) {//fix firefox behaviour when push 2 information in the same time.
  161. _textResponse = text.substring(this.length,newLength-1) ;
  162. /* possible implementation for FF answer (be carreful with the final "," : {JSON: DATA TO PUSH},
  163. *
  164. * text = "["+text.substring(this.length,newLength-1)+"]" ; //So you have [{objectData1},{objectData2},{objectData3}]
  165. var listAction = JSON.decode(text); //you get the array with all the json object from the server
  166. var size = listAction.length ;
  167. for(i=0;i<size;i++) {
  168. this.firePush(listAction[i]);
  169. }
  170. this.length = newLength ;
  171.  
  172. */
  173. }
  174. }
  175. this.firePush(_textResponse);
  176. },
  177. firePush: function (_object) {
  178. try {
  179. if (typeof _object != "object") {
  180. _object = JSON.decode(_object);
  181. }
  182. }
  183. catch (e) {
  184. this.fireEvent('error', 'transmission error or server error. Sometimes the last bracket has a little bug : '+_object);
  185. }
  186. this.fireEvent('push', JSON.decode(_object));
  187. },
  188. answerSender: function (_answer) {
  189. if (typeof(_answer) != "undefined") {
  190. this.fireEvent('answer', _answer);
  191. }
  192. }
  193.  
  194. }) ;
  195.  
  196. Request.XHR = new Class({
  197. Extends: Request,
  198. callbacks: [],
  199. check: function() {
  200. return true;
  201. /*if (!this.timer) return true;
  202. switch (this.options.link){
  203. case 'cancel': this.cancel(); return true;
  204. case 'chain': this.chain(this.start.bind(this, arguments)); return false;
  205. }
  206. return false;*/
  207. },
  208. addCallback: function(fn, options){
  209. options = $extend({
  210. fn: $empty,
  211. readyState: 4,
  212. status: 200
  213. }, options);
  214. this.callbacks.push($extend(options, {fn: fn}));
  215. return this;
  216. },
  217. onStateChange: function(){
  218. //if (!this.running) return;
  219. this.status = 0;
  220. $try(function(){
  221. this.status = this.xhr.status;
  222. }, this);
  223. this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
  224. this.callbacks.forEach(function(callback) {
  225. if (callback.readyState != false && callback.readyState != this.xhr.readyState)
  226. return;
  227. if (callback.status != false && callback.status != this.status)
  228. return;
  229. callback.fn(this.processScripts(this.response.text), this.response.xml);
  230. }, this);
  231. }
  232. });


Coming soon :)
This isn't the final version....