line_push/node_modules/eventsource-polyfill/dist/eventsource.js
2022-07-21 03:28:35 +00:00

622 lines
17 KiB
JavaScript

/*
* EventSource polyfill version 0.9.6
* Supported by sc AmvTek srl
* :email: devel@amvtek.com
*/
;(function (global) {
if (global.EventSource && !global._eventSourceImportPrefix){
return;
}
var evsImportName = (global._eventSourceImportPrefix||'')+"EventSource";
var EventSource = function (url, options) {
if (!url || typeof url != 'string') {
throw new SyntaxError('Not enough arguments');
}
this.URL = url;
this.setOptions(options);
var evs = this;
setTimeout(function(){evs.poll()}, 0);
};
EventSource.prototype = {
CONNECTING: 0,
OPEN: 1,
CLOSED: 2,
defaultOptions: {
loggingEnabled: false,
loggingPrefix: "eventsource",
interval: 500, // milliseconds
bufferSizeLimit: 256*1024, // bytes
silentTimeout: 300000, // milliseconds
getArgs:{
'evs_buffer_size_limit': 256*1024
},
xhrHeaders:{
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
'X-Requested-With': 'XMLHttpRequest'
}
},
setOptions: function(options){
var defaults = this.defaultOptions;
var option;
// set all default options...
for (option in defaults){
if ( defaults.hasOwnProperty(option) ){
this[option] = defaults[option];
}
}
// override with what is in options
for (option in options){
if (option in defaults && options.hasOwnProperty(option)){
this[option] = options[option];
}
}
// if getArgs option is enabled
// ensure evs_buffer_size_limit corresponds to bufferSizeLimit
if (this.getArgs && this.bufferSizeLimit) {
this.getArgs['evs_buffer_size_limit'] = this.bufferSizeLimit;
}
// if console is not available, force loggingEnabled to false
if (typeof console === "undefined" || typeof console.log === "undefined") {
this.loggingEnabled = false;
}
},
log: function(message) {
if (this.loggingEnabled) {
console.log("[" + this.loggingPrefix +"]:" + message)
}
},
poll: function() {
try {
if (this.readyState == this.CLOSED) {
return;
}
this.cleanup();
this.readyState = this.CONNECTING;
this.cursor = 0;
this.cache = '';
this._xhr = new this.XHR(this);
this.resetNoActivityTimer();
}
catch (e) {
// in an attempt to silence the errors
this.log('There were errors inside the pool try-catch');
this.dispatchEvent('error', { type: 'error', data: e.message });
}
},
pollAgain: function (interval) {
// schedule poll to be called after interval milliseconds
var evs = this;
evs.readyState = evs.CONNECTING;
evs.dispatchEvent('error', {
type: 'error',
data: "Reconnecting "
});
this._pollTimer = setTimeout(function(){evs.poll()}, interval||0);
},
cleanup: function() {
this.log('evs cleaning up')
if (this._pollTimer){
clearInterval(this._pollTimer);
this._pollTimer = null;
}
if (this._noActivityTimer){
clearInterval(this._noActivityTimer);
this._noActivityTimer = null;
}
if (this._xhr){
this._xhr.abort();
this._xhr = null;
}
},
resetNoActivityTimer: function(){
if (this.silentTimeout){
if (this._noActivityTimer){
clearInterval(this._noActivityTimer);
}
var evs = this;
this._noActivityTimer = setTimeout(
function(){ evs.log('Timeout! silentTImeout:'+evs.silentTimeout); evs.pollAgain(); },
this.silentTimeout
);
}
},
close: function () {
this.readyState = this.CLOSED;
this.log('Closing connection. readyState: '+this.readyState);
this.cleanup();
},
ondata: function() {
var request = this._xhr;
if (request.isReady() && !request.hasError() ) {
// reset the timer, as we have activity
this.resetNoActivityTimer();
// move this EventSource to OPEN state...
if (this.readyState == this.CONNECTING) {
this.readyState = this.OPEN;
this.dispatchEvent('open', { type: 'open' });
}
var buffer = request.getBuffer();
if (buffer.length > this.bufferSizeLimit) {
this.log('buffer.length > this.bufferSizeLimit');
this.pollAgain();
}
if (this.cursor == 0 && buffer.length > 0){
// skip byte order mark \uFEFF character if it starts the stream
if (buffer.substring(0,1) == '\uFEFF'){
this.cursor = 1;
}
}
var lastMessageIndex = this.lastMessageIndex(buffer);
if (lastMessageIndex[0] >= this.cursor){
var newcursor = lastMessageIndex[1];
var toparse = buffer.substring(this.cursor, newcursor);
this.parseStream(toparse);
this.cursor = newcursor;
}
// if request is finished, reopen the connection
if (request.isDone()) {
this.log('request.isDone(). reopening the connection');
this.pollAgain(this.interval);
}
}
else if (this.readyState !== this.CLOSED) {
this.log('this.readyState !== this.CLOSED');
this.pollAgain(this.interval);
//MV: Unsure why an error was previously dispatched
}
},
parseStream: function(chunk) {
// normalize line separators (\r\n,\r,\n) to \n
// remove white spaces that may precede \n
chunk = this.cache + this.normalizeToLF(chunk);
var events = chunk.split('\n\n');
var i, j, eventType, datas, line, retry;
for (i=0; i < (events.length - 1); i++) {
eventType = 'message';
datas = [];
parts = events[i].split('\n');
for (j=0; j < parts.length; j++) {
line = this.trimWhiteSpace(parts[j]);
if (line.indexOf('event') == 0) {
eventType = line.replace(/event:?\s*/, '');
}
else if (line.indexOf('retry') == 0) {
retry = parseInt(line.replace(/retry:?\s*/, ''));
if(!isNaN(retry)) {
this.interval = retry;
}
}
else if (line.indexOf('data') == 0) {
datas.push(line.replace(/data:?\s*/, ''));
}
else if (line.indexOf('id:') == 0) {
this.lastEventId = line.replace(/id:?\s*/, '');
}
else if (line.indexOf('id') == 0) { // this resets the id
this.lastEventId = null;
}
}
if (datas.length) {
// dispatch a new event
var event = new MessageEvent(eventType, datas.join('\n'), window.location.origin, this.lastEventId);
this.dispatchEvent(eventType, event);
}
}
this.cache = events[events.length - 1];
},
dispatchEvent: function (type, event) {
var handlers = this['_' + type + 'Handlers'];
if (handlers) {
for (var i = 0; i < handlers.length; i++) {
handlers[i].call(this, event);
}
}
if (this['on' + type]) {
this['on' + type].call(this, event);
}
},
addEventListener: function (type, handler) {
if (!this['_' + type + 'Handlers']) {
this['_' + type + 'Handlers'] = [];
}
this['_' + type + 'Handlers'].push(handler);
},
removeEventListener: function (type, handler) {
var handlers = this['_' + type + 'Handlers'];
if (!handlers) {
return;
}
for (var i = handlers.length - 1; i >= 0; --i) {
if (handlers[i] === handler) {
handlers.splice(i, 1);
break;
}
}
},
_pollTimer: null,
_noactivityTimer: null,
_xhr: null,
lastEventId: null,
cache: '',
cursor: 0,
onerror: null,
onmessage: null,
onopen: null,
readyState: 0,
// ===================================================================
// helpers functions
// those are attached to prototype to ease reuse and testing...
urlWithParams: function (baseURL, params) {
var encodedArgs = [];
if (params){
var key, urlarg;
var urlize = encodeURIComponent;
for (key in params){
if (params.hasOwnProperty(key)) {
urlarg = urlize(key)+'='+urlize(params[key]);
encodedArgs.push(urlarg);
}
}
}
if (encodedArgs.length > 0){
if (baseURL.indexOf('?') == -1)
return baseURL + '?' + encodedArgs.join('&');
return baseURL + '&' + encodedArgs.join('&');
}
return baseURL;
},
lastMessageIndex: function(text) {
var ln2 =text.lastIndexOf('\n\n');
var lr2 = text.lastIndexOf('\r\r');
var lrln2 = text.lastIndexOf('\r\n\r\n');
if (lrln2 > Math.max(ln2, lr2)) {
return [lrln2, lrln2+4];
}
return [Math.max(ln2, lr2), Math.max(ln2, lr2) + 2]
},
trimWhiteSpace: function(str) {
// to remove whitespaces left and right of string
var reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
return str.replace(reTrim, '');
},
normalizeToLF: function(str) {
// replace \r and \r\n with \n
return str.replace(/\r\n|\r/g, '\n');
}
};
if (!isOldIE()){
EventSource.isPolyfill = "XHR";
// EventSource will send request using XMLHttpRequest
EventSource.prototype.XHR = function(evs) {
request = new XMLHttpRequest();
this._request = request;
evs._xhr = this;
// set handlers
request.onreadystatechange = function(){
if (request.readyState > 1 && evs.readyState != evs.CLOSED) {
if (request.status == 200 || (request.status>=300 && request.status<400)){
evs.ondata();
}
else {
request._failed = true;
evs.readyState = evs.CLOSED;
evs.dispatchEvent('error', {
type: 'error',
data: "The server responded with "+request.status
});
evs.close();
}
}
};
request.onprogress = function () {
};
request.open('GET', evs.urlWithParams(evs.URL, evs.getArgs), true);
var headers = evs.xhrHeaders; // maybe null
for (var header in headers) {
if (headers.hasOwnProperty(header)){
request.setRequestHeader(header, headers[header]);
}
}
if (evs.lastEventId) {
request.setRequestHeader('Last-Event-Id', evs.lastEventId);
}
request.send();
};
EventSource.prototype.XHR.prototype = {
useXDomainRequest: false,
_request: null,
_failed: false, // true if we have had errors...
isReady: function() {
return this._request.readyState >= 2;
},
isDone: function() {
return (this._request.readyState == 4);
},
hasError: function() {
return (this._failed || (this._request.status >= 400));
},
getBuffer: function() {
var rv = '';
try {
rv = this._request.responseText || '';
}
catch (e){}
return rv;
},
abort: function() {
if ( this._request ) {
this._request.abort();
}
}
};
}
else {
EventSource.isPolyfill = "IE_8-9";
// patch EventSource defaultOptions
var defaults = EventSource.prototype.defaultOptions;
defaults.xhrHeaders = null; // no headers will be sent
defaults.getArgs['evs_preamble'] = 2048 + 8;
// EventSource will send request using Internet Explorer XDomainRequest
EventSource.prototype.XHR = function(evs) {
request = new XDomainRequest();
this._request = request;
// set handlers
request.onprogress = function(){
request._ready = true;
evs.ondata();
};
request.onload = function(){
this._loaded = true;
evs.ondata();
};
request.onerror = function(){
this._failed = true;
evs.readyState = evs.CLOSED;
evs.dispatchEvent('error', {
type: 'error',
data: "XDomainRequest error"
});
};
request.ontimeout = function(){
this._failed = true;
evs.readyState = evs.CLOSED;
evs.dispatchEvent('error', {
type: 'error',
data: "XDomainRequest timed out"
});
};
// XDomainRequest does not allow setting custom headers
// If EventSource has enabled the use of GET arguments
// we add parameters to URL so that server can adapt the stream...
var reqGetArgs = {};
if (evs.getArgs) {
// copy evs.getArgs in reqGetArgs
var defaultArgs = evs.getArgs;
for (var key in defaultArgs) {
if (defaultArgs.hasOwnProperty(key)){
reqGetArgs[key] = defaultArgs[key];
}
}
if (evs.lastEventId){
reqGetArgs['evs_last_event_id'] = evs.lastEventId;
}
}
// send the request
request.open('GET', evs.urlWithParams(evs.URL,reqGetArgs));
request.send();
};
EventSource.prototype.XHR.prototype = {
useXDomainRequest: true,
_request: null,
_ready: false, // true when progress events are dispatched
_loaded: false, // true when request has been loaded
_failed: false, // true if when request is in error
isReady: function() {
return this._request._ready;
},
isDone: function() {
return this._request._loaded;
},
hasError: function() {
return this._request._failed;
},
getBuffer: function() {
var rv = '';
try {
rv = this._request.responseText || '';
}
catch (e){}
return rv;
},
abort: function() {
if ( this._request){
this._request.abort();
}
}
};
}
function MessageEvent(type, data, origin, lastEventId) {
this.bubbles = false;
this.cancelBubble = false;
this.cancelable = false;
this.data = data || null;
this.origin = origin || '';
this.lastEventId = lastEventId || '';
this.type = type || 'message';
}
function isOldIE () {
//return true if we are in IE8 or IE9
return (window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined)) ? true : false;
}
global[evsImportName] = EventSource;
})(this);