5 var Polling = require('./polling');
6 var inherit = require('component-inherit');
7 var globalThis = require('../globalThis');
13 module.exports = JSONPPolling;
16 * Cached regular expressions.
20 var rEscapedNewline = /\\n/g;
23 * Global JSONP callbacks.
35 * JSONP Polling constructor.
37 * @param {Object} opts.
41 function JSONPPolling (opts) {
42 Polling.call(this, opts);
44 this.query = this.query || {};
46 // define global callbacks array if not present
47 // we do this here (lazily) to avoid unneeded global pollution
49 // we need to consider multiple engines in the same page
50 callbacks = globalThis.___eio = (globalThis.___eio || []);
53 // callback identifier
54 this.index = callbacks.length;
56 // add callback to jsonp global
58 callbacks.push(function (msg) {
62 // append to query string
63 this.query.j = this.index;
65 // prevent spurious errors from being emitted when the window is unloaded
66 if (typeof addEventListener === 'function') {
67 addEventListener('beforeunload', function () {
68 if (self.script) self.script.onerror = empty;
74 * Inherits from Polling.
77 inherit(JSONPPolling, Polling);
80 * JSONP only supports binary as base64 encoded strings
83 JSONPPolling.prototype.supportsBinary = false;
91 JSONPPolling.prototype.doClose = function () {
93 this.script.parentNode.removeChild(this.script);
98 this.form.parentNode.removeChild(this.form);
103 Polling.prototype.doClose.call(this);
107 * Starts a poll cycle.
112 JSONPPolling.prototype.doPoll = function () {
114 var script = document.createElement('script');
117 this.script.parentNode.removeChild(this.script);
122 script.src = this.uri();
123 script.onerror = function (e) {
124 self.onError('jsonp poll error', e);
127 var insertAt = document.getElementsByTagName('script')[0];
129 insertAt.parentNode.insertBefore(script, insertAt);
131 (document.head || document.body).appendChild(script);
133 this.script = script;
135 var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
138 setTimeout(function () {
139 var iframe = document.createElement('iframe');
140 document.body.appendChild(iframe);
141 document.body.removeChild(iframe);
147 * Writes with a hidden iframe.
149 * @param {String} data to send
150 * @param {Function} called upon flush.
154 JSONPPolling.prototype.doWrite = function (data, fn) {
158 var form = document.createElement('form');
159 var area = document.createElement('textarea');
160 var id = this.iframeId = 'eio_iframe_' + this.index;
163 form.className = 'socketio';
164 form.style.position = 'absolute';
165 form.style.top = '-1000px';
166 form.style.left = '-1000px';
168 form.method = 'POST';
169 form.setAttribute('accept-charset', 'utf-8');
171 form.appendChild(area);
172 document.body.appendChild(form);
178 this.form.action = this.uri();
180 function complete () {
185 function initIframe () {
188 self.form.removeChild(self.iframe);
190 self.onError('jsonp polling iframe removal error', e);
195 // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
196 var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
197 iframe = document.createElement(html);
199 iframe = document.createElement('iframe');
200 iframe.name = self.iframeId;
201 iframe.src = 'javascript:0';
204 iframe.id = self.iframeId;
206 self.form.appendChild(iframe);
207 self.iframe = iframe;
212 // escape \n to prevent it from being converted into \r\n by some UAs
213 // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
214 data = data.replace(rEscapedNewline, '\\\n');
215 this.area.value = data.replace(rNewline, '\\n');
221 if (this.iframe.attachEvent) {
222 this.iframe.onreadystatechange = function () {
223 if (self.iframe.readyState === 'complete') {
228 this.iframe.onload = complete;