Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

update

  • Loading branch information...
commit a58c236d659de38c9f0b10742e55689497c55960 1 parent 2245d14
@Yaffle Yaffle authored
Showing with 424 additions and 508 deletions.
  1. +212 −254 eventsource.js
  2. +212 −254 nodechat/eventsource.js
View
466 eventsource.js
@@ -1,81 +1,33 @@
-/*jslint sloppy: true, white: true, plusplus: true, indent: 2 */
-/*global XMLHttpRequest, setTimeout, clearTimeout, XDomainRequest*/
+/*jslint indent: 2 */
+/*global setTimeout, clearTimeout */
(function (global) {
- // http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx?PageIndex=1#comments
- // XDomainRequest does not have a binary interface. To use with non-text, first base64 to string.
- // http://cometdaily.com/2008/page/3/
- function XDomainRequestWrapper() {
- var x = new global.XDomainRequest(),
- that = this;
-
- that.readyState = 0;
- that.responseText = '';
-
- function onChange(readyState, responseText) {
- that.readyState = readyState;
- that.responseText = responseText;
- that.onreadystatechange();
- }
-
- x.onload = function () {
- onChange(4, x.responseText);
- };
-
- x.onerror = function () {
- onChange(4, '');
- };
-
- x.onprogress = function () {
- onChange(3, x.responseText);
- };
-
- that.open = function (method, url) {
- return x.open(method, url);
- };
-
- that.abort = function () {
- return x.abort();
- };
-
- that.send = function (postData) {
- return x.send(postData);
- };
-
- that.setRequestHeader = function () {};
-
- that.getResponseHeader = function (name) {
- return (/^content\-type$/i).test(name) ? x.contentType : '';
- };
-
- return that;
- }
-
- function extendAsEventTarget(obj) {
+ function EventTarget() {
var listeners = [];
function lastIndexOf(type, callback) {
var i = listeners.length - 1;
while (i >= 0 && !(listeners[i].type === type && listeners[i].callback === callback)) {
- i--;
+ i -= 1;
}
return i;
}
- obj.dispatchEvent = function (eventObject) {
+ this.dispatchEvent = function (event) {
function a(e) {
return function () {
throw e;
};
}
- var type = eventObject.type,
- candidates = listeners.slice(0), i;
- for (i = 0; i < candidates.length; i++) {
+ var type = event.type,
+ candidates = listeners.slice(0),
+ i;
+ for (i = 0; i < candidates.length; i += 1) {
if (candidates[i].type === type) {
try {
- candidates[i].callback.call(obj, eventObject);
+ candidates[i].callback.call(this, event);
} catch (e) {
// This identifier is local to the catch clause. But it's not true for IE < 9 ? (so "a" used)
setTimeout(a(e), 0);
@@ -84,143 +36,200 @@
}
};
- obj.addEventListener = function (type, callback) {
+ this.addEventListener = function (type, callback) {
if (lastIndexOf(type, callback) === -1) {
listeners.push({type: type, callback: callback});
}
};
- obj.removeEventListener = function (type, callback) {
+ this.removeEventListener = function (type, callback) {
var i = lastIndexOf(type, callback);
if (i !== -1) {
listeners.splice(i, 1);
}
};
- return obj;
+ return this;
}
function empty() {}
- var Transport,
- supportCORS = false; // anonymous mode at least
-
- if (global.EventSource && global.EventSource.constructor && global.EventSource.constructor.length > 1) {
- Transport = null;
- supportCORS = true;
- } else {
- if (global.XMLHttpRequest && ('onprogress' in (new XMLHttpRequest())) && ('withCredentials' in (new XMLHttpRequest()))) {
- Transport = global.XMLHttpRequest;
- supportCORS = true;
- } else {
- if (global.XDomainRequest) {
- Transport = XDomainRequestWrapper;
- supportCORS = true;
- } else {
- if (global.EventSource) {
- Transport = null;
- } else {
- if (global.XMLHttpRequest) {
- Transport = global.XMLHttpRequest;
- supportCORS = ('withCredentials' in (new XMLHttpRequest()));
- } else {
- Transport = function () {
- return (new global.ActiveXObject('Microsoft.XMLHTTP'));
- };
- }
- }
- }
- }
- }
-
- if (Transport) {
- global.EventSource = function (url, options) {
- function F() {}
- F.prototype = global.EventSource.prototype;
-
- url = String(url);
-
- var that = new F(),
- retry = 1000,
- lastEventId = '',
- xhr = null,
- reconnectTimeout = null;
-
- that.url = url;
- that.withCredentials = !!(options && options.withCredentials && ('withCredentials' in (new Transport())));
-
- that.CONNECTING = 0;
- that.OPEN = 1;
- that.CLOSED = 2;
- that.readyState = that.CONNECTING;
-
- // Queue a task which, if the readyState is set to a value other than CLOSED,
- // sets the readyState to ... and fires event
- function queue(event, readyState) {
- setTimeout(function () {
- if (that.readyState === that.CLOSED) {
- return;// http://www.w3.org/Bugs/Public/show_bug.cgi?id=14331
- }
+ var Transport = null,
+ supportCORS = true, // anonymous mode at least
+ tmp = global.XMLHttpRequest && (new global.XMLHttpRequest()),
+ progress = tmp && ('onprogress' in tmp),
+ withCredentials = tmp && ('withCredentials' in tmp);
+ tmp = null;
+
+ function EventSource(url, options) {
+ function F() {}
+ F.prototype = EventSource.prototype;
+
+ url = String(url);
+
+ var that = new F(),
+ retry = 1000,
+ lastEventId = '',
+ xhr = null,
+ reconnectTimeout = null;
+
+ that.url = url;
+ that.withCredentials = !!(options && options.withCredentials && withCredentials);
+
+ that.CONNECTING = 0;
+ that.OPEN = 1;
+ that.CLOSED = 2;
+ that.readyState = that.CONNECTING;
+
+ // Queue a task which, if the readyState is set to a value other than CLOSED,
+ // sets the readyState to ... and fires event
+ function queue(event, readyState) {
+ setTimeout(function () {
+ if (that.readyState !== that.CLOSED) { // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14331
if (readyState !== null) {
that.readyState = readyState;
}
event.target = that;
that.dispatchEvent(event);
- try {
- if (/^(message|error|open)$/.test(event.type) && typeof that['on' + event.type] === 'function') {
- // as IE doesn't support getters/setters, we can't implement 'onmessage' via addEventListener/removeEventListener
- that['on' + event.type](event);
- }
- } catch (e) {
- setTimeout(function () {
- throw e;
- }, 0);
+ if (/^(message|error|open)$/.test(event.type) && typeof that['on' + event.type] === 'function') {
+ // as IE doesn't support getters/setters, we can't implement 'onmessage' via addEventListener/removeEventListener
+ that['on' + event.type](event);
}
- }, 0);
- }
-
- function close() {
- // http://dev.w3.org/html5/eventsource/ The close() method must close the connection, if any; must abort any instances of the fetch algorithm started for this EventSource object; and must set the readyState attribute to CLOSED.
- if (xhr !== null) {
- xhr.onreadystatechange = empty;
- xhr.abort();
- xhr = null;
- }
- if (reconnectTimeout !== null) {
- clearTimeout(reconnectTimeout);
- reconnectTimeout = null;
}
+ }, 0);
+ }
+
+ function close() {
+ // http://dev.w3.org/html5/eventsource/ The close() method must close the connection, if any; must abort any instances of the fetch algorithm started for this EventSource object; and must set the readyState attribute to CLOSED.
+ if (xhr !== null) {
if ('\v' === 'v' && global.detachEvent) {
global.detachEvent('onunload', close);
}
- that.readyState = that.CLOSED;
+ xhr.onload = xhr.onerror = xhr.onprogress = xhr.onreadystatechange = empty;
+ xhr.abort();
+ xhr = null;
+ }
+ if (reconnectTimeout !== null) {
+ clearTimeout(reconnectTimeout);
+ reconnectTimeout = null;
}
+ that.readyState = that.CLOSED;
+ }
- that.close = close;
+ that.close = close;
- extendAsEventTarget(that);
+ EventTarget.call(that);
- function openConnection() {
- reconnectTimeout = null;
- if ('\v' === 'v' && global.attachEvent) {
- global.attachEvent('onunload', close);
+ function openConnection() {
+ reconnectTimeout = null;
+ if ('\v' === 'v' && global.attachEvent) {
+ global.attachEvent('onunload', close);
+ }
+
+ var offset = 0,
+ charOffset = 0,
+ opened = false,
+ buffer = {
+ data: '',
+ lastEventId: lastEventId,
+ name: ''
+ };
+
+ xhr = new Transport();
+
+ // with GET method in FF xhr.onreadystatechange with readyState === 3 doesn't work + POST = no-cache
+ xhr.open('POST', url, true);
+
+ function onReadyStateChange(readyState) {
+ var responseText = '',
+ contentType = '',
+ i,
+ j,
+ part,
+ stream,
+ field,
+ value;
+
+ try {
+ contentType = readyState > 1 ? ((xhr.getResponseHeader ? xhr.getResponseHeader('Content-Type') : xhr.contentType) || '') : '';
+ responseText = readyState > 2 ? xhr.responseText || '' : '';
+ } catch (e) {}
+
+ if (!opened && (/^text\/event\-stream/i).test(contentType)) {
+ queue({type: 'open'}, that.OPEN);
+ opened = true;
}
- var offset = 0,
- charOffset = 0,
- opened = false,
- buffer = {
- data: '',
- lastEventId: lastEventId,
- name: ''
- };
+ if (opened && (/\r|\n/).test(responseText.slice(charOffset))) {
+ part = responseText.slice(offset);
+ stream = (offset ? part : part.replace(/^\uFEFF/, '')).replace(/\r\n?/g, '\n').split('\n');
+
+ offset += part.length - stream[stream.length - 1].length;
+ for (i = 0; i < stream.length - 1; i += 1) {
+ field = stream[i];
+ value = '';
+ j = field.indexOf(':');
+ if (j !== -1) {
+ value = field.slice(j + (field.charAt(j + 1) === ' ' ? 2 : 1));
+ field = field.slice(0, j);
+ }
- xhr = new Transport();
+ if (!stream[i]) {
+ // dispatch the event
+ if (buffer.data) {
+ lastEventId = buffer.lastEventId;
+ queue({
+ type: buffer.name || 'message',
+ lastEventId: lastEventId,
+ data: buffer.data.replace(/\n$/, '')
+ }, null);
+ }
+ // Set the data buffer and the event name buffer to the empty string.
+ buffer.data = '';
+ buffer.name = '';
+ }
+
+ if (field === 'event') {
+ buffer.name = value;
+ }
+
+ if (field === 'id') {
+ buffer.lastEventId = value; // see http://www.w3.org/Bugs/Public/show_bug.cgi?id=13761
+ }
+
+ if (field === 'retry') {
+ if (/^\d+$/.test(value)) {
+ retry = +value;
+ }
+ }
- // with GET method in FF xhr.onreadystatechange with readyState === 3 doesn't work + POST = no-cache
- xhr.open('POST', url, true);
+ if (field === 'data') {
+ buffer.data += value + '\n';
+ }
+ }
+ }
+ charOffset = responseText.length;
+ if (readyState === 4) {
+ if ('\v' === 'v' && global.detachEvent) {
+ global.detachEvent('onunload', close);
+ }
+ xhr.onload = xhr.onerror = xhr.onprogress = xhr.onreadystatechange = empty;
+ xhr = null;
+ if (opened) {
+ // reestablishes the connection
+ queue({type: 'error'}, that.CONNECTING);
+ // setTimeout will wait before previous setTimeout(0) have completed
+ reconnectTimeout = setTimeout(openConnection, retry);
+ } else {
+ // fail the connection
+ queue({type: 'error'}, that.CLOSED);
+ }
+ }
+ }
+
+ if (xhr.setRequestHeader) { // XDomainRequest doesn't have this method
// Chrome bug:
// Request header field Cache-Control is not allowed by Access-Control-Allow-Headers.
//xhr.setRequestHeader('Cache-Control', 'no-cache');
@@ -230,7 +239,8 @@
// If you force Chrome to have a whitelisted content-type, either explicitly with setRequestHeader(), or implicitly by sending a FormData, then no preflight is done.
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- if (!('onprogress' in xhr)) {
+ if (!progress) {
+ //! X-Requested-With header should be allowed for CORS requests
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');// long-polling
}
@@ -238,107 +248,55 @@
//if (lastEventId !== '') {
// xhr.setRequestHeader('Last-Event-ID', lastEventId);
//}
-
- xhr.withCredentials = that.withCredentials;
-
xhr.onreadystatechange = function () {
- var readyState = +xhr.readyState,
- responseText = '',
- contentType = '',
- i,
- j,
- part,
- stream,
- field,
- value;
-
- try {
- contentType = readyState > 1 ? (xhr.getResponseHeader ? xhr.getResponseHeader('Content-Type') || '' : xhr.contentType) : '';
- responseText = readyState > 2 ? xhr.responseText || '' : '';
- } catch (e) {}
-
- if (!opened && /^text\/event\-stream/i.test(contentType)) {
- queue({'type': 'open'}, that.OPEN);
- opened = true;
- }
-
- if (opened && /\r|\n/.test(responseText.slice(charOffset))) {
- part = responseText.slice(offset);
- stream = (offset ? part : part.replace(/^\uFEFF/, '')).replace(/\r\n?/g, '\n').split('\n');
-
- offset += part.length - stream[stream.length - 1].length;
- for (i = 0; i < stream.length - 1; i++) {
- field = stream[i];
- value = '';
- j = field.indexOf(':');
- if (j !== -1) {
- value = field.slice(j + (field.charAt(j + 1) === ' ' ? 2 : 1));
- field = field.slice(0, j);
- }
-
- if (!stream[i]) {
- // dispatch the event
- if (buffer.data) {
- lastEventId = buffer.lastEventId;
- queue({
- 'type': buffer.name || 'message',
- lastEventId: lastEventId,
- data: buffer.data.replace(/\n$/, '')
- }, null);
- }
- // Set the data buffer and the event name buffer to the empty string.
- buffer.data = '';
- buffer.name = '';
- }
+ onReadyStateChange(+xhr.readyState);
+ };
- if (field === 'event') {
- buffer.name = value;
- }
+ xhr.withCredentials = that.withCredentials;
+ } else {
+ xhr.onload = xhr.onerror = function () {
+ onReadyStateChange(4);
+ };
+ xhr.onprogress = function () {
+ onReadyStateChange(3);
+ };
+ }
- if (field === 'id') {
- buffer.lastEventId = value; // see http://www.w3.org/Bugs/Public/show_bug.cgi?id=13761
- }
+ xhr.send(lastEventId !== '' ? 'Last-Event-ID=' + encodeURIComponent(lastEventId) : '');
+ }
+ openConnection();
- if (field === 'retry') {
- if (/^\d+$/.test(value)) {
- retry = +value;
- }
- }
+ return that;
+ }
- if (field === 'data') {
- buffer.data += value + '\n';
- }
- }
- }
- charOffset = responseText.length;
+ EventSource.CONNECTING = 0;
+ EventSource.OPEN = 1;
+ EventSource.CLOSED = 2;
- if (readyState === 4) {
- xhr.onreadystatechange = empty;// old IE bug?
- xhr = null;
- if ('\v' === 'v' && global.detachEvent) {
- global.detachEvent('onunload', close);
- }
- if (opened) {
- // reestablishes the connection
- queue({'type': 'error'}, that.CONNECTING);
- // setTimeout will wait before previous setTimeout(0) have completed
- reconnectTimeout = setTimeout(openConnection, retry);
- } else {
- //fail the connection
- queue({'type': 'error'}, that.CLOSED);
- }
- }
- };
- xhr.send(lastEventId !== '' ? 'Last-Event-ID=' + encodeURIComponent(lastEventId) : '');
+ if (!(global.EventSource && global.EventSource.constructor && global.EventSource.constructor.length > 1)) {
+ if (progress && withCredentials) {
+ Transport = global.XMLHttpRequest;
+ } else {
+ if (global.XDomainRequest) {
+ // http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx?PageIndex=1#comments
+ // XDomainRequest does not have a binary interface. To use with non-text, first base64 to string.
+ // http://cometdaily.com/2008/page/3/
+ Transport = global.XDomainRequest;
+ } else {
+ if (global.EventSource) {
+ supportCORS = false;
+ } else {
+ supportCORS = withCredentials;
+ Transport = global.XMLHttpRequest || function () {
+ return (new global.ActiveXObject('Microsoft.XMLHTTP'));
+ };
+ }
}
- openConnection();
-
- return that;
- };
+ }
+ }
- global.EventSource.CONNECTING = 0;
- global.EventSource.OPEN = 1;
- global.EventSource.CLOSED = 2;
+ if (Transport) {
+ global.EventSource = EventSource;
}
global.EventSource.supportCORS = supportCORS;
View
466 nodechat/eventsource.js
@@ -1,81 +1,33 @@
-/*jslint sloppy: true, white: true, plusplus: true, indent: 2 */
-/*global XMLHttpRequest, setTimeout, clearTimeout, XDomainRequest*/
+/*jslint indent: 2 */
+/*global setTimeout, clearTimeout */
(function (global) {
- // http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx?PageIndex=1#comments
- // XDomainRequest does not have a binary interface. To use with non-text, first base64 to string.
- // http://cometdaily.com/2008/page/3/
- function XDomainRequestWrapper() {
- var x = new global.XDomainRequest(),
- that = this;
-
- that.readyState = 0;
- that.responseText = '';
-
- function onChange(readyState, responseText) {
- that.readyState = readyState;
- that.responseText = responseText;
- that.onreadystatechange();
- }
-
- x.onload = function () {
- onChange(4, x.responseText);
- };
-
- x.onerror = function () {
- onChange(4, '');
- };
-
- x.onprogress = function () {
- onChange(3, x.responseText);
- };
-
- that.open = function (method, url) {
- return x.open(method, url);
- };
-
- that.abort = function () {
- return x.abort();
- };
-
- that.send = function (postData) {
- return x.send(postData);
- };
-
- that.setRequestHeader = function () {};
-
- that.getResponseHeader = function (name) {
- return (/^content\-type$/i).test(name) ? x.contentType : '';
- };
-
- return that;
- }
-
- function extendAsEventTarget(obj) {
+ function EventTarget() {
var listeners = [];
function lastIndexOf(type, callback) {
var i = listeners.length - 1;
while (i >= 0 && !(listeners[i].type === type && listeners[i].callback === callback)) {
- i--;
+ i -= 1;
}
return i;
}
- obj.dispatchEvent = function (eventObject) {
+ this.dispatchEvent = function (event) {
function a(e) {
return function () {
throw e;
};
}
- var type = eventObject.type,
- candidates = listeners.slice(0), i;
- for (i = 0; i < candidates.length; i++) {
+ var type = event.type,
+ candidates = listeners.slice(0),
+ i;
+ for (i = 0; i < candidates.length; i += 1) {
if (candidates[i].type === type) {
try {
- candidates[i].callback.call(obj, eventObject);
+ candidates[i].callback.call(this, event);
} catch (e) {
// This identifier is local to the catch clause. But it's not true for IE < 9 ? (so "a" used)
setTimeout(a(e), 0);
@@ -84,143 +36,200 @@
}
};
- obj.addEventListener = function (type, callback) {
+ this.addEventListener = function (type, callback) {
if (lastIndexOf(type, callback) === -1) {
listeners.push({type: type, callback: callback});
}
};
- obj.removeEventListener = function (type, callback) {
+ this.removeEventListener = function (type, callback) {
var i = lastIndexOf(type, callback);
if (i !== -1) {
listeners.splice(i, 1);
}
};
- return obj;
+ return this;
}
function empty() {}
- var Transport,
- supportCORS = false; // anonymous mode at least
-
- if (global.EventSource && global.EventSource.constructor && global.EventSource.constructor.length > 1) {
- Transport = null;
- supportCORS = true;
- } else {
- if (global.XMLHttpRequest && ('onprogress' in (new XMLHttpRequest())) && ('withCredentials' in (new XMLHttpRequest()))) {
- Transport = global.XMLHttpRequest;
- supportCORS = true;
- } else {
- if (global.XDomainRequest) {
- Transport = XDomainRequestWrapper;
- supportCORS = true;
- } else {
- if (global.EventSource) {
- Transport = null;
- } else {
- if (global.XMLHttpRequest) {
- Transport = global.XMLHttpRequest;
- supportCORS = ('withCredentials' in (new XMLHttpRequest()));
- } else {
- Transport = function () {
- return (new global.ActiveXObject('Microsoft.XMLHTTP'));
- };
- }
- }
- }
- }
- }
-
- if (Transport) {
- global.EventSource = function (url, options) {
- function F() {}
- F.prototype = global.EventSource.prototype;
-
- url = String(url);
-
- var that = new F(),
- retry = 1000,
- lastEventId = '',
- xhr = null,
- reconnectTimeout = null;
-
- that.url = url;
- that.withCredentials = !!(options && options.withCredentials && ('withCredentials' in (new Transport())));
-
- that.CONNECTING = 0;
- that.OPEN = 1;
- that.CLOSED = 2;
- that.readyState = that.CONNECTING;
-
- // Queue a task which, if the readyState is set to a value other than CLOSED,
- // sets the readyState to ... and fires event
- function queue(event, readyState) {
- setTimeout(function () {
- if (that.readyState === that.CLOSED) {
- return;// http://www.w3.org/Bugs/Public/show_bug.cgi?id=14331
- }
+ var Transport = null,
+ supportCORS = true, // anonymous mode at least
+ tmp = global.XMLHttpRequest && (new global.XMLHttpRequest()),
+ progress = tmp && ('onprogress' in tmp),
+ withCredentials = tmp && ('withCredentials' in tmp);
+ tmp = null;
+
+ function EventSource(url, options) {
+ function F() {}
+ F.prototype = EventSource.prototype;
+
+ url = String(url);
+
+ var that = new F(),
+ retry = 1000,
+ lastEventId = '',
+ xhr = null,
+ reconnectTimeout = null;
+
+ that.url = url;
+ that.withCredentials = !!(options && options.withCredentials && withCredentials);
+
+ that.CONNECTING = 0;
+ that.OPEN = 1;
+ that.CLOSED = 2;
+ that.readyState = that.CONNECTING;
+
+ // Queue a task which, if the readyState is set to a value other than CLOSED,
+ // sets the readyState to ... and fires event
+ function queue(event, readyState) {
+ setTimeout(function () {
+ if (that.readyState !== that.CLOSED) { // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14331
if (readyState !== null) {
that.readyState = readyState;
}
event.target = that;
that.dispatchEvent(event);
- try {
- if (/^(message|error|open)$/.test(event.type) && typeof that['on' + event.type] === 'function') {
- // as IE doesn't support getters/setters, we can't implement 'onmessage' via addEventListener/removeEventListener
- that['on' + event.type](event);
- }
- } catch (e) {
- setTimeout(function () {
- throw e;
- }, 0);
+ if (/^(message|error|open)$/.test(event.type) && typeof that['on' + event.type] === 'function') {
+ // as IE doesn't support getters/setters, we can't implement 'onmessage' via addEventListener/removeEventListener
+ that['on' + event.type](event);
}
- }, 0);
- }
-
- function close() {
- // http://dev.w3.org/html5/eventsource/ The close() method must close the connection, if any; must abort any instances of the fetch algorithm started for this EventSource object; and must set the readyState attribute to CLOSED.
- if (xhr !== null) {
- xhr.onreadystatechange = empty;
- xhr.abort();
- xhr = null;
- }
- if (reconnectTimeout !== null) {
- clearTimeout(reconnectTimeout);
- reconnectTimeout = null;
}
+ }, 0);
+ }
+
+ function close() {
+ // http://dev.w3.org/html5/eventsource/ The close() method must close the connection, if any; must abort any instances of the fetch algorithm started for this EventSource object; and must set the readyState attribute to CLOSED.
+ if (xhr !== null) {
if ('\v' === 'v' && global.detachEvent) {
global.detachEvent('onunload', close);
}
- that.readyState = that.CLOSED;
+ xhr.onload = xhr.onerror = xhr.onprogress = xhr.onreadystatechange = empty;
+ xhr.abort();
+ xhr = null;
+ }
+ if (reconnectTimeout !== null) {
+ clearTimeout(reconnectTimeout);
+ reconnectTimeout = null;
}
+ that.readyState = that.CLOSED;
+ }
- that.close = close;
+ that.close = close;
- extendAsEventTarget(that);
+ EventTarget.call(that);
- function openConnection() {
- reconnectTimeout = null;
- if ('\v' === 'v' && global.attachEvent) {
- global.attachEvent('onunload', close);
+ function openConnection() {
+ reconnectTimeout = null;
+ if ('\v' === 'v' && global.attachEvent) {
+ global.attachEvent('onunload', close);
+ }
+
+ var offset = 0,
+ charOffset = 0,
+ opened = false,
+ buffer = {
+ data: '',
+ lastEventId: lastEventId,
+ name: ''
+ };
+
+ xhr = new Transport();
+
+ // with GET method in FF xhr.onreadystatechange with readyState === 3 doesn't work + POST = no-cache
+ xhr.open('POST', url, true);
+
+ function onReadyStateChange(readyState) {
+ var responseText = '',
+ contentType = '',
+ i,
+ j,
+ part,
+ stream,
+ field,
+ value;
+
+ try {
+ contentType = readyState > 1 ? ((xhr.getResponseHeader ? xhr.getResponseHeader('Content-Type') : xhr.contentType) || '') : '';
+ responseText = readyState > 2 ? xhr.responseText || '' : '';
+ } catch (e) {}
+
+ if (!opened && (/^text\/event\-stream/i).test(contentType)) {
+ queue({type: 'open'}, that.OPEN);
+ opened = true;
}
- var offset = 0,
- charOffset = 0,
- opened = false,
- buffer = {
- data: '',
- lastEventId: lastEventId,
- name: ''
- };
+ if (opened && (/\r|\n/).test(responseText.slice(charOffset))) {
+ part = responseText.slice(offset);
+ stream = (offset ? part : part.replace(/^\uFEFF/, '')).replace(/\r\n?/g, '\n').split('\n');
+
+ offset += part.length - stream[stream.length - 1].length;
+ for (i = 0; i < stream.length - 1; i += 1) {
+ field = stream[i];
+ value = '';
+ j = field.indexOf(':');
+ if (j !== -1) {
+ value = field.slice(j + (field.charAt(j + 1) === ' ' ? 2 : 1));
+ field = field.slice(0, j);
+ }
- xhr = new Transport();
+ if (!stream[i]) {
+ // dispatch the event
+ if (buffer.data) {
+ lastEventId = buffer.lastEventId;
+ queue({
+ type: buffer.name || 'message',
+ lastEventId: lastEventId,
+ data: buffer.data.replace(/\n$/, '')
+ }, null);
+ }
+ // Set the data buffer and the event name buffer to the empty string.
+ buffer.data = '';
+ buffer.name = '';
+ }
+
+ if (field === 'event') {
+ buffer.name = value;
+ }
+
+ if (field === 'id') {
+ buffer.lastEventId = value; // see http://www.w3.org/Bugs/Public/show_bug.cgi?id=13761
+ }
+
+ if (field === 'retry') {
+ if (/^\d+$/.test(value)) {
+ retry = +value;
+ }
+ }
- // with GET method in FF xhr.onreadystatechange with readyState === 3 doesn't work + POST = no-cache
- xhr.open('POST', url, true);
+ if (field === 'data') {
+ buffer.data += value + '\n';
+ }
+ }
+ }
+ charOffset = responseText.length;
+ if (readyState === 4) {
+ if ('\v' === 'v' && global.detachEvent) {
+ global.detachEvent('onunload', close);
+ }
+ xhr.onload = xhr.onerror = xhr.onprogress = xhr.onreadystatechange = empty;
+ xhr = null;
+ if (opened) {
+ // reestablishes the connection
+ queue({type: 'error'}, that.CONNECTING);
+ // setTimeout will wait before previous setTimeout(0) have completed
+ reconnectTimeout = setTimeout(openConnection, retry);
+ } else {
+ // fail the connection
+ queue({type: 'error'}, that.CLOSED);
+ }
+ }
+ }
+
+ if (xhr.setRequestHeader) { // XDomainRequest doesn't have this method
// Chrome bug:
// Request header field Cache-Control is not allowed by Access-Control-Allow-Headers.
//xhr.setRequestHeader('Cache-Control', 'no-cache');
@@ -230,7 +239,8 @@
// If you force Chrome to have a whitelisted content-type, either explicitly with setRequestHeader(), or implicitly by sending a FormData, then no preflight is done.
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- if (!('onprogress' in xhr)) {
+ if (!progress) {
+ //! X-Requested-With header should be allowed for CORS requests
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');// long-polling
}
@@ -238,107 +248,55 @@
//if (lastEventId !== '') {
// xhr.setRequestHeader('Last-Event-ID', lastEventId);
//}
-
- xhr.withCredentials = that.withCredentials;
-
xhr.onreadystatechange = function () {
- var readyState = +xhr.readyState,
- responseText = '',
- contentType = '',
- i,
- j,
- part,
- stream,
- field,
- value;
-
- try {
- contentType = readyState > 1 ? (xhr.getResponseHeader ? xhr.getResponseHeader('Content-Type') || '' : xhr.contentType) : '';
- responseText = readyState > 2 ? xhr.responseText || '' : '';
- } catch (e) {}
-
- if (!opened && /^text\/event\-stream/i.test(contentType)) {
- queue({'type': 'open'}, that.OPEN);
- opened = true;
- }
-
- if (opened && /\r|\n/.test(responseText.slice(charOffset))) {
- part = responseText.slice(offset);
- stream = (offset ? part : part.replace(/^\uFEFF/, '')).replace(/\r\n?/g, '\n').split('\n');
-
- offset += part.length - stream[stream.length - 1].length;
- for (i = 0; i < stream.length - 1; i++) {
- field = stream[i];
- value = '';
- j = field.indexOf(':');
- if (j !== -1) {
- value = field.slice(j + (field.charAt(j + 1) === ' ' ? 2 : 1));
- field = field.slice(0, j);
- }
-
- if (!stream[i]) {
- // dispatch the event
- if (buffer.data) {
- lastEventId = buffer.lastEventId;
- queue({
- 'type': buffer.name || 'message',
- lastEventId: lastEventId,
- data: buffer.data.replace(/\n$/, '')
- }, null);
- }
- // Set the data buffer and the event name buffer to the empty string.
- buffer.data = '';
- buffer.name = '';
- }
+ onReadyStateChange(+xhr.readyState);
+ };
- if (field === 'event') {
- buffer.name = value;
- }
+ xhr.withCredentials = that.withCredentials;
+ } else {
+ xhr.onload = xhr.onerror = function () {
+ onReadyStateChange(4);
+ };
+ xhr.onprogress = function () {
+ onReadyStateChange(3);
+ };
+ }
- if (field === 'id') {
- buffer.lastEventId = value; // see http://www.w3.org/Bugs/Public/show_bug.cgi?id=13761
- }
+ xhr.send(lastEventId !== '' ? 'Last-Event-ID=' + encodeURIComponent(lastEventId) : '');
+ }
+ openConnection();
- if (field === 'retry') {
- if (/^\d+$/.test(value)) {
- retry = +value;
- }
- }
+ return that;
+ }
- if (field === 'data') {
- buffer.data += value + '\n';
- }
- }
- }
- charOffset = responseText.length;
+ EventSource.CONNECTING = 0;
+ EventSource.OPEN = 1;
+ EventSource.CLOSED = 2;
- if (readyState === 4) {
- xhr.onreadystatechange = empty;// old IE bug?
- xhr = null;
- if ('\v' === 'v' && global.detachEvent) {
- global.detachEvent('onunload', close);
- }
- if (opened) {
- // reestablishes the connection
- queue({'type': 'error'}, that.CONNECTING);
- // setTimeout will wait before previous setTimeout(0) have completed
- reconnectTimeout = setTimeout(openConnection, retry);
- } else {
- //fail the connection
- queue({'type': 'error'}, that.CLOSED);
- }
- }
- };
- xhr.send(lastEventId !== '' ? 'Last-Event-ID=' + encodeURIComponent(lastEventId) : '');
+ if (!(global.EventSource && global.EventSource.constructor && global.EventSource.constructor.length > 1)) {
+ if (progress && withCredentials) {
+ Transport = global.XMLHttpRequest;
+ } else {
+ if (global.XDomainRequest) {
+ // http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx?PageIndex=1#comments
+ // XDomainRequest does not have a binary interface. To use with non-text, first base64 to string.
+ // http://cometdaily.com/2008/page/3/
+ Transport = global.XDomainRequest;
+ } else {
+ if (global.EventSource) {
+ supportCORS = false;
+ } else {
+ supportCORS = withCredentials;
+ Transport = global.XMLHttpRequest || function () {
+ return (new global.ActiveXObject('Microsoft.XMLHTTP'));
+ };
+ }
}
- openConnection();
-
- return that;
- };
+ }
+ }
- global.EventSource.CONNECTING = 0;
- global.EventSource.OPEN = 1;
- global.EventSource.CLOSED = 2;
+ if (Transport) {
+ global.EventSource = EventSource;
}
global.EventSource.supportCORS = supportCORS;
Please sign in to comment.
Something went wrong with that request. Please try again.