Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
4.8 KiB

4 years ago
const Polling = require("./polling");
const globalThis = require("../globalThis");
const rNewline = /\n/g;
const rEscapedNewline = /\\n/g;
/**
* Global JSONP callbacks.
*/
let callbacks;
/**
* Noop.
*/
function empty() {}
class JSONPPolling extends Polling {
/**
* JSONP Polling constructor.
*
* @param {Object} opts.
* @api public
*/
constructor(opts) {
super(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
const 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
);
}
}
/**
* JSONP only supports binary as base64 encoded strings
*/
get supportsBinary() {
return false;
}
/**
* Closes the socket.
*
* @api private
*/
doClose() {
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;
}
super.doClose();
}
/**
* Starts a poll cycle.
*
* @api private
*/
doPoll() {
const self = this;
const 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);
};
const insertAt = document.getElementsByTagName("script")[0];
if (insertAt) {
insertAt.parentNode.insertBefore(script, insertAt);
} else {
(document.head || document.body).appendChild(script);
}
this.script = script;
const isUAgecko =
"undefined" !== typeof navigator && /gecko/i.test(navigator.userAgent);
if (isUAgecko) {
setTimeout(function() {
const 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
*/
doWrite(data, fn) {
const self = this;
let iframe;
if (!this.form) {
const form = document.createElement("form");
const area = document.createElement("textarea");
const id = (this.iframeId = "eio_iframe_" + this.index);
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)
const 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;
}
}
}
module.exports = JSONPPolling;