123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /**
- * Module requirements.
- */
- var Polling = require('./polling');
- var inherit = require('component-inherit');
- var globalThis = require('../globalThis');
- /**
- * Module exports.
- */
- module.exports = JSONPPolling;
- /**
- * Cached regular expressions.
- */
- var rNewline = /\n/g;
- var rEscapedNewline = /\\n/g;
- /**
- * Global JSONP callbacks.
- */
- var callbacks;
- /**
- * Noop.
- */
- function empty () { }
- /**
- * JSONP Polling constructor.
- *
- * @param {Object} opts.
- * @api public
- */
- function JSONPPolling (opts) {
- Polling.call(this, opts);
- this.query = this.query || {};
- // define global callbacks array if not present
- // we do this here (lazily) to avoid unneeded global pollution
- if (!callbacks) {
- // we need to consider multiple engines in the same page
- callbacks = globalThis.___eio = (globalThis.___eio || []);
- }
- // callback identifier
- this.index = callbacks.length;
- // add callback to jsonp global
- var self = this;
- callbacks.push(function (msg) {
- self.onData(msg);
- });
- // append to query string
- this.query.j = this.index;
- // prevent spurious errors from being emitted when the window is unloaded
- if (typeof addEventListener === 'function') {
- addEventListener('beforeunload', function () {
- if (self.script) self.script.onerror = empty;
- }, false);
- }
- }
- /**
- * Inherits from Polling.
- */
- inherit(JSONPPolling, Polling);
- /*
- * JSONP only supports binary as base64 encoded strings
- */
- JSONPPolling.prototype.supportsBinary = false;
- /**
- * Closes the socket.
- *
- * @api private
- */
- JSONPPolling.prototype.doClose = function () {
- if (this.script) {
- this.script.parentNode.removeChild(this.script);
- this.script = null;
- }
- if (this.form) {
- this.form.parentNode.removeChild(this.form);
- this.form = null;
- this.iframe = null;
- }
- Polling.prototype.doClose.call(this);
- };
- /**
- * Starts a poll cycle.
- *
- * @api private
- */
- JSONPPolling.prototype.doPoll = function () {
- var self = this;
- var script = document.createElement('script');
- if (this.script) {
- this.script.parentNode.removeChild(this.script);
- this.script = null;
- }
- script.async = true;
- script.src = this.uri();
- script.onerror = function (e) {
- self.onError('jsonp poll error', e);
- };
- var insertAt = document.getElementsByTagName('script')[0];
- if (insertAt) {
- insertAt.parentNode.insertBefore(script, insertAt);
- } else {
- (document.head || document.body).appendChild(script);
- }
- this.script = script;
- var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
- if (isUAgecko) {
- setTimeout(function () {
- var iframe = document.createElement('iframe');
- document.body.appendChild(iframe);
- document.body.removeChild(iframe);
- }, 100);
- }
- };
- /**
- * Writes with a hidden iframe.
- *
- * @param {String} data to send
- * @param {Function} called upon flush.
- * @api private
- */
- JSONPPolling.prototype.doWrite = function (data, fn) {
- var self = this;
- if (!this.form) {
- var form = document.createElement('form');
- var area = document.createElement('textarea');
- var id = this.iframeId = 'eio_iframe_' + this.index;
- var iframe;
- form.className = 'socketio';
- form.style.position = 'absolute';
- form.style.top = '-1000px';
- form.style.left = '-1000px';
- form.target = id;
- form.method = 'POST';
- form.setAttribute('accept-charset', 'utf-8');
- area.name = 'd';
- form.appendChild(area);
- document.body.appendChild(form);
- this.form = form;
- this.area = area;
- }
- this.form.action = this.uri();
- function complete () {
- initIframe();
- fn();
- }
- function initIframe () {
- if (self.iframe) {
- try {
- self.form.removeChild(self.iframe);
- } catch (e) {
- self.onError('jsonp polling iframe removal error', e);
- }
- }
- try {
- // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
- var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
- iframe = document.createElement(html);
- } catch (e) {
- iframe = document.createElement('iframe');
- iframe.name = self.iframeId;
- iframe.src = 'javascript:0';
- }
- iframe.id = self.iframeId;
- self.form.appendChild(iframe);
- self.iframe = iframe;
- }
- initIframe();
- // escape \n to prevent it from being converted into \r\n by some UAs
- // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
- data = data.replace(rEscapedNewline, '\\\n');
- this.area.value = data.replace(rNewline, '\\n');
- try {
- this.form.submit();
- } catch (e) {}
- if (this.iframe.attachEvent) {
- this.iframe.onreadystatechange = function () {
- if (self.iframe.readyState === 'complete') {
- complete();
- }
- };
- } else {
- this.iframe.onload = complete;
- }
- };
|