UNPKG

engine.io-client

Version:
196 lines (161 loc) 4.62 kB
const Polling = require("./polling"); const globalThis = require("../globalThis"); const rNewline = /\n/g; const rEscapedNewline = /\\n/g; /** * Global JSONP callbacks. */ let callbacks; 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 callbacks.push(this.onData.bind(this)); // append to query string this.query.j = this.index; } /** * JSONP only supports binary as base64 encoded strings */ get supportsBinary() { return false; } /** * Closes the socket. * * @api private */ doClose() { if (this.script) { // prevent spurious errors from being emitted when the window is unloaded this.script.onerror = () => {}; 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 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 = e => { this.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) { 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(); } const initIframe = () => { if (this.iframe) { try { this.form.removeChild(this.iframe); } catch (e) { this.onError("jsonp polling iframe removal error", e); } } try { // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) const html = '<iframe src="javascript:0" name="' + this.iframeId + '">'; iframe = document.createElement(html); } catch (e) { iframe = document.createElement("iframe"); iframe.name = this.iframeId; iframe.src = "javascript:0"; } iframe.id = this.iframeId; this.form.appendChild(iframe); this.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 = () => { if (this.iframe.readyState === "complete") { complete(); } }; } else { this.iframe.onload = complete; } } } module.exports = JSONPPolling;