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
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;
|