| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 | 'use strict';var utils = require('../../utils/iframe')  , random = require('../../utils/random')  , browser = require('../../utils/browser')  , urlUtils = require('../../utils/url')  , inherits = require('inherits')  , EventEmitter = require('events').EventEmitter  ;var debug = function() {};if (process.env.NODE_ENV !== 'production') {  debug = require('debug')('sockjs-client:receiver:jsonp');}function JsonpReceiver(url) {  debug(url);  var self = this;  EventEmitter.call(this);  utils.polluteGlobalNamespace();  this.id = 'a' + random.string(6);  var urlWithId = urlUtils.addQuery(url, 'c=' + encodeURIComponent(utils.WPrefix + '.' + this.id));  global[utils.WPrefix][this.id] = this._callback.bind(this);  this._createScript(urlWithId);  // Fallback mostly for Konqueror - stupid timer, 35 seconds shall be plenty.  this.timeoutId = setTimeout(function() {    debug('timeout');    self._abort(new Error('JSONP script loaded abnormally (timeout)'));  }, JsonpReceiver.timeout);}inherits(JsonpReceiver, EventEmitter);JsonpReceiver.prototype.abort = function() {  debug('abort');  if (global[utils.WPrefix][this.id]) {    var err = new Error('JSONP user aborted read');    err.code = 1000;    this._abort(err);  }};JsonpReceiver.timeout = 35000;JsonpReceiver.scriptErrorTimeout = 1000;JsonpReceiver.prototype._callback = function(data) {  debug('_callback', data);  this._cleanup();  if (this.aborting) {    return;  }  if (data) {    debug('message', data);    this.emit('message', data);  }  this.emit('close', null, 'network');  this.removeAllListeners();};JsonpReceiver.prototype._abort = function(err) {  debug('_abort', err);  this._cleanup();  this.aborting = true;  this.emit('close', err.code, err.message);  this.removeAllListeners();};JsonpReceiver.prototype._cleanup = function() {  debug('_cleanup');  clearTimeout(this.timeoutId);  if (this.script2) {    this.script2.parentNode.removeChild(this.script2);    this.script2 = null;  }  if (this.script) {    var script = this.script;    // Unfortunately, you can't really abort script loading of    // the script.    script.parentNode.removeChild(script);    script.onreadystatechange = script.onerror =        script.onload = script.onclick = null;    this.script = null;  }  delete global[utils.WPrefix][this.id];};JsonpReceiver.prototype._scriptError = function() {  debug('_scriptError');  var self = this;  if (this.errorTimer) {    return;  }  this.errorTimer = setTimeout(function() {    if (!self.loadedOkay) {      self._abort(new Error('JSONP script loaded abnormally (onerror)'));    }  }, JsonpReceiver.scriptErrorTimeout);};JsonpReceiver.prototype._createScript = function(url) {  debug('_createScript', url);  var self = this;  var script = this.script = global.document.createElement('script');  var script2;  // Opera synchronous load trick.  script.id = 'a' + random.string(8);  script.src = url;  script.type = 'text/javascript';  script.charset = 'UTF-8';  script.onerror = this._scriptError.bind(this);  script.onload = function() {    debug('onload');    self._abort(new Error('JSONP script loaded abnormally (onload)'));  };  // IE9 fires 'error' event after onreadystatechange or before, in random order.  // Use loadedOkay to determine if actually errored  script.onreadystatechange = function() {    debug('onreadystatechange', script.readyState);    if (/loaded|closed/.test(script.readyState)) {      if (script && script.htmlFor && script.onclick) {        self.loadedOkay = true;        try {          // In IE, actually execute the script.          script.onclick();        } catch (x) {          // intentionally empty        }      }      if (script) {        self._abort(new Error('JSONP script loaded abnormally (onreadystatechange)'));      }    }  };  // IE: event/htmlFor/onclick trick.  // One can't rely on proper order for onreadystatechange. In order to  // make sure, set a 'htmlFor' and 'event' properties, so that  // script code will be installed as 'onclick' handler for the  // script object. Later, onreadystatechange, manually execute this  // code. FF and Chrome doesn't work with 'event' and 'htmlFor'  // set. For reference see:  //   http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html  // Also, read on that about script ordering:  //   http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order  if (typeof script.async === 'undefined' && global.document.attachEvent) {    // According to mozilla docs, in recent browsers script.async defaults    // to 'true', so we may use it to detect a good browser:    // https://developer.mozilla.org/en/HTML/Element/script    if (!browser.isOpera()) {      // Naively assume we're in IE      try {        script.htmlFor = script.id;        script.event = 'onclick';      } catch (x) {        // intentionally empty      }      script.async = true;    } else {      // Opera, second sync script hack      script2 = this.script2 = global.document.createElement('script');      script2.text = "try{var a = document.getElementById('" + script.id + "'); if(a)a.onerror();}catch(x){};";      script.async = script2.async = false;    }  }  if (typeof script.async !== 'undefined') {    script.async = true;  }  var head = global.document.getElementsByTagName('head')[0];  head.insertBefore(script, head.firstChild);  if (script2) {    head.insertBefore(script2, head.firstChild);  }};module.exports = JsonpReceiver;
 |