twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

polling-jsonp.js (4896B)


      1 /**
      2  * Module requirements.
      3  */
      4 
      5 var Polling = require('./polling');
      6 var inherit = require('component-inherit');
      7 var globalThis = require('../globalThis');
      8 
      9 /**
     10  * Module exports.
     11  */
     12 
     13 module.exports = JSONPPolling;
     14 
     15 /**
     16  * Cached regular expressions.
     17  */
     18 
     19 var rNewline = /\n/g;
     20 var rEscapedNewline = /\\n/g;
     21 
     22 /**
     23  * Global JSONP callbacks.
     24  */
     25 
     26 var callbacks;
     27 
     28 /**
     29  * Noop.
     30  */
     31 
     32 function empty () { }
     33 
     34 /**
     35  * JSONP Polling constructor.
     36  *
     37  * @param {Object} opts.
     38  * @api public
     39  */
     40 
     41 function JSONPPolling (opts) {
     42   Polling.call(this, opts);
     43 
     44   this.query = this.query || {};
     45 
     46   // define global callbacks array if not present
     47   // we do this here (lazily) to avoid unneeded global pollution
     48   if (!callbacks) {
     49     // we need to consider multiple engines in the same page
     50     callbacks = globalThis.___eio = (globalThis.___eio || []);
     51   }
     52 
     53   // callback identifier
     54   this.index = callbacks.length;
     55 
     56   // add callback to jsonp global
     57   var self = this;
     58   callbacks.push(function (msg) {
     59     self.onData(msg);
     60   });
     61 
     62   // append to query string
     63   this.query.j = this.index;
     64 
     65   // prevent spurious errors from being emitted when the window is unloaded
     66   if (typeof addEventListener === 'function') {
     67     addEventListener('beforeunload', function () {
     68       if (self.script) self.script.onerror = empty;
     69     }, false);
     70   }
     71 }
     72 
     73 /**
     74  * Inherits from Polling.
     75  */
     76 
     77 inherit(JSONPPolling, Polling);
     78 
     79 /*
     80  * JSONP only supports binary as base64 encoded strings
     81  */
     82 
     83 JSONPPolling.prototype.supportsBinary = false;
     84 
     85 /**
     86  * Closes the socket.
     87  *
     88  * @api private
     89  */
     90 
     91 JSONPPolling.prototype.doClose = function () {
     92   if (this.script) {
     93     this.script.parentNode.removeChild(this.script);
     94     this.script = null;
     95   }
     96 
     97   if (this.form) {
     98     this.form.parentNode.removeChild(this.form);
     99     this.form = null;
    100     this.iframe = null;
    101   }
    102 
    103   Polling.prototype.doClose.call(this);
    104 };
    105 
    106 /**
    107  * Starts a poll cycle.
    108  *
    109  * @api private
    110  */
    111 
    112 JSONPPolling.prototype.doPoll = function () {
    113   var self = this;
    114   var script = document.createElement('script');
    115 
    116   if (this.script) {
    117     this.script.parentNode.removeChild(this.script);
    118     this.script = null;
    119   }
    120 
    121   script.async = true;
    122   script.src = this.uri();
    123   script.onerror = function (e) {
    124     self.onError('jsonp poll error', e);
    125   };
    126 
    127   var insertAt = document.getElementsByTagName('script')[0];
    128   if (insertAt) {
    129     insertAt.parentNode.insertBefore(script, insertAt);
    130   } else {
    131     (document.head || document.body).appendChild(script);
    132   }
    133   this.script = script;
    134 
    135   var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
    136 
    137   if (isUAgecko) {
    138     setTimeout(function () {
    139       var iframe = document.createElement('iframe');
    140       document.body.appendChild(iframe);
    141       document.body.removeChild(iframe);
    142     }, 100);
    143   }
    144 };
    145 
    146 /**
    147  * Writes with a hidden iframe.
    148  *
    149  * @param {String} data to send
    150  * @param {Function} called upon flush.
    151  * @api private
    152  */
    153 
    154 JSONPPolling.prototype.doWrite = function (data, fn) {
    155   var self = this;
    156 
    157   if (!this.form) {
    158     var form = document.createElement('form');
    159     var area = document.createElement('textarea');
    160     var id = this.iframeId = 'eio_iframe_' + this.index;
    161     var iframe;
    162 
    163     form.className = 'socketio';
    164     form.style.position = 'absolute';
    165     form.style.top = '-1000px';
    166     form.style.left = '-1000px';
    167     form.target = id;
    168     form.method = 'POST';
    169     form.setAttribute('accept-charset', 'utf-8');
    170     area.name = 'd';
    171     form.appendChild(area);
    172     document.body.appendChild(form);
    173 
    174     this.form = form;
    175     this.area = area;
    176   }
    177 
    178   this.form.action = this.uri();
    179 
    180   function complete () {
    181     initIframe();
    182     fn();
    183   }
    184 
    185   function initIframe () {
    186     if (self.iframe) {
    187       try {
    188         self.form.removeChild(self.iframe);
    189       } catch (e) {
    190         self.onError('jsonp polling iframe removal error', e);
    191       }
    192     }
    193 
    194     try {
    195       // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
    196       var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
    197       iframe = document.createElement(html);
    198     } catch (e) {
    199       iframe = document.createElement('iframe');
    200       iframe.name = self.iframeId;
    201       iframe.src = 'javascript:0';
    202     }
    203 
    204     iframe.id = self.iframeId;
    205 
    206     self.form.appendChild(iframe);
    207     self.iframe = iframe;
    208   }
    209 
    210   initIframe();
    211 
    212   // escape \n to prevent it from being converted into \r\n by some UAs
    213   // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
    214   data = data.replace(rEscapedNewline, '\\\n');
    215   this.area.value = data.replace(rNewline, '\\n');
    216 
    217   try {
    218     this.form.submit();
    219   } catch (e) {}
    220 
    221   if (this.iframe.attachEvent) {
    222     this.iframe.onreadystatechange = function () {
    223       if (self.iframe.readyState === 'complete') {
    224         complete();
    225       }
    226     };
    227   } else {
    228     this.iframe.onload = complete;
    229   }
    230 };